From: Tulio A M Mendes Date: Sat, 14 Mar 2026 07:47:11 +0000 (-0300) Subject: feat: Phase 1 — critical ulibc gaps + uname syscall + new smoke test X-Git-Url: https://projects.tadryanom.me/docs/static/gitweb.css?a=commitdiff_plain;h=1cc82e1979bfc0ea795e8620d71185026c3de0ad;p=AdrOS.git feat: Phase 1 — critical ulibc gaps + uname syscall + new smoke test Kernel: - Add SYSCALL_UNAME (136) with struct utsname (sysname=AdrOS, machine=i686) ulibc headers: - signal.h: Add sigset_t typedef, sigemptyset/sigfillset/sigaddset/sigdelset/ sigismember inline functions, SIG_BLOCK/SIG_UNBLOCK/SIG_SETMASK constants - poll.h: New header with struct pollfd, POLLIN/POLLOUT/POLLERR/POLLHUP/POLLNVAL - sys/utsname.h: New header with struct utsname + uname() declaration - dirent.h: Add DIR typedef, opendir/readdir/closedir/rewinddir declarations - unistd.h: Add sysconf/pathconf/fpathconf + _SC_*/_PC_* constants, gethostname, ttyname, pipe2 declarations - stdio.h: Add clearerr, ungetc, getc, putc, getline, getdelim, popen, pclose, tmpfile, tmpnam declarations + ssize_t typedef - syscall.h: Add SYS_UNAME (136) ulibc implementations: - poll.c: poll() syscall wrapper - utsname.c: uname() syscall wrapper - dirent.c: opendir/readdir/closedir/rewinddir wrapping getdents syscall - sysconf.c: sysconf/pathconf/fpathconf returning compile-time constants - stdio.c: clearerr, ungetc, getc, putc, getdelim, getline, popen/pclose, tmpfile, tmpnam implementations Tests: - fulltest.c: Add uname syscall test (G1) verifying sysname=AdrOS, machine=i686 - smoke_test.exp: Add uname test pattern (101 total tests) Tests: 101/101 smoke, 64/64 host utils, cppcheck clean --- diff --git a/.gitignore b/.gitignore index bcebc40..b58cd1f 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,7 @@ build/ # ISO staging: keep config, ignore generated kernel binaries iso/boot/*.bin +iso/bin/ # Static analysis artifacts compile_commands.json diff --git a/include/syscall.h b/include/syscall.h index 251b862..2ddca80 100644 --- a/include/syscall.h +++ b/include/syscall.h @@ -166,6 +166,7 @@ enum { SYSCALL_SHUTDOWN = 133, SYSCALL_GETPEERNAME = 134, SYSCALL_GETSOCKNAME = 135, + SYSCALL_UNAME = 136, }; #endif diff --git a/src/kernel/syscall.c b/src/kernel/syscall.c index ebd9826..98e6066 100644 --- a/src/kernel/syscall.c +++ b/src/kernel/syscall.c @@ -4866,6 +4866,33 @@ static void socket_syscall_dispatch(struct registers* regs, uint32_t syscall_no) return; } + if (syscall_no == SYSCALL_UNAME) { + /* struct utsname: 5 fields of 65 bytes each = 325 bytes */ + void* user_buf = (void*)sc_arg0(regs); + if (!user_buf) { sc_ret(regs) = (uint32_t)-EFAULT; return; } + if (user_range_ok(user_buf, 325) == 0) { sc_ret(regs) = (uint32_t)-EFAULT; return; } + + struct { + char sysname[65]; + char nodename[65]; + char release[65]; + char version[65]; + char machine[65]; + } uts; + memset(&uts, 0, sizeof(uts)); + strcpy(uts.sysname, "AdrOS"); + strcpy(uts.nodename, "adros"); + strcpy(uts.release, "1.0.0"); + strcpy(uts.version, "AdrOS 1.0.0 POSIX"); + strcpy(uts.machine, "i686"); + + if (copy_to_user(user_buf, &uts, sizeof(uts)) < 0) { + sc_ret(regs) = (uint32_t)-EFAULT; return; + } + sc_ret(regs) = 0; + return; + } + sc_ret(regs) = (uint32_t)-ENOSYS; } diff --git a/tests/smoke_test.exp b/tests/smoke_test.exp index c39fb04..9239254 100755 --- a/tests/smoke_test.exp +++ b/tests/smoke_test.exp @@ -142,6 +142,7 @@ set tests { {"gettimeofday" "\\[init\\] gettimeofday OK"} {"mprotect" "\\[init\\] mprotect OK"} {"getrlimit/setrlimit" "\\[init\\] getrlimit/setrlimit OK"} + {"uname" "\\[init\\] uname OK"} {"LZ4 Frame decomp" "\\[INITRD\\] LZ4"} } diff --git a/user/fulltest.c b/user/fulltest.c index e0cd5cb..4b05955 100644 --- a/user/fulltest.c +++ b/user/fulltest.c @@ -147,6 +147,7 @@ enum { SYSCALL_MPROTECT = 128, SYSCALL_GETRLIMIT = 129, SYSCALL_SETRLIMIT = 130, + SYSCALL_UNAME = 136, }; enum { @@ -1353,6 +1354,20 @@ static int sys_getrlimit(int resource, struct rlimit* rlim) { return __syscall_fix(ret); } +struct utsname { + char sysname[65]; + char nodename[65]; + char release[65]; + char version[65]; + char machine[65]; +}; + +static int sys_uname(struct utsname* buf) { + int ret; + __asm__ volatile("int $0x80" : "=a"(ret) : "a"(SYSCALL_UNAME), "b"(buf) : "memory"); + return __syscall_fix(ret); +} + static int sys_setrlimit(int resource, const struct rlimit* rlim) { int ret; __asm__ volatile("int $0x80" : "=a"(ret) : "a"(SYSCALL_SETRLIMIT), "b"(resource), "c"(rlim) : "memory"); @@ -4358,6 +4373,29 @@ void _start(void) { (void)sys_write(1, wbad, (uint32_t)(sizeof(wbad) - 1)); } + // G1: uname syscall test + { + struct utsname uts; + for (uint32_t i = 0; i < sizeof(uts); i++) ((char*)&uts)[i] = 0; + int r = sys_uname(&uts); + if (r < 0) { + sys_write(1, "[init] uname failed\n", (uint32_t)(sizeof("[init] uname failed\n") - 1)); + sys_exit(1); + } + /* Verify sysname == "AdrOS" */ + if (uts.sysname[0] != 'A' || uts.sysname[1] != 'd' || uts.sysname[2] != 'r' || + uts.sysname[3] != 'O' || uts.sysname[4] != 'S' || uts.sysname[5] != 0) { + sys_write(1, "[init] uname sysname bad\n", (uint32_t)(sizeof("[init] uname sysname bad\n") - 1)); + sys_exit(1); + } + /* Verify machine == "i686" */ + if (uts.machine[0] != 'i' || uts.machine[1] != '6' || uts.machine[2] != '8' || uts.machine[3] != '6') { + sys_write(1, "[init] uname machine bad\n", (uint32_t)(sizeof("[init] uname machine bad\n") - 1)); + sys_exit(1); + } + sys_write(1, "[init] uname OK\n", (uint32_t)(sizeof("[init] uname OK\n") - 1)); + } + (void)sys_write(1, "[init] execve(/bin/echo)\n", (uint32_t)(sizeof("[init] execve(/bin/echo)\n") - 1)); static const char* const argv[] = {"echo", "[echo]", "hello", "from", "echo", 0}; diff --git a/user/ulibc/include/dirent.h b/user/ulibc/include/dirent.h index eba5df0..28e2573 100644 --- a/user/ulibc/include/dirent.h +++ b/user/ulibc/include/dirent.h @@ -2,6 +2,7 @@ #define ULIBC_DIRENT_H #include +#include struct dirent { uint32_t d_ino; @@ -17,4 +18,16 @@ struct dirent { #define DT_BLK 6 #define DT_LNK 10 +typedef struct _DIR { + int fd; + int pos; + int len; + char buf[4096]; +} DIR; + +DIR* opendir(const char* name); +struct dirent* readdir(DIR* dirp); +int closedir(DIR* dirp); +void rewinddir(DIR* dirp); + #endif diff --git a/user/ulibc/include/poll.h b/user/ulibc/include/poll.h new file mode 100644 index 0000000..a090249 --- /dev/null +++ b/user/ulibc/include/poll.h @@ -0,0 +1,21 @@ +#ifndef ULIBC_POLL_H +#define ULIBC_POLL_H + +#define POLLIN 0x0001 +#define POLLPRI 0x0002 +#define POLLOUT 0x0004 +#define POLLERR 0x0008 +#define POLLHUP 0x0010 +#define POLLNVAL 0x0020 + +struct pollfd { + int fd; + short events; + short revents; +}; + +typedef unsigned int nfds_t; + +int poll(struct pollfd* fds, nfds_t nfds, int timeout); + +#endif diff --git a/user/ulibc/include/signal.h b/user/ulibc/include/signal.h index 2fa5951..f944000 100644 --- a/user/ulibc/include/signal.h +++ b/user/ulibc/include/signal.h @@ -65,6 +65,30 @@ int sigprocmask(int how, const uint32_t* set, uint32_t* oldset); int sigpending(uint32_t* set); int sigsuspend(const uint32_t* mask); +/* sigset_t manipulation macros (signal mask is a 32-bit bitmask) */ +typedef uint32_t sigset_t; + +#define _SIGMASK(sig) (1U << ((sig) - 1)) + +static inline int sigemptyset(sigset_t* set) { *set = 0; return 0; } +static inline int sigfillset(sigset_t* set) { *set = 0xFFFFFFFFU; return 0; } +static inline int sigaddset(sigset_t* set, int sig) { + if (sig < 1 || sig > 32) return -1; + *set |= _SIGMASK(sig); return 0; +} +static inline int sigdelset(sigset_t* set, int sig) { + if (sig < 1 || sig > 32) return -1; + *set &= ~_SIGMASK(sig); return 0; +} +static inline int sigismember(const sigset_t* set, int sig) { + if (sig < 1 || sig > 32) return -1; + return (*set & _SIGMASK(sig)) ? 1 : 0; +} + +#define SIG_BLOCK 0 +#define SIG_UNBLOCK 1 +#define SIG_SETMASK 2 + #define SS_DISABLE 2 typedef struct { void* ss_sp; diff --git a/user/ulibc/include/stdio.h b/user/ulibc/include/stdio.h index 3c28807..f39cb5e 100644 --- a/user/ulibc/include/stdio.h +++ b/user/ulibc/include/stdio.h @@ -70,5 +70,19 @@ void setbuf(FILE* fp, char* buf); void perror(const char* s); int fileno(FILE* fp); FILE* fdopen(int fd, const char* mode); +void clearerr(FILE* fp); +int ungetc(int c, FILE* fp); +int getc(FILE* fp); +int putc(int c, FILE* fp); + +typedef long ssize_t; +ssize_t getline(char** lineptr, size_t* n, FILE* stream); +ssize_t getdelim(char** lineptr, size_t* n, int delim, FILE* stream); + +FILE* popen(const char* command, const char* type); +int pclose(FILE* fp); + +FILE* tmpfile(void); +char* tmpnam(char* s); #endif diff --git a/user/ulibc/include/sys/utsname.h b/user/ulibc/include/sys/utsname.h new file mode 100644 index 0000000..ea3b5e2 --- /dev/null +++ b/user/ulibc/include/sys/utsname.h @@ -0,0 +1,16 @@ +#ifndef ULIBC_SYS_UTSNAME_H +#define ULIBC_SYS_UTSNAME_H + +#define _UTSNAME_LENGTH 65 + +struct utsname { + char sysname[_UTSNAME_LENGTH]; + char nodename[_UTSNAME_LENGTH]; + char release[_UTSNAME_LENGTH]; + char version[_UTSNAME_LENGTH]; + char machine[_UTSNAME_LENGTH]; +}; + +int uname(struct utsname* buf); + +#endif diff --git a/user/ulibc/include/syscall.h b/user/ulibc/include/syscall.h index 6bcaaea..e47d199 100644 --- a/user/ulibc/include/syscall.h +++ b/user/ulibc/include/syscall.h @@ -90,6 +90,7 @@ enum { SYS_SHUTDOWN = 133, SYS_GETPEERNAME = 134, SYS_GETSOCKNAME = 135, + SYS_UNAME = 136, }; /* Raw syscall wrappers — up to 5 args via INT 0x80 */ diff --git a/user/ulibc/include/unistd.h b/user/ulibc/include/unistd.h index 9684510..faf3cb3 100644 --- a/user/ulibc/include/unistd.h +++ b/user/ulibc/include/unistd.h @@ -77,6 +77,31 @@ extern int optind, opterr, optopt; void _exit(int status) __attribute__((noreturn)); +/* sysconf / pathconf constants */ +#define _SC_CLK_TCK 2 +#define _SC_PAGE_SIZE 30 +#define _SC_PAGESIZE _SC_PAGE_SIZE +#define _SC_OPEN_MAX 4 +#define _SC_NGROUPS_MAX 3 +#define _SC_CHILD_MAX 1 +#define _SC_ARG_MAX 0 +#define _SC_HOST_NAME_MAX 180 +#define _SC_LOGIN_NAME_MAX 71 +#define _SC_LINE_MAX 43 + +#define _PC_PATH_MAX 4 +#define _PC_NAME_MAX 3 +#define _PC_PIPE_BUF 5 +#define _PC_LINK_MAX 0 + +long sysconf(int name); +long pathconf(const char* path, int name); +long fpathconf(int fd, int name); + +int gethostname(char* name, size_t len); +char* ttyname(int fd); +int pipe2(int fds[2], int flags); + /* Environment pointer (set by crt0) */ extern char** __environ; diff --git a/user/ulibc/src/dirent.c b/user/ulibc/src/dirent.c new file mode 100644 index 0000000..36d4b97 --- /dev/null +++ b/user/ulibc/src/dirent.c @@ -0,0 +1,62 @@ +#include "dirent.h" +#include "unistd.h" +#include "fcntl.h" +#include "stdlib.h" +#include "string.h" +#include "syscall.h" +#include "errno.h" + +/* AdrOS getdents returns fixed-size entries: { uint32_t ino; char name[256]; } = 260 bytes */ +#define ADROS_DIRENT_SIZE 260 + +DIR* opendir(const char* name) { + int fd = open(name, O_RDONLY); + if (fd < 0) return (void*)0; + DIR* d = (DIR*)malloc(sizeof(DIR)); + if (!d) { close(fd); errno = ENOMEM; return (void*)0; } + d->fd = fd; + d->pos = 0; + d->len = 0; + return d; +} + +static struct dirent _de_static; + +struct dirent* readdir(DIR* dirp) { + if (!dirp) return (void*)0; + + /* Refill buffer if exhausted */ + if (dirp->pos >= dirp->len) { + int n = getdents(dirp->fd, dirp->buf, sizeof(dirp->buf)); + if (n <= 0) return (void*)0; + dirp->len = n; + dirp->pos = 0; + } + + if (dirp->pos + ADROS_DIRENT_SIZE > dirp->len) return (void*)0; + + char* ent = dirp->buf + dirp->pos; + uint32_t ino; + memcpy(&ino, ent, 4); + _de_static.d_ino = ino; + _de_static.d_reclen = ADROS_DIRENT_SIZE; + _de_static.d_type = DT_UNKNOWN; + strncpy(_de_static.d_name, ent + 4, 255); + _de_static.d_name[255] = '\0'; + dirp->pos += ADROS_DIRENT_SIZE; + return &_de_static; +} + +int closedir(DIR* dirp) { + if (!dirp) return -1; + int r = close(dirp->fd); + free(dirp); + return r; +} + +void rewinddir(DIR* dirp) { + if (!dirp) return; + lseek(dirp->fd, 0, SEEK_SET); + dirp->pos = 0; + dirp->len = 0; +} diff --git a/user/ulibc/src/poll.c b/user/ulibc/src/poll.c new file mode 100644 index 0000000..1f72d19 --- /dev/null +++ b/user/ulibc/src/poll.c @@ -0,0 +1,8 @@ +#include "poll.h" +#include "syscall.h" +#include "errno.h" + +int poll(struct pollfd* fds, nfds_t nfds, int timeout) { + int r = _syscall3(SYS_POLL, (int)fds, (int)nfds, timeout); + return __syscall_ret(r); +} diff --git a/user/ulibc/src/stdio.c b/user/ulibc/src/stdio.c index be589ba..5301c7a 100644 --- a/user/ulibc/src/stdio.c +++ b/user/ulibc/src/stdio.c @@ -1,6 +1,7 @@ #include "stdio.h" #include "unistd.h" #include "string.h" +#include "stdlib.h" #include static FILE _stdin_file = { .fd = 0, .flags = _STDIO_READ }; @@ -450,3 +451,128 @@ int printf(const char* fmt, ...) { va_end(ap); return r; } + +void clearerr(FILE* fp) { + if (fp) fp->flags &= ~(_STDIO_EOF | _STDIO_ERR); +} + +int ungetc(int c, FILE* fp) { + if (!fp || c == EOF) return EOF; + /* Simple 1-byte pushback: store in buf_pos-1 if possible */ + if (fp->buf_pos > 0) { + fp->buf_pos--; + fp->buf[fp->buf_pos] = (char)c; + } else if (fp->buf_len < (int)sizeof(fp->buf)) { + /* Shift buffer right by 1 */ + for (int i = fp->buf_len; i > 0; i--) + fp->buf[i] = fp->buf[i - 1]; + fp->buf[0] = (char)c; + fp->buf_len++; + } else { + return EOF; + } + fp->flags &= ~_STDIO_EOF; + return (unsigned char)c; +} + +int getc(FILE* fp) { return fgetc(fp); } +int putc(int c, FILE* fp) { return fputc(c, fp); } + +ssize_t getdelim(char** lineptr, size_t* n, int delim, FILE* stream) { + if (!lineptr || !n || !stream) { return -1; } + + if (!*lineptr || *n == 0) { + *n = 128; + *lineptr = (char*)malloc(*n); + if (!*lineptr) return -1; + } + + size_t pos = 0; + int c; + while ((c = fgetc(stream)) != EOF) { + if (pos + 2 > *n) { + size_t newn = *n * 2; + char* tmp = (char*)realloc(*lineptr, newn); + if (!tmp) return -1; + *lineptr = tmp; + *n = newn; + } + (*lineptr)[pos++] = (char)c; + if (c == delim) break; + } + if (pos == 0 && c == EOF) return -1; + (*lineptr)[pos] = '\0'; + return (ssize_t)pos; +} + +ssize_t getline(char** lineptr, size_t* n, FILE* stream) { + return getdelim(lineptr, n, '\n', stream); +} + +FILE* popen(const char* command, const char* type) { + int fds[2]; + extern int pipe(int[2]); + extern int fork(void); + extern int execl(const char*, const char*, ...); + extern int dup2(int, int); + extern int close(int); + extern void _exit(int); + + if (pipe(fds) < 0) return (FILE*)0; + + int pid = fork(); + if (pid < 0) { + close(fds[0]); + close(fds[1]); + return (FILE*)0; + } + if (pid == 0) { + /* Child */ + if (type[0] == 'r') { + close(fds[0]); + dup2(fds[1], 1); + close(fds[1]); + } else { + close(fds[1]); + dup2(fds[0], 0); + close(fds[0]); + } + execl("/bin/sh", "sh", "-c", command, (char*)0); + _exit(127); + } + /* Parent */ + if (type[0] == 'r') { + close(fds[1]); + return fdopen(fds[0], "r"); + } else { + close(fds[0]); + return fdopen(fds[1], "w"); + } +} + +int pclose(FILE* fp) { + extern int waitpid(int, int*, int); + if (!fp) return -1; + int fd = fileno(fp); + (void)fd; + fclose(fp); + int status = 0; + waitpid(-1, &status, 0); + return status; +} + +FILE* tmpfile(void) { + static int tmpcount = 0; + char name[32]; + extern int getpid(void); + snprintf(name, sizeof(name), "/tmp/.tmpf_%d_%d", getpid(), tmpcount++); + return fopen(name, "w+"); +} + +char* tmpnam(char* s) { + static char buf[32]; + static int count = 0; + snprintf(buf, sizeof(buf), "/tmp/tmp_%d", count++); + if (s) { strcpy(s, buf); return s; } + return buf; +} diff --git a/user/ulibc/src/sysconf.c b/user/ulibc/src/sysconf.c new file mode 100644 index 0000000..d9847ab --- /dev/null +++ b/user/ulibc/src/sysconf.c @@ -0,0 +1,37 @@ +#include "unistd.h" +#include "errno.h" + +long sysconf(int name) { + switch (name) { + case _SC_CLK_TCK: return 100; + case _SC_PAGE_SIZE: return 4096; + case _SC_OPEN_MAX: return 64; + case _SC_NGROUPS_MAX: return 0; + case _SC_CHILD_MAX: return 128; + case _SC_ARG_MAX: return 131072; + case _SC_HOST_NAME_MAX: return 64; + case _SC_LOGIN_NAME_MAX: return 32; + case _SC_LINE_MAX: return 2048; + default: + errno = EINVAL; + return -1; + } +} + +long pathconf(const char* path, int name) { + (void)path; + switch (name) { + case _PC_PATH_MAX: return 256; + case _PC_NAME_MAX: return 255; + case _PC_PIPE_BUF: return 4096; + case _PC_LINK_MAX: return 127; + default: + errno = EINVAL; + return -1; + } +} + +long fpathconf(int fd, int name) { + (void)fd; + return pathconf("/", name); +} diff --git a/user/ulibc/src/utsname.c b/user/ulibc/src/utsname.c new file mode 100644 index 0000000..e802192 --- /dev/null +++ b/user/ulibc/src/utsname.c @@ -0,0 +1,8 @@ +#include "sys/utsname.h" +#include "syscall.h" +#include "errno.h" + +int uname(struct utsname* buf) { + int r = _syscall1(SYS_UNAME, (int)buf); + return __syscall_ret(r); +}