├── .gitignore ├── LICENSE ├── Makefile ├── PORTING.md ├── README.md └── source ├── crt └── crt0-linux-amd64.S ├── headers-sysdeps └── linux │ └── host_mmap.h ├── headers ├── stdlib.h ├── string.h ├── sys │ └── mman.h └── unistd.h └── vlibc ├── libc ├── stdlib-exit.v ├── stdlib-memory.v ├── stdlib-sort.v ├── string.v ├── sys-mman.v └── unistd.v ├── main.v ├── sysdeps └── sysdeps_linux.v └── v.mod /.gitignore: -------------------------------------------------------------------------------- 1 | crt0.o 2 | libc.a 3 | libm.a 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 The V Programming Language 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Locations and targets. 2 | TARGET_OS = linux 3 | TARGET_ARCH = amd64 4 | PREFIX = /usr/local 5 | DESTDIR = 6 | SRCDIR := source 7 | HDIR := source/headers 8 | SYSHDIR := source/headers-sysdeps/$(TARGET_OS) 9 | 10 | # Compilers and its flags. 11 | VC = v 12 | CC = cc 13 | AS = cc 14 | AR = ar 15 | VFLAGS = 16 | CFLAGS = 17 | ASFLAGS = 18 | ARFLAGS = 19 | 20 | VHARDFLAGS := $(VFLAGS) -enable-globals -os $(TARGET_OS) -d $(TARGET_ARCH) 21 | CHARDFLAGS := $(CFLAGS) -I$(SRCDIR)/headers -ffreestanding -fno-stack-protector -fdata-sections -ffunction-sections 22 | ASHARDFLAGS := $(ASFLAGS) -ffreestanding 23 | ARHARDFLAGS := $(ARFLAGS) rcs 24 | 25 | CRT := crt0.o 26 | LIBC := libc.a 27 | LIBM := libm.a 28 | 29 | .PHONY: all install-headers install clean 30 | 31 | all: $(CRT) $(LIBC) $(LIBM) 32 | 33 | $(CRT): 34 | @echo "Building $(CRT)" 35 | @$(AS) $(ASHARDFLAGS) -c $(SRCDIR)/crt/crt0-$(TARGET_OS)-$(TARGET_ARCH).S -o $(CRT) 36 | 37 | $(LIBC): 38 | @echo "Building $(LIBC)" 39 | @$(VC) $(VHARDFLAGS) $(SRCDIR)/vlibc -o $(LIBC).c 40 | @sed -i 's/int main(int ___argc/static int ___delete____(int ___argc/g' $(LIBC).c 41 | @$(CC) $(CHARDFLAGS) -c $(LIBC).c -o $(LIBC).o 42 | @$(AR) $(ARHARDFLAGS) $(LIBC) $(LIBC).o 43 | @rm $(LIBC).c $(LIBC).o 44 | 45 | $(LIBM): 46 | @echo "Building $(LIBM)" 47 | @touch a.S 48 | @$(AS) $(ASHARDFLAGS) -c a.S -o $(LIBM).o 49 | @$(AR) $(ARHARDFLAGS) $(LIBM) $(LIBM).o 50 | @rm $(LIBM).o a.S 51 | 52 | install-headers: 53 | install -d "$(DESTDIR)$(PREFIX)/include/sys" 54 | cp -rL $(HDIR)/* "$(DESTDIR)$(PREFIX)/include" 55 | cp -rL $(SYSHDIR)/* "$(DESTDIR)$(PREFIX)/include/sys" 56 | 57 | install: install-headers all 58 | install -d "$(DESTDIR)$(PREFIX)/lib" 59 | install -m 644 $(CRT) "$(DESTDIR)$(PREFIX)/lib/" 60 | install -m 644 $(LIBC) "$(DESTDIR)$(PREFIX)/lib/" 61 | install -m 644 $(LIBM) "$(DESTDIR)$(PREFIX)/lib/" 62 | 63 | clean: 64 | rm -f $(CRT) $(LIBC) $(LIBM) 65 | -------------------------------------------------------------------------------- /PORTING.md: -------------------------------------------------------------------------------- 1 | # Porting vlibc 2 | 3 | ## How to port 4 | 5 | To get the changes on tree please make a pull request, I can mantain and update 6 | them from there if needed. 7 | 8 | The modifications to do to port vlibc are located in 3 folders, and are the 9 | following: 10 | 11 | - `source/crt`: crt0, declared in a file for each OS/arch combination. 12 | - `headers-sysdeps`: The headers representing the sysdeps along with OS-specific 13 | functions. 14 | - `vlibc/sysdeps`: The actual V code for the sysdeps, one file per OS, and 15 | language features for separate architectures. 16 | 17 | ## Headers necessary under headers-sysdeps 18 | 19 | - `host_mmap.h`: `PROT` and `MAP` flags of the implementation. 20 | 21 | ## Functions necessary under sysdeps 22 | 23 | - `sys_exit`: POSIX-compatible `_exit` equivalent. 24 | - `sys_mmap`: POSIX-compatible `mmap` equivalent. 25 | - `sys_munmap`: POSIX-compatible `munmap` equivalent. 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vlibc, the V libc. 2 | 3 | A POSIX-compatible libc meant for ease of porting and performance for hobbyist 4 | OSes, obtaining this by using the V programming language. 5 | 6 | For now, static linking is the only mode the libc supports, to be changed 7 | with future work. 8 | 9 | ## Porting 10 | 11 | To learn about porting vlibc to a new OS or architecture please refer to 12 | [this document](PORTING.md). 13 | 14 | ## Building 15 | 16 | A Makefile is provided, featuring a `PREFIX` and `DESTDIR` variables for 17 | choosing a path for installation. Along with those, a `TARGET_OS` and 18 | `TARGET_ARCH` variables are provided for choosing the target to build the libc 19 | for. 20 | 21 | Only a V toolchain along with a C toolchain are needed for compiling the 22 | project. 23 | 24 | An example process would be: 25 | 26 | ```bash 27 | make VFLAGS="-prod" CFLAGS='-O -pipe' # Optimization flags are not passed by default. 28 | make PREFIX="/usr/" install # Install, feel free to use PREFIX or DESTDIR. 29 | ``` 30 | -------------------------------------------------------------------------------- /source/crt/crt0-linux-amd64.S: -------------------------------------------------------------------------------- 1 | .section .text 2 | .globl _start 3 | _start: 4 | xor %ebp, %ebp // Mark the end of stack frames 5 | mov (%rsp), %edi // Get argc from the stack 6 | lea 8(%rsp), %rsi // Take the address of argv from the stack 7 | lea 16(%rsp,%rdi,8), %rdx // Take the address of envp from the stack 8 | xor %eax, %eax // Per ABI and compatibility with icc 9 | call __vlibc_init // Hand it off to the libc. 10 | -------------------------------------------------------------------------------- /source/headers-sysdeps/linux/host_mmap.h: -------------------------------------------------------------------------------- 1 | #ifndef __HOST_MMAP_H__ 2 | #define __HOST_MMAP_H__ 3 | 4 | #define PROT_NONE 0 5 | #define PROT_READ 1 6 | #define PROT_WRITE 2 7 | #define PROT_EXEC 4 8 | #define PROT_GROWSDOWN 0x01000000 9 | #define PROT_GROWSUP 0x02000000 10 | 11 | #define MAP_TYPE 0x0f 12 | #define MAP_FILE 0x00 13 | #define MAP_ANON 0x20 14 | #define MAP_NORESERVE 0x4000 15 | #define MAP_GROWSDOWN 0x0100 16 | #define MAP_DENYWRITE 0x0800 17 | #define MAP_EXECUTABLE 0x1000 18 | #define MAP_LOCKED 0x2000 19 | #define MAP_POPULATE 0x8000 20 | #define MAP_NONBLOCK 0x10000 21 | #define MAP_STACK 0x20000 22 | #define MAP_HUGETLB 0x40000 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /source/headers/stdlib.h: -------------------------------------------------------------------------------- 1 | #ifndef __STDLIB_H__ 2 | #define __STDLIB_H__ 3 | 4 | #include 5 | 6 | #define EXIT_FAILURE 1 7 | #define EXIT_SUCCESS 0 8 | 9 | #ifdef __cplusplus 10 | extern "C" { 11 | #endif 12 | 13 | void abort(void) __attribute__((noreturn)); 14 | void exit(int status) __attribute__((noreturn)); 15 | void _Exit(int status) __attribute__((noreturn)); 16 | int atexit(void (*function)(void)); 17 | 18 | void *malloc(size_t size); 19 | void *calloc(size_t num, size_t size); 20 | void *realloc(void *ptr, size_t new_size); 21 | void free(void *ptr); 22 | 23 | void qsort(void *base, size_t count, size_t width, int (*compare)(void *, void *)); 24 | 25 | #ifdef __cplusplus 26 | } 27 | #endif 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /source/headers/string.h: -------------------------------------------------------------------------------- 1 | #ifndef __STRING_H__ 2 | #define __STRING_H__ 3 | 4 | #include 5 | 6 | #ifdef __cplusplus 7 | extern "C" { 8 | #endif 9 | 10 | size_t strlen(const char *str); 11 | void *memset(void *destination, int value, size_t count); 12 | void *memcpy(void *destination, const void *source, size_t count); 13 | int memcmp(const void *source1, const void *source2, size_t count); 14 | void *memmove(void *destination, const void *source, size_t count); 15 | 16 | #ifdef __cplusplus 17 | } 18 | #endif 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /source/headers/sys/mman.h: -------------------------------------------------------------------------------- 1 | #ifndef __SYS_MMAN_H__ 2 | #define __SYS_MMAN_H__ 3 | 4 | #include 5 | 6 | #define MAP_ANONYMOUS MAP_ANON 7 | 8 | #ifdef __cplusplus 9 | extern "C" { 10 | #endif 11 | 12 | void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset); 13 | int munmap(void *addr, size_t length); 14 | 15 | #ifdef __cplusplus 16 | } 17 | #endif 18 | -------------------------------------------------------------------------------- /source/headers/unistd.h: -------------------------------------------------------------------------------- 1 | #ifndef __UNISTD_H__ 2 | #define __UNISTD_H__ 3 | 4 | #define _POSIX_MAPPED_FILES 1 5 | 6 | #ifdef __cplusplus 7 | extern "C" { 8 | #endif 9 | 10 | void _exit(int status) __attribute__((noreturn)); 11 | 12 | #ifdef __cplusplus 13 | } 14 | #endif 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /source/vlibc/libc/stdlib-exit.v: -------------------------------------------------------------------------------- 1 | module libc 2 | 3 | import sysdeps 4 | 5 | const exit_success = 0 6 | const exit_failure = 1 7 | 8 | // TODO: Lock the atexit registration of functions for MT-safety. 9 | 10 | __global( 11 | atexit_list []fn() 12 | ) 13 | 14 | [export: 'abort'] 15 | [noreturn] 16 | pub fn abort() { 17 | sysdeps.sys_exit(C.int(exit_failure)) 18 | } 19 | 20 | [export: 'exit'] 21 | [noreturn] 22 | pub fn c_exit(status C.int) { 23 | for callback in atexit_list { 24 | callback() 25 | } 26 | sysdeps.sys_exit(status) 27 | } 28 | 29 | [export: '_Exit'] 30 | [noreturn] 31 | pub fn immediate_exit(status C.int) { 32 | sysdeps.sys_exit(status) 33 | } 34 | 35 | [export: 'atexit'] 36 | pub fn atexit(callback fn()) C.int { 37 | atexit_list.prepend(callback) 38 | return C.int(0) 39 | } 40 | -------------------------------------------------------------------------------- /source/vlibc/libc/stdlib-memory.v: -------------------------------------------------------------------------------- 1 | module libc 2 | 3 | // TODO: Implement. 4 | 5 | [export: 'malloc'] 6 | pub fn malloc(size C.size_t) voidptr { 7 | return voidptr(0) 8 | } 9 | 10 | [export: 'calloc'] 11 | pub fn calloc(count C.size_t, size C.size_t) voidptr { 12 | return voidptr(0) 13 | } 14 | 15 | [export: 'realloc'] 16 | pub fn realloc(ptr voidptr, new_size C.size_t) voidptr { 17 | return voidptr(0) 18 | } 19 | 20 | [export: 'free'] 21 | pub fn free(ptr voidptr) { 22 | return 23 | } 24 | -------------------------------------------------------------------------------- /source/vlibc/libc/stdlib-sort.v: -------------------------------------------------------------------------------- 1 | module libc 2 | 3 | // TODO: Tons of unsafe given the insane ammount of C pointer arithmetic and 4 | // indexing. The code could do with a cleanup to remove them. 5 | 6 | fn swap(x voidptr, y voidptr, len u64) { 7 | mut first := &byte(x) 8 | mut second := &byte(y) 9 | 10 | for i := 0; i < len; i++ { 11 | unsafe { 12 | val := first[i] 13 | first[i] = second[i] 14 | second[i] = val 15 | } 16 | } 17 | } 18 | 19 | fn inner_sort(array &byte, size u64, cmp fn(a &C.void, b &C.void) C.int, begin u64, end u64) { 20 | if end < begin { 21 | mut pivot := unsafe { array + begin } 22 | mut length := begin + size 23 | mut remain := end 24 | 25 | for length < remain { 26 | unsafe { 27 | if int(cmp(&C.void(array + length), &C.void(pivot))) <= 0 { 28 | length += size 29 | } else if int(cmp(&C.void(array + remain), &C.void(pivot))) > 0 { 30 | remain -= size 31 | } else if length < remain { 32 | swap(array + length, array + remain, size) 33 | } 34 | } 35 | } 36 | 37 | unsafe { 38 | length -= size 39 | swap(array + begin, array + length, size) 40 | inner_sort(array, size, cmp, begin, length) 41 | inner_sort(array, size, cmp, remain, end) 42 | } 43 | } 44 | } 45 | 46 | [export: 'qsort'] 47 | pub fn qsort(base &C.void, count C.size_t, size C.size_t, f fn(const_a &C.void, const_b &C.void) C.int) { 48 | if u64(count) > 0 { 49 | inner_sort(unsafe { &byte(base) }, u64(size), f, 0, (u64(count) - 1) * u64(size)) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /source/vlibc/libc/string.v: -------------------------------------------------------------------------------- 1 | module libc 2 | 3 | [export: 'strlen'] 4 | pub fn strlen(const_str &char) u64 { 5 | mut i := 0 6 | for unsafe { const_str[i] } != char(`\0`) { 7 | i += 1 8 | } 9 | return u64(i) 10 | } 11 | 12 | [export: 'memset'] 13 | pub fn memset(destination voidptr, value int, count u64) voidptr { 14 | mut ptr := &byte(destination) 15 | for i := 0; i < count; i++ { 16 | unsafe { ptr[i] = byte(value) } 17 | } 18 | return destination 19 | } 20 | 21 | [export: 'memcpy'] 22 | pub fn memcpy(destination voidptr, const_source &C.void, count u64) voidptr { 23 | mut dest := &byte(destination) 24 | src := unsafe { &byte(const_source) } 25 | for i := 0; i < count; i++ { 26 | unsafe { dest[i] = src[i] } 27 | } 28 | return destination 29 | } 30 | 31 | [export: 'memcmp'] 32 | pub fn memcmp(const_source1 &C.void, const_source2 &C.void, count u64) int { 33 | src1 := unsafe { &byte(const_source1) } 34 | src2 := unsafe { &byte(const_source2) } 35 | 36 | for i := 0; i < count; i++ { 37 | s1 := unsafe { src1[i] } 38 | s2 := unsafe { src2[i] } 39 | if s1 != s2 { 40 | return if s1 < s2 { -1 } else { 1 } 41 | } 42 | } 43 | 44 | return 0 45 | } 46 | 47 | [export: 'memmove'] 48 | pub fn memmove(destination voidptr, const_source &C.void, count u64) voidptr { 49 | mut dest := &byte(destination) 50 | src := unsafe { &byte(const_source) } 51 | 52 | if const_source > destination { 53 | for i := 0; i < count; i++ { 54 | unsafe { dest[i] = src[i] } 55 | } 56 | } else if const_source < destination { 57 | for i := count; i > 0; i-- { 58 | unsafe { dest[i - 1] = src[i - 1] } 59 | } 60 | } 61 | 62 | return dest 63 | } 64 | -------------------------------------------------------------------------------- /source/vlibc/libc/sys-mman.v: -------------------------------------------------------------------------------- 1 | module libc 2 | 3 | import sysdeps 4 | 5 | [export: 'mmap'] 6 | pub fn mmap(addr voidptr, length C.size_t, prot C.int, flags C.int, fd C.int, offset C.off_t) voidptr { 7 | return sysdeps.sys_mmap(addr, u64(length), int(prot), int(flags), int(fd), int(offset)) 8 | } 9 | 10 | [export: 'munmap'] 11 | pub fn munmap(addr voidptr, length C.size_t) int { 12 | return sysdeps.sys_munmap(addr, u64(length)) 13 | } 14 | -------------------------------------------------------------------------------- /source/vlibc/libc/unistd.v: -------------------------------------------------------------------------------- 1 | module libc 2 | 3 | [export: '_exit'] 4 | [noreturn] 5 | pub fn immediate_exit2(status C.int) { 6 | immediate_exit(status) 7 | } 8 | -------------------------------------------------------------------------------- /source/vlibc/main.v: -------------------------------------------------------------------------------- 1 | module main 2 | 3 | import libc 4 | 5 | // Here because v wants one. 6 | fn main() { 7 | return 8 | } 9 | 10 | fn C.main(argc C.int, argv &charptr, envp &charptr) C.int 11 | fn C._vinit(argc C.int, argv &charptr) 12 | 13 | [export: '__vlibc_init'] 14 | pub fn vlibc_init(argc C.int, argv &charptr, envp &charptr) { 15 | // Initialize V itself. 16 | // C._vinit(argc, argv) 17 | 18 | ret := C.main(argc, argv, envp) 19 | libc.c_exit(ret) 20 | } 21 | -------------------------------------------------------------------------------- /source/vlibc/sysdeps/sysdeps_linux.v: -------------------------------------------------------------------------------- 1 | module sysdeps 2 | 3 | const mmap_prot_none = 0 4 | const mmap_prot_read = 1 5 | const mmap_prot_write = 2 6 | const mmap_prot_exec = 4 7 | const mmap_prot_growsdown = 0x01000000 8 | const mmap_prot_growsup = 0x02000000 9 | 10 | const mmap_map_type = 0x0f 11 | const mmap_map_file = 0x00 12 | const mmap_map_anon = 0x20 13 | const mmap_map_noreserve = 0x4000 14 | const mmap_map_growsdown = 0x0100 15 | const mmap_map_denywrite = 0x0800 16 | const mmap_map_executable = 0x1000 17 | const mmap_map_locked = 0x2000 18 | const mmap_map_populate = 0x8000 19 | const mmap_map_nonblock = 0x10000 20 | const mmap_map_stack = 0x20000 21 | const mmap_map_hugetlb = 0x40000 22 | 23 | [noreturn] 24 | pub fn sys_exit(code C.int) { 25 | asm volatile amd64 { 26 | syscall 27 | ;; a(60) D(code) ; rcx r11 memory 28 | } 29 | for {} 30 | } 31 | 32 | pub fn sys_mmap(addr voidptr, length u64, prot int, flag int, fd int, offset int) voidptr { 33 | mut ret := voidptr(0) 34 | asm volatile amd64 { 35 | mov r10, flag 36 | mov r8, fd 37 | mov r9, offset 38 | syscall 39 | ; =a(ret) ; a(9) D(addr) S(length) d(prot) m(flag) m(fd) m(offset); rcx r11 memory 40 | } 41 | return ret 42 | } 43 | 44 | pub fn sys_munmap(addr voidptr, length u64) int { 45 | mut ret := 0 46 | asm volatile amd64 { 47 | syscall 48 | ; =a(ret) ; a(9) D(addr) S(length); rcx r11 memory 49 | } 50 | return ret 51 | } 52 | -------------------------------------------------------------------------------- /source/vlibc/v.mod: -------------------------------------------------------------------------------- 1 | Module { 2 | name: 'vlibc' 3 | description: 'A libc' 4 | version: '0.0.0' 5 | license: 'MIT' 6 | dependencies: [] 7 | } 8 | --------------------------------------------------------------------------------