Skip to main content

musl_compat/
lib.rs

1#![no_std]
2
3use strat9_abi::{
4    flag::{posix_oflags_to_strat9, OpenFlags},
5    syscall::*,
6};
7use strat9_syscall::{
8    error::Error, syscall0, syscall1, syscall2, syscall3, syscall4, syscall5, syscall6,
9};
10
11// ── raw syscall helpers (return i64 for musl C ABI) ────────────────────
12
13#[inline(always)]
14unsafe fn raw0(nr: usize) -> i64 {
15    match syscall0(nr) {
16        Ok(v) => v as i64,
17        Err(e) => -(e.to_errno() as i64),
18    }
19}
20
21#[inline(always)]
22unsafe fn raw1(nr: usize, a1: usize) -> i64 {
23    match syscall1(nr, a1) {
24        Ok(v) => v as i64,
25        Err(e) => -(e.to_errno() as i64),
26    }
27}
28
29#[inline(always)]
30unsafe fn raw2(nr: usize, a1: usize, a2: usize) -> i64 {
31    match syscall2(nr, a1, a2) {
32        Ok(v) => v as i64,
33        Err(e) => -(e.to_errno() as i64),
34    }
35}
36
37#[inline(always)]
38unsafe fn raw3(nr: usize, a1: usize, a2: usize, a3: usize) -> i64 {
39    match syscall3(nr, a1, a2, a3) {
40        Ok(v) => v as i64,
41        Err(e) => -(e.to_errno() as i64),
42    }
43}
44
45#[inline(always)]
46unsafe fn raw4(nr: usize, a1: usize, a2: usize, a3: usize, a4: usize) -> i64 {
47    match syscall4(nr, a1, a2, a3, a4) {
48        Ok(v) => v as i64,
49        Err(e) => -(e.to_errno() as i64),
50    }
51}
52
53#[inline(always)]
54unsafe fn raw5(nr: usize, a1: usize, a2: usize, a3: usize, a4: usize, a5: usize) -> i64 {
55    match syscall5(nr, a1, a2, a3, a4, a5) {
56        Ok(v) => v as i64,
57        Err(e) => -(e.to_errno() as i64),
58    }
59}
60
61#[inline(always)]
62unsafe fn raw6(nr: usize, a1: usize, a2: usize, a3: usize, a4: usize, a5: usize, a6: usize) -> i64 {
63    match syscall6(nr, a1, a2, a3, a4, a5, a6) {
64        Ok(v) => v as i64,
65        Err(e) => -(e.to_errno() as i64),
66    }
67}
68
69// === Linux futex operation constants ==================================
70
71const FUTEX_WAIT: usize = 0;
72const FUTEX_WAKE: usize = 1;
73const FUTEX_REQUEUE: usize = 3;
74const FUTEX_CMP_REQUEUE: usize = 4;
75const FUTEX_WAKE_OP: usize = 4; // same value, different semantics
76const FUTEX_PRIVATE_FLAG: usize = 128;
77
78// === arch_prctl sub-commands
79
80const ARCH_SET_FS: usize = 0x1002;
81const ARCH_GET_FS: usize = 0x1003;
82
83// === Linux syscall numbers used by musl internally ==================================
84// See sdk/musl/arch/x86_64/bits/syscall.h.in for the full list.
85// We define the ones we actually handle; the rest fall through to the
86// default -ENOSYS branch.
87
88const LNR_read: i64 = 0;
89const LNR_write: i64 = 1;
90const LNR_open: i64 = 2;
91const LNR_close: i64 = 3;
92const LNR_stat: i64 = 4;
93const LNR_fstat: i64 = 5;
94const LNR_lstat: i64 = 6;
95const LNR_poll: i64 = 7;
96const LNR_lseek: i64 = 8;
97const LNR_mmap: i64 = 9;
98const LNR_mprotect: i64 = 10;
99const LNR_munmap: i64 = 11;
100const LNR_brk: i64 = 12;
101const LNR_rt_sigaction: i64 = 13;
102const LNR_rt_sigprocmask: i64 = 14;
103const LNR_rt_sigreturn: i64 = 15;
104const LNR_ioctl: i64 = 16;
105const LNR_pread64: i64 = 17;
106const LNR_pwrite64: i64 = 18;
107const LNR_access: i64 = 21;
108const LNR_pipe: i64 = 22;
109const LNR_sched_yield: i64 = 24;
110const LNR_mremap: i64 = 25;
111const LNR_dup: i64 = 32;
112const LNR_dup2: i64 = 33;
113const LNR_nanosleep: i64 = 35;
114const LNR_getitimer: i64 = 36;
115const LNR_setitimer: i64 = 38;
116const LNR_getpid: i64 = 39;
117const LNR_clone: i64 = 56;
118const LNR_fork: i64 = 57;
119const LNR_execve: i64 = 59;
120const LNR_exit: i64 = 60;
121const LNR_wait4: i64 = 61;
122const LNR_kill: i64 = 62;
123const LNR_uname: i64 = 63;
124const LNR_fcntl: i64 = 72;
125const LNR_truncate: i64 = 76;
126const LNR_ftruncate: i64 = 77;
127const LNR_getdents: i64 = 78;
128const LNR_getcwd: i64 = 79;
129const LNR_chdir: i64 = 80;
130const LNR_fchdir: i64 = 81;
131const LNR_rename: i64 = 82;
132const LNR_mkdir: i64 = 83;
133const LNR_rmdir: i64 = 84;
134const LNR_link: i64 = 86;
135const LNR_unlink: i64 = 87;
136const LNR_symlink: i64 = 88;
137const LNR_readlink: i64 = 89;
138const LNR_chmod: i64 = 90;
139const LNR_fchmod: i64 = 91;
140const LNR_umask: i64 = 95;
141const LNR_getuid: i64 = 102;
142const LNR_getgid: i64 = 104;
143const LNR_setuid: i64 = 105;
144const LNR_setgid: i64 = 106;
145const LNR_geteuid: i64 = 107;
146const LNR_getegid: i64 = 108;
147const LNR_setpgid: i64 = 109;
148const LNR_getppid: i64 = 110;
149const LNR_getpgrp: i64 = 111;
150const LNR_setsid: i64 = 112;
151const LNR_getpgid: i64 = 121;
152const LNR_getsid: i64 = 124;
153const LNR_rt_sigpending: i64 = 127;
154const LNR_rt_sigtimedwait: i64 = 128;
155const LNR_rt_sigsuspend: i64 = 130;
156const LNR_sigaltstack: i64 = 131;
157const LNR_pause: i64 = 34;
158const LNR_socket: i64 = 41;
159const LNR_connect: i64 = 42;
160const LNR_accept: i64 = 43;
161const LNR_sendto: i64 = 44;
162const LNR_recvfrom: i64 = 45;
163const LNR_sendmsg: i64 = 46;
164const LNR_recvmsg: i64 = 47;
165const LNR_shutdown: i64 = 48;
166const LNR_bind: i64 = 49;
167const LNR_listen: i64 = 50;
168const LNR_getsockname: i64 = 51;
169const LNR_getpeername: i64 = 52;
170const LNR_socketpair: i64 = 53;
171const LNR_setsockopt: i64 = 54;
172const LNR_getsockopt: i64 = 55;
173const LNR_gettid: i64 = 186;
174const LNR_tkill: i64 = 200;
175const LNR_futex: i64 = 202;
176const LNR_set_tid_address: i64 = 218;
177const LNR_clock_gettime: i64 = 228;
178const LNR_clock_getres: i64 = 229;
179const LNR_clock_nanosleep: i64 = 230;
180const LNR_exit_group: i64 = 231;
181const LNR_tgkill: i64 = 234;
182const LNR_openat: i64 = 257;
183const LNR_mkdirat: i64 = 258;
184const LNR_newfstatat: i64 = 262;
185const LNR_unlinkat: i64 = 263;
186const LNR_renameat: i64 = 264;
187const LNR_readlinkat: i64 = 267;
188const LNR_ppoll: i64 = 271;
189const LNR_set_robust_list: i64 = 273;
190const LNR_get_robust_list: i64 = 274;
191const LNR_arch_prctl: i64 = 158;
192const LNR_getrandom: i64 = 318;
193
194// === dispatcher ============================================================
195
196/// Main entry point called from musl's `__syscallN` stubs.
197///
198/// Translates Linux syscall numbers to Strat9 native syscall numbers,
199/// performs any necessary argument conversions, and returns the result
200/// as a raw i64 (positive = success, negative = -errno).
201///
202/// # Safety
203/// Called from musl C code. All pointer arguments are passed through
204/// unchanged.
205#[no_mangle]
206pub unsafe extern "C" fn strat9_syscall_dispatcher(
207    n: i64,
208    a1: i64,
209    a2: i64,
210    a3: i64,
211    a4: i64,
212    a5: i64,
213    a6: i64,
214) -> i64 {
215    let a1u = a1 as usize;
216    let a2u = a2 as usize;
217    let a3u = a3 as usize;
218    let a4u = a4 as usize;
219    let a5u = a5 as usize;
220    let a6u = a6 as usize;
221
222    match n {
223        // ── File I/O ───────────────────────────────────────────────
224        LNR_read => raw3(SYS_READ, a1u, a2u, a3u),
225        LNR_write => raw3(SYS_WRITE, a1u, a2u, a3u),
226        LNR_open => {
227            // Linux open(path, flags, mode) → Strat9 open(path, flags, mode)
228            // Convert POSIX O_* flags to Strat9 OpenFlags
229            let sf = posix_oflags_to_strat9(a2 as u32);
230            raw3(SYS_OPEN, a1u, sf.bits() as usize, a3u)
231        }
232        LNR_close => raw1(SYS_CLOSE, a1u),
233        LNR_lseek => raw3(SYS_LSEEK, a1u, a2u, a3u),
234        LNR_pread64 => raw4(SYS_PREAD, a1u, a2u, a3u, a4u),
235        LNR_pwrite64 => raw4(SYS_PWRITE, a1u, a2u, a3u, a4u),
236        LNR_fstat => raw2(SYS_FSTAT, a1u, a2u),
237        LNR_stat => raw2(SYS_STAT, a1u, a2u),
238        LNR_lstat => raw2(SYS_STAT, a1u, a2u), // no symlinks in Strat9
239        LNR_access => raw2(SYS_STAT, a1u, a2u), // TODO: proper access() syscall
240        LNR_pipe => raw1(SYS_PIPE, a1u),
241        LNR_dup => raw1(SYS_DUP, a1u),
242        LNR_dup2 => raw2(SYS_DUP2, a1u, a2u),
243        LNR_ioctl => raw3(SYS_IOCTL, a1u, a2u, a3u),
244        LNR_fcntl => raw3(SYS_FCNTL, a1u, a2u, a3u),
245        LNR_truncate => raw2(SYS_TRUNCATE, a1u, a2u),
246        LNR_ftruncate => raw2(SYS_FTRUNCATE, a1u, a2u),
247        LNR_getdents => raw3(SYS_GETDENTS, a1u, a2u, a3u),
248        LNR_poll => raw3(SYS_POLL, a1u, a2u, a3u),
249        LNR_ppoll => raw5(SYS_PPOLL, a1u, a2u, a3u, a4u, a5u),
250
251        // === *at() syscalls ================================================
252        LNR_openat => {
253            let sf = posix_oflags_to_strat9(a3 as u32);
254            raw4(SYS_OPENAT, a1u, a2u, sf.bits() as usize, a4u)
255        }
256        LNR_mkdirat => raw3(SYS_MKDIRAT, a1u, a2u, a3u),
257        LNR_newfstatat => raw4(SYS_FSTATAT, a1u, a2u, a3u, a4u),
258        LNR_unlinkat => raw3(SYS_UNLINKAT, a1u, a2u, a3u),
259        LNR_renameat => raw4(SYS_RENAMEAT, a1u, a2u, a3u, a4u),
260        LNR_readlinkat => raw4(SYS_READLINKAT, a1u, a2u, a3u, a4u),
261
262        // === Directory ops ================================================
263        LNR_getcwd => raw2(SYS_GETCWD, a1u, a2u),
264        LNR_chdir => raw1(SYS_CHDIR, a1u),
265        LNR_fchdir => raw1(SYS_FCHDIR, a1u),
266        LNR_rename => raw2(SYS_RENAME, a1u, a2u),
267        LNR_mkdir => raw2(SYS_MKDIR, a1u, a2u),
268        LNR_rmdir => raw1(SYS_RMDIR, a1u),
269        LNR_link => raw2(SYS_LINK, a1u, a2u),
270        LNR_unlink => raw1(SYS_UNLINK, a1u),
271        LNR_symlink => raw2(SYS_SYMLINK, a1u, a2u),
272        LNR_readlink => raw3(SYS_READLINK, a1u, a2u, a3u),
273        LNR_chmod => raw2(SYS_CHMOD, a1u, a2u),
274        LNR_fchmod => raw2(SYS_FCHMOD, a1u, a2u),
275        LNR_umask => raw1(SYS_UMASK, a1u),
276
277        // === Memory management ================================================
278        LNR_mmap => raw6(
279            SYS_MMAP, a1u, a2u, a3u, /* prot */
280            a4u, /* flags */
281            a5u, /* fd */
282            a6u, /* offset */
283        ),
284        LNR_munmap => raw2(SYS_MUNMAP, a1u, a2u),
285        LNR_mprotect => raw3(SYS_MPROTECT, a1u, a2u, a3u),
286        LNR_brk => raw1(SYS_BRK, a1u),
287        LNR_mremap => raw4(SYS_MREMAP, a1u, a2u, a3u, a4u),
288
289        // === Process / thread ================================================
290        LNR_exit => {
291            raw1(SYS_PROC_EXIT, a1u);
292            loop {}
293        }
294        LNR_exit_group => {
295            raw1(SYS_EXIT_GROUP, a1u);
296            loop {}
297        }
298        LNR_sched_yield => raw0(SYS_PROC_YIELD),
299        LNR_getpid => raw0(SYS_GETPID),
300        LNR_getppid => raw0(SYS_GETPPID),
301        LNR_gettid => raw0(SYS_GETTID),
302        LNR_getuid => raw0(SYS_GETUID),
303        LNR_geteuid => raw0(SYS_GETEUID),
304        LNR_getgid => raw0(SYS_GETGID),
305        LNR_getegid => raw0(SYS_GETEGID),
306        LNR_setuid => raw1(SYS_SETUID, a1u),
307        LNR_setgid => raw1(SYS_SETGID, a1u),
308        LNR_setpgid => raw2(SYS_SETPGID, a1u, a2u),
309        LNR_getpgid => raw1(SYS_GETPGID, a1u),
310        LNR_getpgrp => raw0(SYS_GETPGRP),
311        LNR_setsid => raw0(SYS_SETSID),
312        LNR_getsid => raw1(SYS_GETSID, a1u),
313        LNR_fork => raw0(SYS_PROC_FORK),
314        LNR_execve => raw3(SYS_PROC_EXECVE, a1u, a2u, a3u),
315        LNR_wait4 => raw4(SYS_PROC_WAITPID, a1u, a2u, a3u, a4u),
316        LNR_uname => raw1(SYS_UNAME, a1u),
317        LNR_pause => raw1(SYS_SIGSUSPEND, 0), // pause() ≈ sigsuspend(NULL)
318        LNR_kill => raw2(SYS_KILL, a1u, a2u),
319
320        // === Threading =====================================================
321        LNR_clone => {
322            // TODO: CLONE_ flags -> SYS_THREAD_CREATE / SYS_PROC_FORK mapping
323            // Linux clone(flags, stack, ptid, tls, ctid) has different
324            // semantics from Strat9 thread_create(entry, stack, arg, flags, tls).
325            // musl uses clone for both fork() and pthread_create().
326            -38 // ENOSYS
327        }
328        LNR_tkill => raw2(SYS_TGKILL, a1u, a2u),
329        LNR_tgkill => raw3(SYS_TGKILL, a1u, a2u, a3u),
330
331        // === Signals =======================================================
332        LNR_rt_sigaction => raw4(SYS_SIGACTION, a1u, a2u, a3u, a4u),
333        LNR_rt_sigprocmask => raw4(SYS_SIGPROCMASK, a1u, a2u, a3u, a4u),
334        LNR_rt_sigreturn => raw0(SYS_RT_SIGRETURN),
335        LNR_rt_sigpending => raw2(SYS_SIGPENDING, a1u, a2u),
336        LNR_rt_sigtimedwait => raw4(SYS_SIGTIMEDWAIT, a1u, a2u, a3u, a4u),
337        LNR_rt_sigsuspend => raw2(SYS_SIGSUSPEND, a1u, a2u),
338        LNR_sigaltstack => raw2(SYS_SIGALTSTACK, a1u, a2u),
339
340        // === Time ==========================================================
341        LNR_nanosleep => raw2(SYS_NANOSLEEP, a1u, a2u),
342        LNR_clock_gettime => raw2(SYS_CLOCK_GETTIME, a1u, a2u),
343        LNR_clock_getres => {
344            // clock_getres is almost never used; stub with success
345            0
346        }
347        LNR_clock_nanosleep => {
348            // TODO: implement clock_nanosleep in kernel
349            -38 // ENOSYS
350        }
351        LNR_getitimer => raw2(SYS_GETITIMER, a1u, a2u),
352        LNR_setitimer => raw3(SYS_SETITIMER, a1u, a2u, a3u),
353
354        // === Futex ==========================================================
355        LNR_futex => {
356            // Linux futex(uaddr, futex_op, val, timeout, uaddr2, val3)
357            let op = a2u & !FUTEX_PRIVATE_FLAG; // strip PRIVATE flag
358            match op {
359                FUTEX_WAIT => raw3(SYS_FUTEX_WAIT, a1u, a3u, a4u),
360                FUTEX_WAKE => raw2(SYS_FUTEX_WAKE, a1u, a3u),
361                FUTEX_REQUEUE => raw4(SYS_FUTEX_REQUEUE, a1u, a3u, a5u, a4u),
362                FUTEX_CMP_REQUEUE => raw5(SYS_FUTEX_CMP_REQUEUE, a1u, a3u, a5u, a6u, a4u),
363                _ => {
364                    // Unknown futex op
365                    -38 // ENOSYS
366                }
367            }
368        }
369
370        // === Networking (stubs — Strat9 uses IPC-based networking) ===
371        LNR_socket | LNR_connect | LNR_accept | LNR_sendto | LNR_recvfrom | LNR_sendmsg
372        | LNR_recvmsg | LNR_shutdown | LNR_bind | LNR_listen | LNR_getsockname
373        | LNR_getpeername | LNR_socketpair | LNR_setsockopt | LNR_getsockopt => {
374            // TODO: implement networking via Strat9 IPC net strate
375            -38 // ENOSYS
376        }
377
378        // === Linux-specific thread init (critical for musl) ===
379        LNR_set_tid_address => {
380            // TODO: kernel support for set_tid_address
381            // musl needs this to get a valid tid. Return the current tid.
382            raw1(SYS_GETTID, 0)
383        }
384        LNR_set_robust_list => {
385            // TODO: kernel support for robust futex lists
386            // Returning 0 (success) is safe; musl will work without it.
387            0
388        }
389        LNR_get_robust_list => {
390            // TODO: kernel support
391            -38 // ENOSYS
392        }
393        LNR_arch_prctl => {
394            match a1u {
395                ARCH_SET_FS => raw2(SYS_ARCH_PRCTL, a1u, a2u),
396                ARCH_GET_FS => raw2(SYS_ARCH_PRCTL, a1u, a2u),
397                _ => -22, // EINVAL
398            }
399        }
400
401        // === Random ==========================================================
402        LNR_getrandom => {
403            // TODO: kernel support for getrandom
404            -38 // ENOSYS
405        }
406
407        // === Unknown / unimplemented ========================================
408        _ => -38, // ENOSYS
409    }
410}
411
412// === panic handler ========================================================
413
414#[panic_handler]
415fn panic(_info: &core::panic::PanicInfo) -> ! {
416    unsafe {
417        let _ = syscall1(SYS_PROC_EXIT, 127);
418    }
419    loop {}
420}