forktest.c (4128B)
1 /* Tier 2 demo: parent atomic-spawns "child" with one syscall, waitid's 2 * for it, reports the result. Mirrors the scheme1 prelude's spawn/wait 3 * pattern in C. The seed kernel offers sys_spawn (private syscall 1024) 4 * in place of POSIX clone+execve. */ 5 6 typedef long i64; 7 typedef unsigned long u64; 8 typedef int i32; 9 10 #if defined(__x86_64__) 11 #define SYS_write 1 12 #define SYS_exit_group 60 13 #define SYS_waitid 247 14 #elif defined(__riscv) 15 #define SYS_write 64 16 #define SYS_exit_group 93 17 #define SYS_waitid 95 18 #else 19 #define SYS_write 64 20 #define SYS_exit_group 93 21 #define SYS_waitid 95 22 #endif 23 #define SYS_spawn 1024 24 25 extern i64 syscall6(u64 nr, u64 a, u64 b, u64 c, u64 d, u64 e, u64 f); 26 27 static i64 sys_write(int fd, const void *buf, u64 n) { return syscall6(SYS_write, (u64)fd, (u64)buf, n, 0,0,0); } 28 static void sys_exit(int c) { syscall6(SYS_exit_group, (u64)c, 0,0,0,0,0); for(;;); } 29 static i64 sys_spawn(const char *p, char **argv) { return syscall6(SYS_spawn, (u64)p, (u64)argv, 0, 0, 0, 0); } 30 static i64 sys_waitid(int id, int pid, void *info, int opts) { return syscall6(SYS_waitid, (u64)id, (u64)pid, (u64)info, (u64)opts, 0, 0); } 31 32 void *memset(void *d, int c, u64 n) { 33 unsigned char *dd = d; for (u64 i = 0; i < n; i++) dd[i] = (unsigned char)c; return d; 34 } 35 36 static u64 strlen_(const char *s) { u64 n = 0; while (s[n]) n++; return n; } 37 static void puts_(const char *s) { sys_write(1, s, strlen_(s)); } 38 static void put_d(i64 v) { 39 char buf[24]; int i = 0; 40 if (v < 0) { sys_write(1, "-", 1); v = -v; } 41 if (v == 0) buf[i++] = '0'; 42 while (v) { buf[i++] = '0' + (char)(v % 10); v /= 10; } 43 while (i--) sys_write(1, &buf[i], 1); 44 } 45 46 void _start_c(long argc, char **argv) { 47 puts_("[forktest] argc="); put_d(argc); puts_(" argv[0]="); puts_(argv[0]); puts_("\n"); 48 49 char *cargv[3]; 50 cargv[0] = "child"; 51 cargv[1] = "from-parent"; 52 cargv[2] = 0; 53 long pid = sys_spawn("child", cargv); 54 puts_("[forktest:parent] spawn returned pid="); put_d(pid); puts_("\n"); 55 unsigned char info[128]; 56 memset(info, 0, sizeof info); 57 long w = sys_waitid(/*P_PID*/1, (int)pid, info, /*WEXITED*/4); 58 puts_("[forktest:parent] waitid="); put_d(w); 59 puts_(" si_code="); put_d(*(i32 *)(info + 8)); 60 puts_(" si_status="); put_d(*(i32 *)(info + 24)); 61 puts_("\n"); 62 sys_exit(0); 63 } 64 65 /* tcc 0.9.26 quirk: in a toplevel asm() block, `.globl name` followed by 66 * `name:` leaves the symbol UND in the .o symtab. Putting the label 67 * first and `.globl name` after makes tcc register it as defined. gcc 68 * accepts both orderings. */ 69 #if defined(__x86_64__) 70 asm( 71 "_start: mov (%rsp), %rdi\n" 72 ".globl _start\n" 73 ".type _start, @function\n" 74 " lea 8(%rsp), %rsi\n" 75 " call _start_c\n" 76 "\n" 77 "syscall6:\n" 78 ".globl syscall6\n" 79 ".type syscall6, @function\n" 80 " mov %rdi, %rax\n" 81 " mov %rsi, %rdi\n" 82 " mov %rdx, %rsi\n" 83 " mov %rcx, %rdx\n" 84 " mov %r8, %r10\n" 85 " mov %r9, %r8\n" 86 " mov 8(%rsp), %r9\n" 87 " int $0x80\n" 88 " ret\n"); 89 #elif defined(__riscv) 90 asm( 91 "_start: ld a0, 0(sp)\n" 92 ".globl _start\n" 93 ".type _start, @function\n" 94 " addi a1, sp, 8\n" 95 " tail _start_c\n" 96 "\n" 97 "syscall6:\n" 98 ".globl syscall6\n" 99 ".type syscall6, @function\n" 100 " mv a7, a0\n" 101 " mv a0, a1\n" 102 " mv a1, a2\n" 103 " mv a2, a3\n" 104 " mv a3, a4\n" 105 " mv a4, a5\n" 106 " mv a5, a6\n" 107 " ecall\n" 108 " ret\n"); 109 #else 110 asm( 111 "_start: ldr x0, [sp]\n" 112 ".globl _start\n" 113 ".type _start, %function\n" 114 " add x1, sp, #8\n" 115 " b _start_c\n" 116 "\n" 117 /* syscall6(nr, a..f) — args land in x0..x6 by SysV ABI. Linux 118 * arm64 wants the syscall number in x8 and a..f in x0..x5. */ 119 "syscall6:\n" 120 ".globl syscall6\n" 121 ".type syscall6, %function\n" 122 " mov x8, x0\n" 123 " mov x0, x1\n" 124 " mov x1, x2\n" 125 " mov x2, x3\n" 126 " mov x3, x4\n" 127 " mov x4, x5\n" 128 " mov x5, x6\n" 129 " svc #0\n" 130 " ret\n"); 131 #endif