From: Tulio A M Mendes Date: Tue, 10 Feb 2026 07:19:46 +0000 (-0300) Subject: feat: add ulibc — minimal userspace C library X-Git-Url: https://projects.tadryanom.me/docs/static/gitweb.css?a=commitdiff_plain;h=925909c43a145077a441b87403c0b2a077d0c99d;p=AdrOS.git feat: add ulibc — minimal userspace C library Create user/ulibc/ with headers and implementations for: - crt0.S: _start entry point that calls main() then exit() - syscall.h: raw INT 0x80 wrappers (_syscall0..5) - unistd.c/h: POSIX wrappers (read, write, open, close, fork, execve, pipe, dup2, brk, getpid, chdir, mkdir, etc) - string.c/h: memcpy, memset, memmove, strlen, strcmp, strcpy, strchr, strcat, etc - stdlib.c/h: malloc/free (bump allocator via brk), calloc, realloc, atoi, exit - stdio.c/h: printf, puts, putchar, snprintf, vsnprintf with format specifiers: %d %i %u %x %X %s %c %p %% - errno.c/h: errno variable + error codes + __syscall_ret helper Build system: user/ulibc/Makefile produces libulibc.a static lib. Main Makefile updated with ULIBC_LIB target. Future user programs can link against libulibc.a instead of duplicating syscall wrappers inline. Passes: make (kernel), ulibc builds clean, QEMU smoke test OK. --- diff --git a/.gitignore b/.gitignore index 6143acd..9a38798 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ build/ *.img *.log *.elf +*.a # ISO staging: keep config, ignore generated kernel binaries iso/boot/*.bin diff --git a/Makefile b/Makefile index 582d1aa..f7fc27b 100644 --- a/Makefile +++ b/Makefile @@ -118,6 +118,12 @@ iso: $(KERNEL_NAME) $(INITRD_IMG) $(MKINITRD): tools/mkinitrd.c @gcc tools/mkinitrd.c -o $(MKINITRD) +ULIBC_DIR := user/ulibc +ULIBC_LIB := $(ULIBC_DIR)/libulibc.a + +$(ULIBC_LIB): + @$(MAKE) -C $(ULIBC_DIR) --no-print-directory + $(USER_ELF): user/init.c user/linker.ld @i686-elf-gcc -m32 -I include -ffreestanding -fno-pie -no-pie -nostdlib -Wl,-T,user/linker.ld -o $(USER_ELF) user/init.c user/errno.c diff --git a/user/ulibc/Makefile b/user/ulibc/Makefile new file mode 100644 index 0000000..8924596 --- /dev/null +++ b/user/ulibc/Makefile @@ -0,0 +1,30 @@ +# ulibc — Minimal C library for AdrOS userspace +CC := i686-elf-gcc +AS := i686-elf-as +AR := i686-elf-ar + +CFLAGS := -m32 -ffreestanding -fno-pie -no-pie -nostdlib -O2 -Wall -Wextra -Iinclude +ASFLAGS := --32 + +SRC_C := $(wildcard src/*.c) +SRC_S := $(wildcard src/*.S) +OBJ := $(SRC_C:.c=.o) $(SRC_S:.S=.o) + +all: libulibc.a + +libulibc.a: $(OBJ) + @$(AR) rcs $@ $^ + @echo " AR $@" + +src/%.o: src/%.c + @$(CC) $(CFLAGS) -c $< -o $@ + @echo " CC $<" + +src/%.o: src/%.S + @$(AS) $(ASFLAGS) $< -o $@ + @echo " AS $<" + +clean: + rm -f src/*.o libulibc.a + +.PHONY: all clean diff --git a/user/ulibc/include/errno.h b/user/ulibc/include/errno.h new file mode 100644 index 0000000..a567956 --- /dev/null +++ b/user/ulibc/include/errno.h @@ -0,0 +1,37 @@ +#ifndef ULIBC_ERRNO_H +#define ULIBC_ERRNO_H + +extern int errno; + +#define EPERM 1 +#define ENOENT 2 +#define ESRCH 3 +#define EINTR 4 +#define EIO 5 +#define ENXIO 6 +#define EBADF 9 +#define ECHILD 10 +#define EAGAIN 11 +#define ENOMEM 12 +#define EACCES 13 +#define EFAULT 14 +#define EEXIST 17 +#define ENOTDIR 20 +#define EISDIR 21 +#define EINVAL 22 +#define EMFILE 24 +#define ENOSPC 28 +#define EPIPE 32 +#define ENOSYS 38 +#define ENOTEMPTY 39 + +/* Convert raw syscall return to errno-style */ +static inline int __syscall_ret(int r) { + if (r < 0) { + errno = -r; + return -1; + } + return r; +} + +#endif diff --git a/user/ulibc/include/stdio.h b/user/ulibc/include/stdio.h new file mode 100644 index 0000000..b1cddf4 --- /dev/null +++ b/user/ulibc/include/stdio.h @@ -0,0 +1,14 @@ +#ifndef ULIBC_STDIO_H +#define ULIBC_STDIO_H + +#include +#include + +int putchar(int c); +int puts(const char* s); +int printf(const char* fmt, ...) __attribute__((format(printf, 1, 2))); +int vprintf(const char* fmt, va_list ap); +int snprintf(char* buf, size_t size, const char* fmt, ...) __attribute__((format(printf, 3, 4))); +int vsnprintf(char* buf, size_t size, const char* fmt, va_list ap); + +#endif diff --git a/user/ulibc/include/stdlib.h b/user/ulibc/include/stdlib.h new file mode 100644 index 0000000..6794a99 --- /dev/null +++ b/user/ulibc/include/stdlib.h @@ -0,0 +1,18 @@ +#ifndef ULIBC_STDLIB_H +#define ULIBC_STDLIB_H + +#include + +void* malloc(size_t size); +void free(void* ptr); +void* calloc(size_t nmemb, size_t size); +void* realloc(void* ptr, size_t size); + +int atoi(const char* s); +void exit(int status) __attribute__((noreturn)); + +#ifndef NULL +#define NULL ((void*)0) +#endif + +#endif diff --git a/user/ulibc/include/string.h b/user/ulibc/include/string.h new file mode 100644 index 0000000..10fe09d --- /dev/null +++ b/user/ulibc/include/string.h @@ -0,0 +1,20 @@ +#ifndef ULIBC_STRING_H +#define ULIBC_STRING_H + +#include +#include + +void* memcpy(void* dst, const void* src, size_t n); +void* memset(void* s, int c, size_t n); +void* memmove(void* dst, const void* src, size_t n); +int memcmp(const void* a, const void* b, size_t n); +size_t strlen(const char* s); +char* strcpy(char* dst, const char* src); +char* strncpy(char* dst, const char* src, size_t n); +int strcmp(const char* a, const char* b); +int strncmp(const char* a, const char* b, size_t n); +char* strchr(const char* s, int c); +char* strrchr(const char* s, int c); +char* strcat(char* dst, const char* src); + +#endif diff --git a/user/ulibc/include/syscall.h b/user/ulibc/include/syscall.h new file mode 100644 index 0000000..a108fa9 --- /dev/null +++ b/user/ulibc/include/syscall.h @@ -0,0 +1,90 @@ +#ifndef ULIBC_SYSCALL_H +#define ULIBC_SYSCALL_H + +#include + +enum { + SYS_WRITE = 1, + SYS_EXIT = 2, + SYS_GETPID = 3, + SYS_OPEN = 4, + SYS_READ = 5, + SYS_CLOSE = 6, + SYS_WAITPID = 7, + SYS_LSEEK = 9, + SYS_FSTAT = 10, + SYS_STAT = 11, + SYS_DUP = 12, + SYS_DUP2 = 13, + SYS_PIPE = 14, + SYS_EXECVE = 15, + SYS_FORK = 16, + SYS_GETPPID = 17, + SYS_POLL = 18, + SYS_KILL = 19, + SYS_SELECT = 20, + SYS_IOCTL = 21, + SYS_SETSID = 22, + SYS_SETPGID = 23, + SYS_GETPGRP = 24, + SYS_SIGACTION = 25, + SYS_SIGPROCMASK = 26, + SYS_SIGRETURN = 27, + SYS_MKDIR = 28, + SYS_UNLINK = 29, + SYS_GETDENTS = 30, + SYS_FCNTL = 31, + SYS_CHDIR = 32, + SYS_GETCWD = 33, + SYS_PIPE2 = 34, + SYS_DUP3 = 35, + SYS_OPENAT = 36, + SYS_FSTATAT = 37, + SYS_UNLINKAT = 38, + SYS_RENAME = 39, + SYS_RMDIR = 40, + SYS_BRK = 41, + SYS_NANOSLEEP = 42, + SYS_CLOCK_GETTIME = 43, + SYS_MMAP = 44, + SYS_MUNMAP = 45, +}; + +/* Raw syscall wrappers — up to 5 args via INT 0x80 */ +static inline int _syscall0(int nr) { + int ret; + __asm__ volatile("int $0x80" : "=a"(ret) : "a"(nr) : "memory"); + return ret; +} + +static inline int _syscall1(int nr, int a1) { + int ret; + __asm__ volatile("int $0x80" : "=a"(ret) : "a"(nr), "b"(a1) : "memory"); + return ret; +} + +static inline int _syscall2(int nr, int a1, int a2) { + int ret; + __asm__ volatile("int $0x80" : "=a"(ret) : "a"(nr), "b"(a1), "c"(a2) : "memory"); + return ret; +} + +static inline int _syscall3(int nr, int a1, int a2, int a3) { + int ret; + __asm__ volatile("int $0x80" : "=a"(ret) : "a"(nr), "b"(a1), "c"(a2), "d"(a3) : "memory"); + return ret; +} + +static inline int _syscall4(int nr, int a1, int a2, int a3, int a4) { + int ret; + __asm__ volatile("int $0x80" : "=a"(ret) : "a"(nr), "b"(a1), "c"(a2), "d"(a3), "S"(a4) : "memory"); + return ret; +} + +static inline int _syscall5(int nr, int a1, int a2, int a3, int a4, int a5) { + int ret; + __asm__ volatile("int $0x80" : "=a"(ret) : "a"(nr), "b"(a1), "c"(a2), "d"(a3), "S"(a4), "D"(a5) : "memory"); + return ret; +} + +#endif diff --git a/user/ulibc/include/unistd.h b/user/ulibc/include/unistd.h new file mode 100644 index 0000000..35976f1 --- /dev/null +++ b/user/ulibc/include/unistd.h @@ -0,0 +1,39 @@ +#ifndef ULIBC_UNISTD_H +#define ULIBC_UNISTD_H + +#include +#include + +#define SEEK_SET 0 +#define SEEK_CUR 1 +#define SEEK_END 2 + +#define STDIN_FILENO 0 +#define STDOUT_FILENO 1 +#define STDERR_FILENO 2 + +int read(int fd, void* buf, size_t count); +int write(int fd, const void* buf, size_t count); +int open(const char* path, int flags); +int close(int fd); +int lseek(int fd, int offset, int whence); +int dup(int oldfd); +int dup2(int oldfd, int newfd); +int pipe(int fds[2]); +int fork(void); +int execve(const char* path, const char* const* argv, const char* const* envp); +int getpid(void); +int getppid(void); +int chdir(const char* path); +int getcwd(char* buf, size_t size); +int mkdir(const char* path); +int unlink(const char* path); +int rmdir(const char* path); +int setsid(void); +int setpgid(int pid, int pgid); +int getpgrp(void); +void* brk(void* addr); + +void _exit(int status) __attribute__((noreturn)); + +#endif diff --git a/user/ulibc/src/crt0.S b/user/ulibc/src/crt0.S new file mode 100644 index 0000000..746cc10 --- /dev/null +++ b/user/ulibc/src/crt0.S @@ -0,0 +1,32 @@ +/* + * ulibc crt0 — C runtime startup for AdrOS userspace + * Entry point: _start → main() → exit() + */ +.section .text +.global _start +.extern main +.extern exit + +_start: + /* Set up user data segments */ + mov $0x23, %ax + mov %ax, %ds + mov %ax, %es + mov %ax, %fs + mov %ax, %gs + + /* Call main() — no argc/argv yet */ + push $0 /* envp = NULL */ + push $0 /* argv = NULL */ + push $0 /* argc = 0 */ + call main + add $12, %esp + + /* exit(main return value) */ + push %eax + call exit + + /* Should never reach here */ +1: jmp 1b + +.section .note.GNU-stack,"",@progbits diff --git a/user/ulibc/src/errno.c b/user/ulibc/src/errno.c new file mode 100644 index 0000000..8330a8f --- /dev/null +++ b/user/ulibc/src/errno.c @@ -0,0 +1 @@ +int errno = 0; diff --git a/user/ulibc/src/stdio.c b/user/ulibc/src/stdio.c new file mode 100644 index 0000000..87089a1 --- /dev/null +++ b/user/ulibc/src/stdio.c @@ -0,0 +1,185 @@ +#include "stdio.h" +#include "unistd.h" +#include "string.h" +#include + +int putchar(int c) { + char ch = (char)c; + write(STDOUT_FILENO, &ch, 1); + return c; +} + +int puts(const char* s) { + int len = (int)strlen(s); + write(STDOUT_FILENO, s, (size_t)len); + write(STDOUT_FILENO, "\n", 1); + return len + 1; +} + +/* Minimal vsnprintf supporting: %d %i %u %x %X %s %c %p %% */ +int vsnprintf(char* buf, size_t size, const char* fmt, va_list ap) { + size_t pos = 0; + +#define PUTC(c) do { if (pos < size - 1) buf[pos] = (c); pos++; } while(0) + + if (size == 0) { + /* Just count characters */ + while (*fmt) { pos++; fmt++; } + return (int)pos; + } + + while (*fmt) { + if (*fmt != '%') { + PUTC(*fmt); + fmt++; + continue; + } + fmt++; /* skip '%' */ + + /* Flags */ + int pad_zero = 0; + int left_align = 0; + while (*fmt == '0' || *fmt == '-') { + if (*fmt == '0') pad_zero = 1; + if (*fmt == '-') left_align = 1; + fmt++; + } + if (left_align) pad_zero = 0; + + /* Width */ + int width = 0; + while (*fmt >= '0' && *fmt <= '9') { + width = width * 10 + (*fmt - '0'); + fmt++; + } + + /* Specifier */ + char tmp[32]; + int tmplen = 0; + const char* str = 0; + + switch (*fmt) { + case 'd': + case 'i': { + int v = va_arg(ap, int); + int neg = 0; + unsigned int uv; + if (v < 0) { neg = 1; uv = (unsigned int)(-(v + 1)) + 1; } + else { uv = (unsigned int)v; } + if (uv == 0) { tmp[tmplen++] = '0'; } + else { while (uv) { tmp[tmplen++] = (char)('0' + uv % 10); uv /= 10; } } + if (neg) tmp[tmplen++] = '-'; + /* reverse */ + for (int i = 0; i < tmplen / 2; i++) { + char t = tmp[i]; tmp[i] = tmp[tmplen-1-i]; tmp[tmplen-1-i] = t; + } + str = tmp; + break; + } + case 'u': { + unsigned int v = va_arg(ap, unsigned int); + if (v == 0) { tmp[tmplen++] = '0'; } + else { while (v) { tmp[tmplen++] = (char)('0' + v % 10); v /= 10; } } + for (int i = 0; i < tmplen / 2; i++) { + char t = tmp[i]; tmp[i] = tmp[tmplen-1-i]; tmp[tmplen-1-i] = t; + } + str = tmp; + break; + } + case 'x': + case 'X': + case 'p': { + const char* hex = (*fmt == 'X') ? "0123456789ABCDEF" : "0123456789abcdef"; + unsigned int v; + if (*fmt == 'p') { + v = (unsigned int)(uintptr_t)va_arg(ap, void*); + tmp[tmplen++] = '0'; + tmp[tmplen++] = 'x'; + } else { + v = va_arg(ap, unsigned int); + } + int start = tmplen; + if (v == 0) { tmp[tmplen++] = '0'; } + else { while (v) { tmp[tmplen++] = hex[v & 0xF]; v >>= 4; } } + /* reverse the hex digits only */ + for (int i = start; i < start + (tmplen - start) / 2; i++) { + int j = start + (tmplen - start) - 1 - (i - start); + char t = tmp[i]; tmp[i] = tmp[j]; tmp[j] = t; + } + str = tmp; + break; + } + case 's': { + str = va_arg(ap, const char*); + if (!str) str = "(null)"; + tmplen = (int)strlen(str); + break; + } + case 'c': { + tmp[0] = (char)va_arg(ap, int); + tmplen = 1; + str = tmp; + break; + } + case '%': + PUTC('%'); + fmt++; + continue; + case '\0': + goto done; + default: + PUTC('%'); + PUTC(*fmt); + fmt++; + continue; + } + fmt++; + + if (str && tmplen == 0) tmplen = (int)strlen(str); + + /* Padding */ + int pad = width - tmplen; + if (!left_align && pad > 0) { + char pc = pad_zero ? '0' : ' '; + for (int i = 0; i < pad; i++) PUTC(pc); + } + for (int i = 0; i < tmplen; i++) PUTC(str[i]); + if (left_align && pad > 0) { + for (int i = 0; i < pad; i++) PUTC(' '); + } + } + +done: + if (pos < size) buf[pos] = '\0'; + else if (size > 0) buf[size - 1] = '\0'; + return (int)pos; + +#undef PUTC +} + +int snprintf(char* buf, size_t size, const char* fmt, ...) { + va_list ap; + va_start(ap, fmt); + int r = vsnprintf(buf, size, fmt, ap); + va_end(ap); + return r; +} + +int vprintf(const char* fmt, va_list ap) { + char buf[1024]; + int n = vsnprintf(buf, sizeof(buf), fmt, ap); + if (n > 0) { + int w = n; + if (w > (int)(sizeof(buf) - 1)) w = (int)(sizeof(buf) - 1); + write(STDOUT_FILENO, buf, (size_t)w); + } + return n; +} + +int printf(const char* fmt, ...) { + va_list ap; + va_start(ap, fmt); + int r = vprintf(fmt, ap); + va_end(ap); + return r; +} diff --git a/user/ulibc/src/stdlib.c b/user/ulibc/src/stdlib.c new file mode 100644 index 0000000..7afd02d --- /dev/null +++ b/user/ulibc/src/stdlib.c @@ -0,0 +1,75 @@ +#include "stdlib.h" +#include "unistd.h" +#include "string.h" + +/* + * Minimal bump allocator using brk() syscall. + * No free() support yet — memory is only reclaimed on process exit. + * A proper free-list allocator can be added later. + */ +static void* heap_base = 0; +static void* heap_end = 0; + +void* malloc(size_t size) { + if (size == 0) return (void*)0; + + /* Align to 8 bytes */ + size = (size + 7) & ~(size_t)7; + + if (!heap_base) { + heap_base = brk(0); + heap_end = heap_base; + } + + void* old_end = heap_end; + void* new_end = (void*)((char*)heap_end + size); + void* result = brk(new_end); + + if ((unsigned int)result < (unsigned int)new_end) { + return (void*)0; /* OOM */ + } + + heap_end = new_end; + return old_end; +} + +void free(void* ptr) { + /* Bump allocator: no-op for now */ + (void)ptr; +} + +void* calloc(size_t nmemb, size_t size) { + size_t total = nmemb * size; + if (nmemb != 0 && total / nmemb != size) return (void*)0; + void* p = malloc(total); + if (p) memset(p, 0, total); + return p; +} + +void* realloc(void* ptr, size_t size) { + if (!ptr) return malloc(size); + if (size == 0) { free(ptr); return (void*)0; } + /* Bump allocator: just allocate new and copy. + * We don't know the old size, so copy 'size' bytes + * (caller must ensure old block >= size). */ + void* new_ptr = malloc(size); + if (new_ptr) memcpy(new_ptr, ptr, size); + return new_ptr; +} + +int atoi(const char* s) { + int n = 0; + int neg = 0; + while (*s == ' ' || *s == '\t') s++; + if (*s == '-') { neg = 1; s++; } + else if (*s == '+') { s++; } + while (*s >= '0' && *s <= '9') { + n = n * 10 + (*s - '0'); + s++; + } + return neg ? -n : n; +} + +void exit(int status) { + _exit(status); +} diff --git a/user/ulibc/src/string.c b/user/ulibc/src/string.c new file mode 100644 index 0000000..1201fe2 --- /dev/null +++ b/user/ulibc/src/string.c @@ -0,0 +1,93 @@ +#include "string.h" + +void* memcpy(void* dst, const void* src, size_t n) { + uint8_t* d = (uint8_t*)dst; + const uint8_t* s = (const uint8_t*)src; + for (size_t i = 0; i < n; i++) d[i] = s[i]; + return dst; +} + +void* memset(void* s, int c, size_t n) { + uint8_t* p = (uint8_t*)s; + for (size_t i = 0; i < n; i++) p[i] = (uint8_t)c; + return s; +} + +void* memmove(void* dst, const void* src, size_t n) { + uint8_t* d = (uint8_t*)dst; + const uint8_t* s = (const uint8_t*)src; + if (d < s) { + for (size_t i = 0; i < n; i++) d[i] = s[i]; + } else { + for (size_t i = n; i > 0; i--) d[i-1] = s[i-1]; + } + return dst; +} + +int memcmp(const void* a, const void* b, size_t n) { + const uint8_t* x = (const uint8_t*)a; + const uint8_t* y = (const uint8_t*)b; + for (size_t i = 0; i < n; i++) { + if (x[i] != y[i]) return (int)x[i] - (int)y[i]; + } + return 0; +} + +size_t strlen(const char* s) { + size_t n = 0; + while (s[n]) n++; + return n; +} + +char* strcpy(char* dst, const char* src) { + size_t i = 0; + while (src[i]) { dst[i] = src[i]; i++; } + dst[i] = 0; + return dst; +} + +char* strncpy(char* dst, const char* src, size_t n) { + size_t i = 0; + while (i < n && src[i]) { dst[i] = src[i]; i++; } + while (i < n) { dst[i] = 0; i++; } + return dst; +} + +int strcmp(const char* a, const char* b) { + while (*a && *a == *b) { a++; b++; } + return (int)(unsigned char)*a - (int)(unsigned char)*b; +} + +int strncmp(const char* a, const char* b, size_t n) { + for (size_t i = 0; i < n; i++) { + if (a[i] != b[i]) return (int)(unsigned char)a[i] - (int)(unsigned char)b[i]; + if (a[i] == 0) break; + } + return 0; +} + +char* strchr(const char* s, int c) { + while (*s) { + if (*s == (char)c) return (char*)s; + s++; + } + return (c == 0) ? (char*)s : (void*)0; +} + +char* strrchr(const char* s, int c) { + const char* last = (void*)0; + while (*s) { + if (*s == (char)c) last = s; + s++; + } + if (c == 0) return (char*)s; + return (char*)last; +} + +char* strcat(char* dst, const char* src) { + char* p = dst; + while (*p) p++; + while (*src) *p++ = *src++; + *p = 0; + return dst; +} diff --git a/user/ulibc/src/unistd.c b/user/ulibc/src/unistd.c new file mode 100644 index 0000000..5114a75 --- /dev/null +++ b/user/ulibc/src/unistd.c @@ -0,0 +1,92 @@ +#include "unistd.h" +#include "syscall.h" +#include "errno.h" + +int read(int fd, void* buf, size_t count) { + return __syscall_ret(_syscall3(SYS_READ, fd, (int)buf, (int)count)); +} + +int write(int fd, const void* buf, size_t count) { + return __syscall_ret(_syscall3(SYS_WRITE, fd, (int)buf, (int)count)); +} + +int open(const char* path, int flags) { + return __syscall_ret(_syscall2(SYS_OPEN, (int)path, flags)); +} + +int close(int fd) { + return __syscall_ret(_syscall1(SYS_CLOSE, fd)); +} + +int lseek(int fd, int offset, int whence) { + return __syscall_ret(_syscall3(SYS_LSEEK, fd, offset, whence)); +} + +int dup(int oldfd) { + return __syscall_ret(_syscall1(SYS_DUP, oldfd)); +} + +int dup2(int oldfd, int newfd) { + return __syscall_ret(_syscall2(SYS_DUP2, oldfd, newfd)); +} + +int pipe(int fds[2]) { + return __syscall_ret(_syscall1(SYS_PIPE, (int)fds)); +} + +int fork(void) { + return __syscall_ret(_syscall0(SYS_FORK)); +} + +int execve(const char* path, const char* const* argv, const char* const* envp) { + return __syscall_ret(_syscall3(SYS_EXECVE, (int)path, (int)argv, (int)envp)); +} + +int getpid(void) { + return _syscall0(SYS_GETPID); +} + +int getppid(void) { + return _syscall0(SYS_GETPPID); +} + +int chdir(const char* path) { + return __syscall_ret(_syscall1(SYS_CHDIR, (int)path)); +} + +int getcwd(char* buf, size_t size) { + return __syscall_ret(_syscall2(SYS_GETCWD, (int)buf, (int)size)); +} + +int mkdir(const char* path) { + return __syscall_ret(_syscall1(SYS_MKDIR, (int)path)); +} + +int unlink(const char* path) { + return __syscall_ret(_syscall1(SYS_UNLINK, (int)path)); +} + +int rmdir(const char* path) { + return __syscall_ret(_syscall1(SYS_RMDIR, (int)path)); +} + +int setsid(void) { + return __syscall_ret(_syscall0(SYS_SETSID)); +} + +int setpgid(int pid, int pgid) { + return __syscall_ret(_syscall2(SYS_SETPGID, pid, pgid)); +} + +int getpgrp(void) { + return __syscall_ret(_syscall0(SYS_GETPGRP)); +} + +void* brk(void* addr) { + return (void*)_syscall1(SYS_BRK, (int)addr); +} + +void _exit(int status) { + _syscall1(SYS_EXIT, status); + for (;;) __asm__ volatile("hlt"); +}