]> Projects (at) Tadryanom (dot) Me - AdrOS.git/commitdiff
feat: Phase 0 — convert libgloss posix_stubs.c from ENOSYS to real AdrOS syscalls
authorTulio A M Mendes <[email protected]>
Sat, 14 Mar 2026 07:34:43 +0000 (04:34 -0300)
committerTulio A M Mendes <[email protected]>
Sat, 14 Mar 2026 07:34:43 +0000 (04:34 -0300)
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

docs/SELF_HOSTING_ANALYSIS.md [new file with mode: 0644]
newlib/libgloss/adros/posix_stubs.c

diff --git a/docs/SELF_HOSTING_ANALYSIS.md b/docs/SELF_HOSTING_ANALYSIS.md
new file mode 100644 (file)
index 0000000..bd238d1
--- /dev/null
@@ -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 |
+| `<setjmp.h>` | ✅ | i386 asm: `setjmp.S` (setjmp/longjmp/sigsetjmp/siglongjmp) |
+| `<locale.h>` | ✅ | C locale stubs: `locale.c` |
+| `<pwd.h>` / `<grp.h>` | ✅ | `pwd_grp.c` with getpwnam/getpwuid/getpwent/getgrnam/getgrgid/getgrent |
+| `<fnmatch.h>` | ✅ | `fnmatch.c` with pattern matching |
+| `<getopt.h>` | ✅ | `getopt.c` with getopt/getopt_long |
+| `<sys/select.h>` | ✅ | FD_SET/FD_CLR/FD_ISSET/FD_ZERO macros |
+| `<sys/resource.h>` | ✅ | 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 |
+|--------|----------|-----------|--------|
+| `<regex.h>` | **Critical** | Bash `[[ =~ ]]`, grep, sed | Large — need regex engine |
+| `<glob.h>` | **Critical** | Bash wildcard expansion | Medium — glob/globfree |
+| `<sys/utsname.h>` | High | autoconf, uname command, Bash | Small — struct + uname() |
+| `<poll.h>` | High | Many programs | Small — poll/struct pollfd |
+| `<libgen.h>` | Medium | POSIX basename/dirname | Small |
+| `<netdb.h>` | Medium | Network programs | Medium |
+| `<netinet/in.h>` | Medium | Network programs | Small |
+| `<arpa/inet.h>` | Medium | Network programs | Small |
+| `<sys/socket.h>` | Medium | Network programs | Medium |
+| `<dlfcn.h>` | Low | GCC plugins, shared libs | Small |
+| `<spawn.h>` | Low | Some programs | Small |
+| `<wordexp.h>` | Low | Bash | Medium |
+| `<sys/un.h>` | Low | Unix domain sockets | Small |
+| `<syslog.h>` | Low | Daemons | Small (stubs) |
+
+### 2C. Missing Functions in EXISTING ulibc Headers
+
+#### `<stdio.h>` — 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 |
+
+#### `<unistd.h>` — 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 |
+
+#### `<signal.h>` — Missing:
+| Function | Priority | Effort |
+|----------|----------|--------|
+| `sigemptyset`/`sigfillset`/`sigaddset`/`sigdelset`/`sigismember` | **Critical** | Trivial — macros on uint32_t |
+
+#### `<pthread.h>` — 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 |
+
+#### `<termios.h>` — 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. **`<sys/utsname.h>`** + `uname` syscall — struct utsname with sysname/nodename/release/version/machine
+3. **`<poll.h>`** — struct pollfd + poll() wrapper (syscall exists)
+4. **`<glob.h>`** — 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 `<regex.h>` 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.
index c1c4e9dff1d69447c4b0df83ab9aa48a7e5bf8fc..f8c3f32ecab03823afc9368cfe3f7afa90bfd1e2 100644 (file)
@@ -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 <errno.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <pwd.h>
 #include <sys/ioctl.h>
 #include <dirent.h>
+#include <stdint.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <regex.h>
+#include <fnmatch.h>
+#include <time.h>
 
-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 <regex.h>
-#include <fnmatch.h>
-#include <time.h>
+/* ================================================================
+ * 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;
+}