Skip to main content

musl_compat/
lib.rs

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