From: Tulio A M Mendes Date: Sat, 14 Mar 2026 07:34:43 +0000 (-0300) Subject: feat: Phase 0 — convert libgloss posix_stubs.c from ENOSYS to real AdrOS syscalls X-Git-Url: https://projects.tadryanom.me/?a=commitdiff_plain;h=760380fbdb7cac80218b057fdc61e376a024efd0;p=AdrOS.git feat: Phase 0 — convert libgloss posix_stubs.c from ENOSYS to real AdrOS syscalls Convert ~30 POSIX stub functions in newlib/libgloss/adros/posix_stubs.c from returning ENOSYS to making real AdrOS kernel syscalls via INT 0x80. Functions now making real syscalls: - File ops: dup, dup2, fcntl, pipe, chown, chmod, access, chdir, getcwd - Process: waitpid, setpgid, setsid, getpgrp, getppid, getuid/geteuid/ getgid/getegid, setuid/setgid, setreuid/setregid, umask, alarm, sleep - Signals: sigaction, sigprocmask - Terminal: tcgetattr, tcsetattr, tcgetpgrp, tcsetpgrp, ioctl - I/O mux: select, pselect (wraps select) - Directory: opendir/readdir/closedir/rewinddir (wraps getdents) - Improved fnmatch with proper * and ? wildcard support Also adds docs/SELF_HOSTING_ANALYSIS.md with comprehensive POSIX gap analysis, self-hosting assessment, and prioritized implementation plan. Bash 5.2.21 rebuilt against new libadros.a (1.7MB static ELF i386). Tests: 100/100 smoke, 64/64 host utils, cppcheck clean --- diff --git a/docs/SELF_HOSTING_ANALYSIS.md b/docs/SELF_HOSTING_ANALYSIS.md new file mode 100644 index 0000000..bd238d1 --- /dev/null +++ b/docs/SELF_HOSTING_ANALYSIS.md @@ -0,0 +1,334 @@ +# AdrOS — Deep POSIX Audit, Self-Hosting Assessment & Implementation Plan + +**Date:** 2026-03-14 +**Base Commit:** f2506b7 (master) + +--- + +## Part 1: FULL_POSIX_AUDIT.md vs POSIX_ROADMAP.md — Reconciliation + +### Summary + +`POSIX_ROADMAP.md` tracks 75 features as all completed (✅) and declares "~98% POSIX". +`FULL_POSIX_AUDIT.md` identifies critical gaps that block Bash/Busybox/GCC porting. + +**Both documents are partially outdated.** Since the audit was written (commit 2deaf85), significant work was done in commits `9b56e33` through `f2506b7`, adding: +- 9 new kernel syscalls (gettimeofday, mprotect, getrlimit/setrlimit, setsockopt/getsockopt, shutdown, getpeername/getsockname) +- 15+ new ulibc headers and functions (setjmp.h, locale.h, pwd.h, grp.h, fnmatch.h, getopt.h, sys/select.h, sys/resource.h) +- Complete cross-toolchain (binutils 2.42 + GCC 13.2.0 + Newlib 4.4.0) +- Bash 5.2.21 cross-compiled as static ELF 32-bit i386 +- Proper malloc with free() (free-list allocator with coalescing) + +### Items the Audit flagged as missing that are NOW IMPLEMENTED + +| Item | Status | Evidence | +|------|--------|----------| +| `mprotect` syscall | ✅ | syscall 128, `syscall.c` line ~3939 | +| `gettimeofday` syscall | ✅ | syscall 127, `syscall.c` line ~3880 | +| `getrlimit`/`setrlimit` | ✅ | syscalls 129/130, `syscall.c` line ~3900 | +| `setsockopt`/`getsockopt` | ✅ | syscalls 131/132 | +| `shutdown` (socket) | ✅ | syscall 133 | +| `getpeername`/`getsockname` | ✅ | syscalls 134/135 | +| `` | ✅ | i386 asm: `setjmp.S` (setjmp/longjmp/sigsetjmp/siglongjmp) | +| `` | ✅ | C locale stubs: `locale.c` | +| `` / `` | ✅ | `pwd_grp.c` with getpwnam/getpwuid/getpwent/getgrnam/getgrgid/getgrent | +| `` | ✅ | `fnmatch.c` with pattern matching | +| `` | ✅ | `getopt.c` with getopt/getopt_long | +| `` | ✅ | FD_SET/FD_CLR/FD_ISSET/FD_ZERO macros | +| `` | ✅ | getrlimit/setrlimit wrappers | +| `strtoul`/`strtoll`/`strtoull` | ✅ | `strtoul.c` | +| `setenv`/`unsetenv`/`putenv` | ✅ | `environ.c` | +| `atexit`/`abort` | ✅ | `abort_atexit.c` | +| `rand`/`srand` | ✅ | `rand.c` | +| `strerror` | ✅ | `strerror.c` | +| `strtok_r`/`strpbrk`/`strspn`/`strcspn`/`strnlen` | ✅ | `string.c` | +| `sleep`/`usleep` | ✅ | `sleep.c` | +| `execvp`/`execlp`/`execl` | ✅ | `execvp.c` | +| `signal()` wrapper | ✅ | `signal_wrap.c` | +| `perror`/`fileno`/`fdopen` | ✅ | `stdio.c` | +| `free()` (proper allocator) | ✅ | `stdlib.c` — free-list with coalescing | +| `.gitignore` | ✅ | exists at root | +| `.gitmodules` | ✅ | exists (lwip + doomgeneric) | + +--- + +## Part 2: What's STILL Truly Missing (verified against source code) + +### 2A. Missing Kernel Syscalls + +| Syscall | Priority | Needed By | Effort | +|---------|----------|-----------|--------| +| `getrusage` | High | Bash (`time` builtin), make | Small — wrapper over process utime/stime | +| `wait4` | Medium | Some programs (fallback for waitpid) | Small — wrapper over waitpid + rusage | +| `ioctl FIONREAD` | High | Bash, many programs | Small — return bytes available in pipe/tty buffer | +| `umount2` | Low | Full mount/umount | Small | +| `lstat` | Medium | ls -l, find, many programs | Small — stat without following symlinks | +| `fchmod`/`fchown` | Medium | Bash, tar, cp -p | Small — chmod/chown by fd | +| `uname` syscall | Medium | autoconf, Bash | Small — return system info struct | +| `getrusage` | High | Bash `time`, make | Small | + +### 2B. Missing ulibc Headers (not in `user/ulibc/include/`) + +| Header | Priority | Needed By | Effort | +|--------|----------|-----------|--------| +| `` | **Critical** | Bash `[[ =~ ]]`, grep, sed | Large — need regex engine | +| `` | **Critical** | Bash wildcard expansion | Medium — glob/globfree | +| `` | High | autoconf, uname command, Bash | Small — struct + uname() | +| `` | High | Many programs | Small — poll/struct pollfd | +| `` | Medium | POSIX basename/dirname | Small | +| `` | Medium | Network programs | Medium | +| `` | Medium | Network programs | Small | +| `` | Medium | Network programs | Small | +| `` | Medium | Network programs | Medium | +| `` | Low | GCC plugins, shared libs | Small | +| `` | Low | Some programs | Small | +| `` | Low | Bash | Medium | +| `` | Low | Unix domain sockets | Small | +| `` | Low | Daemons | Small (stubs) | + +### 2C. Missing Functions in EXISTING ulibc Headers + +#### `` — Missing: +| Function | Priority | Effort | +|----------|----------|--------| +| `popen`/`pclose` | High (Bash) | Medium — fork+exec+pipe | +| `getline`/`getdelim` | High (many programs) | Small | +| `ungetc` | Medium | Small — 1 byte pushback | +| `clearerr` | Medium | Trivial | +| `scanf`/`fscanf` | Medium | Large (full implementation) | +| `tmpfile`/`tmpnam` | Medium (GCC) | Small | + +#### `` — Missing: +| Function | Priority | Effort | +|----------|----------|--------| +| `sysconf`/`pathconf`/`fpathconf` | **Critical** (autoconf) | Medium — return constants | +| `tcgetpgrp`/`tcsetpgrp` | High (Bash job control) | Small — ioctl wrapper | +| `pipe2` | Medium | Small — syscall exists | +| `gethostname` | Medium | Small | +| `ttyname`/`ttyname_r` | Medium | Small | +| `getlogin` | Low | Small | +| `confstr` | Low | Small | + +#### `` — Missing: +| Function | Priority | Effort | +|----------|----------|--------| +| `sigemptyset`/`sigfillset`/`sigaddset`/`sigdelset`/`sigismember` | **Critical** | Trivial — macros on uint32_t | + +#### `` — Missing: +| Function | Priority | Effort | +|----------|----------|--------| +| `pthread_mutex_init/lock/unlock/destroy` | Medium | Small — futex-based | +| `pthread_cond_*` | Medium | Medium | +| `pthread_key_*` (TLS) | Medium | Medium | +| `pthread_once` | Low | Small | + +#### `` — Missing: +| Function | Priority | Effort | +|----------|----------|--------| +| `cfmakeraw` | High (Bash) | Trivial — set flags | +| `cfgetispeed`/`cfsetispeed` etc. | Medium | Small | +| `tcdrain`/`tcflush`/`tcflow` | Medium | Small — ioctl wrappers | + +### 2D. Other Gaps + +| Gap | Priority | Impact | +|-----|----------|--------| +| `/etc/passwd` and `/etc/group` files | High | pwd.h functions return hardcoded "root" | +| `sigsetjmp` doesn't save/restore signal mask | Medium | Bash error recovery | +| `opendir`/`readdir`/`closedir` in ulibc | **Critical** | Bash glob, ls, find, many programs | +| `ssize_t` return types for read/write | Low | POSIX compliance | + +--- + +## Part 3: Self-Hosting Assessment + +### 3A. Cross-Toolchain Status: ✅ COMPLETE + +| Component | Version | Status | +|-----------|---------|--------| +| `i686-adros-gcc` | 13.2.0 | ✅ Installed at `/opt/adros-toolchain/bin/` | +| `i686-adros-g++` | 13.2.0 | ✅ Available | +| `i686-adros-as` | 2.42 | ✅ Binutils | +| `i686-adros-ld` | 2.42 | ✅ Binutils | +| Newlib (libc) | 4.4.0 | ✅ Installed in sysroot | +| libgloss/adros | — | ✅ crt0.o + libadros.a in sysroot | +| Bash | 5.2.21 | ✅ Cross-compiled (static ELF 32-bit i386) | + +### 3B. Can Bash Run on AdrOS? — NOT YET + +**The Bash binary exists** (`toolchain/build/bash/bash`, ELF 32-bit i386, static), but it was compiled with **stub implementations** in `posix_stubs.c`. These stubs return `ENOSYS` for critical operations: + +| Stubbed Function | Real AdrOS Syscall | Status | +|-----------------|-------------------|--------| +| `dup(fd)` | SYSCALL_DUP (12) | ❌ Stub returns ENOSYS | +| `dup2(old, new)` | SYSCALL_DUP2 (13) | ❌ Stub returns ENOSYS | +| `fcntl(fd, cmd)` | SYSCALL_FCNTL (31) | ❌ Stub returns ENOSYS | +| `pipe(fds)` | SYSCALL_PIPE (14) | ❌ Stub returns ENOSYS | +| `select(...)` | SYSCALL_SELECT (20) | ❌ Stub returns ENOSYS | +| `sigaction(...)` | SYSCALL_SIGACTION (25) | ❌ Stub returns ENOSYS | +| `setpgid(...)` | SYSCALL_SETPGID (23) | ❌ Stub returns ENOSYS | +| `setsid()` | SYSCALL_SETSID (22) | ❌ Stub returns ENOSYS | +| `tcgetattr(...)` | SYSCALL_IOCTL/TCGETS (21) | ❌ Stub returns ENOSYS | +| `tcsetattr(...)` | SYSCALL_IOCTL/TCSETS (21) | ❌ Stub returns ENOSYS | +| `chdir(path)` | SYSCALL_CHDIR (32) | ❌ Stub returns ENOSYS | +| `getcwd(buf, sz)` | SYSCALL_GETCWD (33) | ❌ Returns "/" hardcoded | +| `ioctl(fd, req)` | SYSCALL_IOCTL (21) | ❌ Stub returns ENOSYS | +| `opendir/readdir/closedir` | SYSCALL_GETDENTS (30) | ❌ Stub returns ENOSYS/NULL | +| `access(path, mode)` | SYSCALL_ACCESS (74) | ❌ Stub always returns 0 | +| `waitpid(...)` | SYSCALL_WAITPID (7) | ❌ Stub returns ECHILD | + +**The kernel has ALL these syscalls implemented.** The blocker is that the Newlib libgloss stubs don't call them. + +### 3C. Can GCC/Binutils Run Natively? — NO (far from it) + +Native self-hosting requires: +1. **Bash or sh** working natively (for configure scripts) — NOT YET +2. **GCC + binutils** as native ELF binaries running on AdrOS — NOT BUILT +3. **Enough RAM** — GCC needs ~64MB+ for compilation +4. **Working /tmp** — tmpfs exists ✅ +5. **Working pipes/process creation** — kernel supports ✅, libgloss stubs ❌ +6. **Time functions** — gettimeofday ✅, clock_gettime ✅ +7. **Large file support** — 32-bit off_t limits files to 4GB (acceptable for now) + +**Critical path to self-hosting:** +``` +Fix libgloss stubs → Bash runs on AdrOS → Cross-compile native GCC/binutils +→ Package on disk image → Boot AdrOS → Run native GCC +``` + +### 3D. Can Busybox Run on AdrOS? — NOT YET + +Same libgloss stub problem as Bash, plus Busybox needs additional APIs: +- `getpwent`/`getgrent` iteration +- `syslog()` (can be stubbed) +- Network socket wrappers in libc + +--- + +## Part 4: Prioritized Implementation Plan + +### Phase 0: IMMEDIATE — Fix libgloss posix_stubs.c (1-2 days) + +**This is THE critical blocker.** Convert all ENOSYS stubs in `newlib/libgloss/adros/posix_stubs.c` to real AdrOS syscall wrappers. The kernel already has every required syscall. + +Functions to implement (in order of importance): + +1. **dup, dup2** → INT 0x80 with SYSCALL_DUP/DUP2 +2. **pipe** → INT 0x80 with SYSCALL_PIPE +3. **fcntl** → INT 0x80 with SYSCALL_FCNTL +4. **sigaction, sigprocmask** → INT 0x80 with SYSCALL_SIGACTION/SIGPROCMASK +5. **waitpid** → INT 0x80 with SYSCALL_WAITPID +6. **setpgid, setsid, getpgrp** → INT 0x80 with SYSCALL_SETPGID/SETSID/GETPGRP +7. **chdir, getcwd** → INT 0x80 with SYSCALL_CHDIR/GETCWD +8. **select** → INT 0x80 with SYSCALL_SELECT +9. **tcgetattr, tcsetattr** → INT 0x80 with SYSCALL_IOCTL + TCGETS/TCSETS +10. **tcgetpgrp, tcsetpgrp** → INT 0x80 with SYSCALL_IOCTL + TIOCGPGRP/TIOCSPGRP +11. **ioctl** → INT 0x80 with SYSCALL_IOCTL +12. **access** → INT 0x80 with SYSCALL_ACCESS +13. **alarm** → INT 0x80 with SYSCALL_ALARM +14. **chown** → INT 0x80 with SYSCALL_CHOWN +15. **opendir/readdir/closedir** → wrap SYSCALL_OPEN + SYSCALL_GETDENTS + SYSCALL_CLOSE +16. **sleep** → INT 0x80 with SYSCALL_NANOSLEEP +17. **umask** → INT 0x80 with SYSCALL_UMASK +18. **getuid/geteuid/getgid/getegid/getppid/setuid/setgid** → direct syscalls + +After fixing: **rebuild Bash** with `./toolchain/build.sh` and test on AdrOS. + +### Phase 1: Critical ulibc Gaps for Bash (2-3 days) + +Even with Newlib as the runtime libc, these gaps affect programs compiled against ulibc (the native AdrOS userland): + +1. **sigset macros** — `sigemptyset`/`sigfillset`/`sigaddset`/`sigdelset`/`sigismember` (trivial bitops) +2. **``** + `uname` syscall — struct utsname with sysname/nodename/release/version/machine +3. **``** — struct pollfd + poll() wrapper (syscall exists) +4. **``** — glob/globfree (Bash wildcard expansion) +5. **`opendir`/`readdir`/`closedir`** in ulibc — wrap getdents syscall +6. **`sysconf`/`pathconf`** — return compile-time constants (_SC_CLK_TCK, _SC_PAGE_SIZE, etc.) +7. **`tcgetpgrp`/`tcsetpgrp`** — ioctl wrappers +8. **`cfmakeraw`** — termios convenience function +9. **`getline`/`getdelim`** — dynamic line reading +10. **`popen`/`pclose`** — fork+exec+pipe +11. **`clearerr`/`ungetc`** — stdio functions + +### Phase 2: Additional Syscalls (1 day) + +1. **`getrusage`** — aggregate utime/stime into struct rusage +2. **`uname`** — return static struct utsname ("AdrOS", "adros", "1.0.0", "i686") +3. **`lstat`** — stat without following symlinks +4. **`fchmod`/`fchown`** — by file descriptor +5. **`ioctl FIONREAD`** — return bytes available in pipe/tty/socket buffer +6. **`wait4`** — waitpid + rusage + +### Phase 3: Regex Engine (2-3 days) + +Bash needs `` for `[[ string =~ pattern ]]`. Options: +- **Port TRE** (lightweight POSIX regex, ~5000 lines) — recommended +- **Port musl regex** — smaller but less featureful +- **Write minimal engine** — only ERE subset needed by Bash + +### Phase 4: Test Bash on AdrOS (1 day) + +1. Rebuild Bash with fixed libgloss stubs +2. Package `bash` binary into AdrOS initrd +3. Boot with `init=/bin/bash` +4. Test: command execution, pipes, redirects, job control, `$PATH`, wildcards, `~` expansion +5. Fix issues iteratively + +### Phase 5: Cross-compile Busybox (1 week) + +1. Configure Busybox with minimal config (disable applets that need missing APIs) +2. Cross-compile with `i686-adros-gcc` +3. Fix link errors iteratively (add missing stubs/implementations) +4. Package in initrd — replaces individual `/bin/*` utilities +5. Key applets to target first: sh, ls, cat, cp, mv, rm, mkdir, rmdir, mount, umount, ps, grep, sed, awk, find, xargs, tar + +### Phase 6: Cross-compile Native Binutils + GCC (2-3 weeks) + +This is the final step to self-hosting: + +1. **Native Binutils** — cross-compile binutils with `--host=i686-adros --target=i686-adros` +2. **Native GCC (stage 1)** — cross-compile GCC C-only with `--host=i686-adros --target=i686-adros` +3. **Package on ext2 disk image** — toolchain binaries + headers + libraries +4. **Test on AdrOS** — boot, mount ext2, run `gcc -o hello hello.c` +5. **Bootstrap (stage 2)** — use native GCC to rebuild itself on AdrOS + +**Prerequisites for Phase 6:** +- Working Bash (for configure scripts) +- Working make (needs fork/exec/waitpid/pipes — all available after Phase 0) +- Working sed, grep, awk (Busybox or standalone) +- At least 64MB RAM available to processes +- /tmp with enough space for GCC temporaries + +--- + +## Part 5: Effort Estimate Summary + +| Phase | Description | Effort | Dependency | +|-------|-------------|--------|------------| +| **Phase 0** | Fix libgloss posix_stubs.c | 1-2 days | None | +| **Phase 1** | Critical ulibc gaps | 2-3 days | None (parallel) | +| **Phase 2** | Additional syscalls | 1 day | None (parallel) | +| **Phase 3** | Regex engine | 2-3 days | Phase 1 | +| **Phase 4** | Test Bash on AdrOS | 1 day | Phase 0 | +| **Phase 5** | Busybox cross-compile | 1 week | Phase 0, 2, 3 | +| **Phase 6** | Native GCC/Binutils | 2-3 weeks | Phase 4, 5 | + +**Total to Bash running:** ~1 week +**Total to Busybox running:** ~2-3 weeks +**Total to self-hosting GCC:** ~1-2 months + +--- + +## Part 6: Current Score Assessment + +| Category | Audit Claim | Actual (verified) | +|----------|-------------|-------------------| +| Kernel syscalls | ~85% POSIX | **~92%** — 135 syscall numbers, missing ~8 | +| ulibc coverage | "NOT sufficient" | **Much improved** — 25 headers, proper malloc, but still missing regex/glob/network headers | +| Cross-toolchain | Not started | **✅ COMPLETE** — GCC 13.2.0 + Newlib + Binutils | +| Bash cross-compiled | Not started | **✅ DONE** — static ELF, needs runtime stubs | +| Bash running on AdrOS | ❌ | ❌ — libgloss stubs return ENOSYS | +| Busybox | Not started | ❌ | +| Native GCC | Not started | ❌ | + +**Bottom line:** The kernel is ~92% POSIX-ready. The cross-toolchain is complete. The #1 blocker to running Bash natively is **converting the ~30 ENOSYS stubs in libgloss/posix_stubs.c to real AdrOS syscall wrappers** — the kernel already supports every required operation. This is a straightforward 1-2 day task that unblocks everything else. diff --git a/newlib/libgloss/adros/posix_stubs.c b/newlib/libgloss/adros/posix_stubs.c index c1c4e9d..f8c3f32 100644 --- a/newlib/libgloss/adros/posix_stubs.c +++ b/newlib/libgloss/adros/posix_stubs.c @@ -1,4 +1,15 @@ -/* posix_stubs.c — POSIX stubs for Bash cross-compilation on AdrOS/newlib */ +/* + * posix_stubs.c — POSIX syscall wrappers for AdrOS/newlib (i686) + * + * These functions provide the POSIX API layer on top of AdrOS kernel syscalls. + * Each function issues an AdrOS syscall via INT 0x80. + * + * AdrOS syscall convention (i386): + * EAX = syscall number + * EBX = arg1, ECX = arg2, EDX = arg3, ESI = arg4, EDI = arg5 + * Return value in EAX (negative = -errno) + */ + #include #include #include @@ -10,125 +21,437 @@ #include #include #include +#include +#include +#include +#include +#include +#include +#include -static int nosys(void) { errno = ENOSYS; return -1; } +/* ---- AdrOS syscall numbers (must match include/syscall.h) ---- */ +#define SYS_WAITPID 7 +#define SYS_DUP 12 +#define SYS_DUP2 13 +#define SYS_PIPE 14 +#define SYS_GETPPID 17 +#define SYS_POLL 18 +#define SYS_SELECT 20 +#define SYS_IOCTL 21 +#define SYS_SETSID 22 +#define SYS_SETPGID 23 +#define SYS_GETPGRP 24 +#define SYS_SIGACTION 25 +#define SYS_SIGPROCMASK 26 +#define SYS_GETDENTS 30 +#define SYS_FCNTL 31 +#define SYS_CHDIR 32 +#define SYS_GETCWD 33 +#define SYS_NANOSLEEP 42 +#define SYS_CHMOD 50 +#define SYS_CHOWN 51 +#define SYS_GETUID 52 +#define SYS_GETGID 53 +#define SYS_ACCESS 74 +#define SYS_UMASK 75 +#define SYS_SETUID 76 +#define SYS_SETGID 77 +#define SYS_ALARM 83 +#define SYS_GETEUID 88 +#define SYS_GETEGID 89 +#define SYS_SETEUID 90 +#define SYS_SETEGID 91 -/* File descriptor ops */ -int dup(int fd) { (void)fd; return nosys(); } -int dup2(int o, int n) { (void)o; (void)n; return nosys(); } -int fcntl(int fd, int c, ...) { (void)fd; (void)c; return nosys(); } -int pipe(int p[2]) { (void)p; return nosys(); } +/* ---- Raw syscall helpers ---- */ -/* File ops */ -int chown(const char *p, uid_t o, gid_t g) { (void)p; (void)o; (void)g; return nosys(); } -int lstat(const char *p, struct stat *s) { return stat(p, s); } +static inline int _sc0(int nr) { + int ret; + __asm__ volatile("int $0x80" : "=a"(ret) : "a"(nr) : "memory"); + return ret; +} -/* Process/signal */ -int killpg(int pg, int s) { return kill(-pg, s); } -int setpgid(pid_t p, pid_t g) { (void)p; (void)g; return nosys(); } -pid_t setsid(void) { return (pid_t)nosys(); } -pid_t getpgrp(void) { return getpid(); } -int sigaction(int sig, const struct sigaction *a, struct sigaction *o) { - (void)sig; (void)a; (void)o; return nosys(); +static inline int _sc1(int nr, int a1) { + int ret; + __asm__ volatile("int $0x80" : "=a"(ret) : "a"(nr), "b"(a1) : "memory"); + return ret; } -int sigprocmask(int h, const sigset_t *s, sigset_t *o) { - (void)h; (void)s; (void)o; return 0; + +static inline int _sc2(int nr, int a1, int a2) { + int ret; + __asm__ volatile("int $0x80" : "=a"(ret) : "a"(nr), "b"(a1), "c"(a2) : "memory"); + return ret; } -/* Terminal */ -int tcgetattr(int fd, struct termios *t) { (void)fd; (void)t; return nosys(); } -int tcsetattr(int fd, int a, const struct termios *t) { (void)fd; (void)a; (void)t; return nosys(); } -pid_t tcgetpgrp(int fd) { (void)fd; return (pid_t)nosys(); } -int tcsetpgrp(int fd, pid_t pg) { (void)fd; (void)pg; return nosys(); } +static inline int _sc3(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; +} -/* I/O multiplexing */ -int select(int n, fd_set *r, fd_set *w, fd_set *e, struct timeval *t) { - (void)n; (void)r; (void)w; (void)e; (void)t; return nosys(); +static inline int _sc4(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; } -/* Misc */ -char *getcwd(char *b, size_t sz) { - if (b && sz > 1) { b[0] = '/'; b[1] = 0; return b; } - errno = ERANGE; return 0; +static inline int _sc5(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; } -int gethostname(char *n, size_t l) { - if (n && l > 5) { strcpy(n, "adros"); return 0; } - errno = ENAMETOOLONG; return -1; + +/* Convert negative syscall return to errno + return -1 */ +static inline int _check(int r) { + if (r < 0) { errno = -r; return -1; } + return r; } -int ioctl(int fd, unsigned long r, ...) { (void)fd; (void)r; return nosys(); } -/* pwd stubs */ -static struct passwd _pw = {"root", "", 0, 0, "", "/", "/bin/sh"}; -struct passwd *getpwuid(uid_t u) { (void)u; return &_pw; } -struct passwd *getpwnam(const char *n) { (void)n; return &_pw; } +/* ---- TTY ioctl numbers (must match kernel tty.c) ---- */ +#define TTY_TCGETS 0x5401 +#define TTY_TCSETS 0x5402 +#define TTY_TCSETSW 0x5403 +#define TTY_TCSETSF 0x5404 +#define TTY_TIOCGPGRP 0x540F +#define TTY_TIOCSPGRP 0x5410 +#define TTY_TIOCGWINSZ 0x5413 -/* Additional stubs */ -int mkfifo(const char *p, mode_t m) { (void)p; (void)m; errno = ENOSYS; return -1; } -int tcdrain(int fd) { (void)fd; return 0; } -int tcflow(int fd, int a) { (void)fd; (void)a; return 0; } -int tcflush(int fd, int q) { (void)fd; (void)q; return 0; } -int tcsendbreak(int fd, int d) { (void)fd; (void)d; return 0; } -speed_t cfgetispeed(const struct termios *t) { (void)t; return 0; } -speed_t cfgetospeed(const struct termios *t) { (void)t; return 0; } -int cfsetispeed(struct termios *t, speed_t s) { (void)t; (void)s; return 0; } -int cfsetospeed(struct termios *t, speed_t s) { (void)t; (void)s; return 0; } - -/* Directory stubs */ -DIR *opendir(const char *p) { (void)p; errno = ENOSYS; return 0; } -struct dirent *readdir(DIR *d) { (void)d; return 0; } -int closedir(DIR *d) { (void)d; return 0; } -void rewinddir(DIR *d) { (void)d; } - -/* pselect stub */ -int pselect(int n, fd_set *r, fd_set *w, fd_set *e, - const struct timespec *t, const sigset_t *sm) { - (void)n; (void)r; (void)w; (void)e; (void)t; (void)sm; - errno = ENOSYS; return -1; -} - -/* --- Link-time stubs for Bash --- */ -#include -#include -#include +/* ================================================================ + * File descriptor operations + * ================================================================ */ -int access(const char *p, int m) { (void)p; (void)m; return 0; } -unsigned alarm(unsigned s) { (void)s; return 0; } -int chdir(const char *p) { (void)p; errno = ENOSYS; return -1; } -int fchmod(int fd, mode_t m) { (void)fd; (void)m; errno = ENOSYS; return -1; } -uid_t getuid(void) { return 0; } -uid_t geteuid(void) { return 0; } -gid_t getegid(void) { return 0; } -pid_t getppid(void) { return 1; } -int setuid(uid_t u) { (void)u; return 0; } -int setreuid(uid_t r, uid_t e) { (void)r; (void)e; return 0; } -int setregid(gid_t r, gid_t e) { (void)r; (void)e; return 0; } -unsigned sleep(unsigned s) { (void)s; return 0; } -mode_t umask(mode_t m) { (void)m; return 022; } -char *ttyname(int fd) { (void)fd; return "/dev/tty"; } -pid_t waitpid(pid_t p, int *s, int o) { (void)p; (void)s; (void)o; errno = ECHILD; return -1; } -void setgrent(void) {} -void endgrent(void) {} +int dup(int fd) { + return _check(_sc1(SYS_DUP, fd)); +} -int fnmatch(const char *pat, const char *str, int flags) { - (void)flags; - /* Trivial: only support exact match */ - return strcmp(pat, str) == 0 ? 0 : 1; +int dup2(int oldfd, int newfd) { + return _check(_sc2(SYS_DUP2, oldfd, newfd)); +} + +int fcntl(int fd, int cmd, ...) { + va_list ap; + va_start(ap, cmd); + int arg = va_arg(ap, int); + va_end(ap); + return _check(_sc3(SYS_FCNTL, fd, cmd, arg)); +} + +int pipe(int fds[2]) { + return _check(_sc1(SYS_PIPE, (int)fds)); } -int regcomp(regex_t *r, const char *p, int f) { (void)r; (void)p; (void)f; return -1; } -int regexec(const regex_t *r, const char *s, size_t n, regmatch_t pm[], int f) { - (void)r; (void)s; (void)n; (void)pm; (void)f; return -1; +/* ================================================================ + * File operations + * ================================================================ */ + +int chown(const char *path, uid_t owner, gid_t group) { + return _check(_sc3(SYS_CHOWN, (int)path, (int)owner, (int)group)); } -void regfree(regex_t *r) { (void)r; } -size_t regerror(int e, const regex_t *r, char *buf, size_t sz) { - (void)e; (void)r; - if (buf && sz > 0) { buf[0] = 0; } + +int chmod(const char *path, mode_t mode) { + return _check(_sc2(SYS_CHMOD, (int)path, (int)mode)); +} + +int lstat(const char *path, struct stat *st) { + /* AdrOS has no separate lstat syscall yet — fall back to stat */ + return stat(path, st); +} + +int access(const char *path, int mode) { + return _check(_sc2(SYS_ACCESS, (int)path, mode)); +} + +int chdir(const char *path) { + return _check(_sc1(SYS_CHDIR, (int)path)); +} + +char *getcwd(char *buf, size_t size) { + int r = _sc2(SYS_GETCWD, (int)buf, (int)size); + if (r < 0) { errno = -r; return 0; } + return buf; +} + +int fchmod(int fd, mode_t mode) { + /* No fchmod syscall yet — return ENOSYS */ + (void)fd; (void)mode; + errno = ENOSYS; + return -1; +} + +/* ================================================================ + * Process/signal operations + * ================================================================ */ + +pid_t waitpid(pid_t pid, int *status, int options) { + return (pid_t)_check(_sc3(SYS_WAITPID, (int)pid, (int)status, options)); +} + +int setpgid(pid_t pid, pid_t pgid) { + return _check(_sc2(SYS_SETPGID, (int)pid, (int)pgid)); +} + +pid_t setsid(void) { + return (pid_t)_check(_sc0(SYS_SETSID)); +} + +pid_t getpgrp(void) { + return (pid_t)_sc0(SYS_GETPGRP); +} + +int killpg(int pgrp, int sig) { + return kill(-pgrp, sig); +} + +pid_t getppid(void) { + return (pid_t)_sc0(SYS_GETPPID); +} + +uid_t getuid(void) { return (uid_t)_sc0(SYS_GETUID); } +uid_t geteuid(void) { return (uid_t)_sc0(SYS_GETEUID); } +gid_t getgid(void) { return (gid_t)_sc0(SYS_GETGID); } +gid_t getegid(void) { return (gid_t)_sc0(SYS_GETEGID); } + +int setuid(uid_t uid) { return _check(_sc1(SYS_SETUID, (int)uid)); } +int setgid(gid_t gid) { return _check(_sc1(SYS_SETGID, (int)gid)); } + +int setreuid(uid_t ruid, uid_t euid) { + /* AdrOS has setuid/seteuid but no setreuid — approximate */ + if (ruid != (uid_t)-1) { int r = _check(_sc1(SYS_SETUID, (int)ruid)); if (r < 0) return r; } + if (euid != (uid_t)-1) { int r = _check(_sc1(SYS_SETEUID, (int)euid)); if (r < 0) return r; } + return 0; +} + +int setregid(gid_t rgid, gid_t egid) { + if (rgid != (gid_t)-1) { int r = _check(_sc1(SYS_SETGID, (int)rgid)); if (r < 0) return r; } + if (egid != (gid_t)-1) { int r = _check(_sc1(SYS_SETEGID, (int)egid)); if (r < 0) return r; } return 0; } -/* Group/user stubs */ -gid_t getgid(void) { return 0; } -int setgid(gid_t g) { (void)g; return 0; } +mode_t umask(mode_t mask) { + return (mode_t)_sc1(SYS_UMASK, (int)mask); +} + +unsigned alarm(unsigned seconds) { + return (unsigned)_sc1(SYS_ALARM, (int)seconds); +} + +unsigned sleep(unsigned seconds) { + struct timespec ts = { .tv_sec = seconds, .tv_nsec = 0 }; + struct timespec rem = { 0, 0 }; + int r = _sc2(SYS_NANOSLEEP, (int)&ts, (int)&rem); + if (r < 0) return (unsigned)rem.tv_sec; + return 0; +} + +/* ================================================================ + * Signal operations + * ================================================================ */ + +int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact) { + return _check(_sc3(SYS_SIGACTION, signum, (int)act, (int)oldact)); +} + +int sigprocmask(int how, const sigset_t *set, sigset_t *oldset) { + return _check(_sc3(SYS_SIGPROCMASK, how, (int)set, (int)oldset)); +} + +/* ================================================================ + * Terminal operations + * ================================================================ */ + +int tcgetattr(int fd, struct termios *t) { + return _check(_sc3(SYS_IOCTL, fd, TTY_TCGETS, (int)t)); +} + +int tcsetattr(int fd, int optional_actions, const struct termios *t) { + int cmd; + switch (optional_actions) { + case TCSADRAIN: cmd = TTY_TCSETSW; break; + case TCSAFLUSH: cmd = TTY_TCSETSF; break; + default: cmd = TTY_TCSETS; break; + } + return _check(_sc3(SYS_IOCTL, fd, cmd, (int)t)); +} + +pid_t tcgetpgrp(int fd) { + pid_t pgrp = 0; + int r = _sc3(SYS_IOCTL, fd, TTY_TIOCGPGRP, (int)&pgrp); + if (r < 0) { errno = -r; return (pid_t)-1; } + return pgrp; +} + +int tcsetpgrp(int fd, pid_t pgrp) { + return _check(_sc3(SYS_IOCTL, fd, TTY_TIOCSPGRP, (int)&pgrp)); +} + +int tcdrain(int fd) { (void)fd; return 0; } +int tcflow(int fd, int action) { (void)fd; (void)action; return 0; } +int tcflush(int fd, int queue_selector) { (void)fd; (void)queue_selector; return 0; } +int tcsendbreak(int fd, int duration) { (void)fd; (void)duration; return 0; } + +speed_t cfgetispeed(const struct termios *t) { (void)t; return B9600; } +speed_t cfgetospeed(const struct termios *t) { (void)t; return B9600; } +int cfsetispeed(struct termios *t, speed_t speed) { (void)t; (void)speed; return 0; } +int cfsetospeed(struct termios *t, speed_t speed) { (void)t; (void)speed; return 0; } + +char *ttyname(int fd) { + /* Minimal: check if fd is a tty, return generic name */ + int r = _sc3(SYS_IOCTL, fd, TTY_TIOCGPGRP, 0); + if (r < 0) { errno = ENOTTY; return 0; } + return "/dev/tty"; +} + +int ioctl(int fd, unsigned long request, ...) { + va_list ap; + va_start(ap, request); + void *arg = va_arg(ap, void *); + va_end(ap); + return _check(_sc3(SYS_IOCTL, fd, (int)request, (int)arg)); +} + +/* ================================================================ + * I/O multiplexing + * ================================================================ */ + +int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, + struct timeval *timeout) { + return _check(_sc5(SYS_SELECT, nfds, (int)readfds, (int)writefds, + (int)exceptfds, (int)timeout)); +} + +int pselect(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, + const struct timespec *timeout, const sigset_t *sigmask) { + /* Approximate pselect with select (ignore sigmask) */ + struct timeval tv, *tvp = 0; + (void)sigmask; + if (timeout) { + tv.tv_sec = timeout->tv_sec; + tv.tv_usec = timeout->tv_nsec / 1000; + tvp = &tv; + } + return select(nfds, readfds, writefds, exceptfds, tvp); +} + +/* ================================================================ + * Directory operations (wrap getdents syscall) + * ================================================================ */ + +/* AdrOS getdents returns fixed-size entries: { uint32_t ino; char name[256]; } */ +#define ADROS_DIRENT_SIZE 260 + +DIR *opendir(const char *name) { + int fd = open(name, 0 /* O_RDONLY */, 0); + if (fd < 0) return 0; + DIR *d = (DIR *)malloc(sizeof(DIR)); + if (!d) { close(fd); errno = ENOMEM; return 0; } + d->dd_fd = fd; + d->dd_loc = 0; + d->dd_size = 0; + d->dd_buf = (char *)malloc(4096); + if (!d->dd_buf) { close(fd); free(d); errno = ENOMEM; return 0; } + return d; +} + +static struct dirent _de_ret; + +struct dirent *readdir(DIR *d) { + if (!d) return 0; + /* Refill buffer if exhausted */ + if (d->dd_loc >= d->dd_size) { + int n = _check(_sc3(SYS_GETDENTS, d->dd_fd, (int)d->dd_buf, 4096)); + if (n <= 0) return 0; + d->dd_size = n; + d->dd_loc = 0; + } + if (d->dd_loc + ADROS_DIRENT_SIZE > d->dd_size) return 0; + char *ent = d->dd_buf + d->dd_loc; + uint32_t ino; + memcpy(&ino, ent, 4); + _de_ret.d_ino = (ino_t)ino; + strncpy(_de_ret.d_name, ent + 4, MAXNAMLEN); + _de_ret.d_name[MAXNAMLEN] = '\0'; + d->dd_loc += ADROS_DIRENT_SIZE; + return &_de_ret; +} + +int closedir(DIR *d) { + if (!d) return -1; + int r = close(d->dd_fd); + free(d->dd_buf); + free(d); + return r; +} + +void rewinddir(DIR *d) { + if (!d) return; + lseek(d->dd_fd, 0, 0); + d->dd_loc = 0; + d->dd_size = 0; +} + +/* ================================================================ + * Misc + * ================================================================ */ + +int gethostname(char *name, size_t len) { + if (name && len > 5) { strcpy(name, "adros"); return 0; } + errno = ENAMETOOLONG; return -1; +} + +int mkfifo(const char *path, mode_t mode) { + (void)path; (void)mode; + errno = ENOSYS; + return -1; +} + +/* pwd stubs — return hardcoded root user */ +static struct passwd _pw = {"root", "", 0, 0, "", "/", "/bin/sh"}; +struct passwd *getpwuid(uid_t uid) { (void)uid; return &_pw; } +struct passwd *getpwnam(const char *name) { (void)name; return &_pw; } + +/* Group stubs */ struct group { char *gr_name; char *gr_passwd; gid_t gr_gid; char **gr_mem; }; static struct group _gr = {"root", "", 0, 0}; struct group *getgrent(void) { return &_gr; } +void setgrent(void) {} +void endgrent(void) {} + +/* ================================================================ + * Regex stubs (kept as stubs — real regex in Phase 3) + * ================================================================ */ + +int regcomp(regex_t *preg, const char *pattern, int cflags) { + (void)preg; (void)pattern; (void)cflags; + return -1; +} +int regexec(const regex_t *preg, const char *string, size_t nmatch, + regmatch_t pmatch[], int eflags) { + (void)preg; (void)string; (void)nmatch; (void)pmatch; (void)eflags; + return -1; +} +void regfree(regex_t *preg) { (void)preg; } +size_t regerror(int errcode, const regex_t *preg, char *errbuf, size_t errbuf_size) { + (void)errcode; (void)preg; + if (errbuf && errbuf_size > 0) errbuf[0] = '\0'; + return 0; +} + +/* fnmatch — basic wildcard matching (* and ? support) */ +int fnmatch(const char *pattern, const char *string, int flags) { + (void)flags; + const char *p = pattern, *s = string; + const char *star_p = 0, *star_s = 0; + while (*s) { + if (*p == '*') { + star_p = ++p; + star_s = s; + continue; + } + if (*p == '?' || *p == *s) { p++; s++; continue; } + if (star_p) { p = star_p; s = ++star_s; continue; } + return 1; /* FNM_NOMATCH */ + } + while (*p == '*') p++; + return *p ? 1 : 0; +}