]> Projects (at) Tadryanom (dot) Me - AdrOS.git/commitdiff
feat: Phase 1 — critical ulibc gaps + uname syscall + new smoke test
authorTulio A M Mendes <[email protected]>
Sat, 14 Mar 2026 07:47:11 +0000 (04:47 -0300)
committerTulio A M Mendes <[email protected]>
Sat, 14 Mar 2026 07:47:11 +0000 (04:47 -0300)
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

17 files changed:
.gitignore
include/syscall.h
src/kernel/syscall.c
tests/smoke_test.exp
user/fulltest.c
user/ulibc/include/dirent.h
user/ulibc/include/poll.h [new file with mode: 0644]
user/ulibc/include/signal.h
user/ulibc/include/stdio.h
user/ulibc/include/sys/utsname.h [new file with mode: 0644]
user/ulibc/include/syscall.h
user/ulibc/include/unistd.h
user/ulibc/src/dirent.c [new file with mode: 0644]
user/ulibc/src/poll.c [new file with mode: 0644]
user/ulibc/src/stdio.c
user/ulibc/src/sysconf.c [new file with mode: 0644]
user/ulibc/src/utsname.c [new file with mode: 0644]

index bcebc406399db31f786eb1f43fab7484492a10f1..b58cd1fab28c9cbaf666ae12969511d8484cb983 100644 (file)
@@ -11,6 +11,7 @@ build/
 
 # ISO staging: keep config, ignore generated kernel binaries
 iso/boot/*.bin
+iso/bin/
 
 # Static analysis artifacts
 compile_commands.json
index 251b86288417c735e122d638900e66d8825409b2..2ddca80cea794d2d8f6ed767698fd4d8795514bd 100644 (file)
@@ -166,6 +166,7 @@ enum {
     SYSCALL_SHUTDOWN     = 133,
     SYSCALL_GETPEERNAME  = 134,
     SYSCALL_GETSOCKNAME  = 135,
+    SYSCALL_UNAME        = 136,
 };
 
 #endif
index ebd9826c2e566cdc7b89fbf99b507df741214c13..98e606659e2377a51cb94542799849b8a7400d58 100644 (file)
@@ -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;
 }
 
index c39fb0459759df0beb59c1251b2a24f160aa00be..9239254fa9a074ab3f619be0e798087a3b18ccf4 100755 (executable)
@@ -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"}
 }
 
index e0cd5cba4dffd3fdfc3417b05203eb3123a13899..4b059558c456ecaeaa3497f2363bf8a09da4605e 100644 (file)
@@ -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};
index eba5df005aa8240c8e89be9ff121d6cc866c1ef4..28e257388cfe4c06a364b8cedcce934ac7f348d4 100644 (file)
@@ -2,6 +2,7 @@
 #define ULIBC_DIRENT_H
 
 #include <stdint.h>
+#include <stddef.h>
 
 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 (file)
index 0000000..a090249
--- /dev/null
@@ -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
index 2fa5951ce87d1de0958225d79473b945b687b4c8..f9440009c6e0e056d38fa4a211696f92dad3896c 100644 (file)
@@ -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;
index 3c2880703b693c11483537555a62398668befa6f..f39cb5eb26d008d5f6da596e5a43e24a896d8768 100644 (file)
@@ -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 (file)
index 0000000..ea3b5e2
--- /dev/null
@@ -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
index 6bcaaea41816876837b23f428a3d3467107ab06d..e47d199c9885741e66a71be95f3640ef94e7eced 100644 (file)
@@ -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 */
index 96845101b4de38343e2c124a41523c8d2378aec9..faf3cb380033cb732f264a6d20748feb3bda7438 100644 (file)
@@ -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 (file)
index 0000000..36d4b97
--- /dev/null
@@ -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 (file)
index 0000000..1f72d19
--- /dev/null
@@ -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);
+}
index be589baf989adc4239a01334c13259d375904b7b..5301c7a5f45de85a5393e40c8bbaf30b36630266 100644 (file)
@@ -1,6 +1,7 @@
 #include "stdio.h"
 #include "unistd.h"
 #include "string.h"
+#include "stdlib.h"
 #include <stdarg.h>
 
 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 (file)
index 0000000..d9847ab
--- /dev/null
@@ -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 (file)
index 0000000..e802192
--- /dev/null
@@ -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);
+}