Take up openkal 0.11's unit: a handle the caller holds, not a flag (#17) #23
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: numbers | |
| # The kernel's own table, read from the system rather than remembered. | |
| # | |
| # src/sys.h states a number for each call this implementation makes. A number | |
| # that is wrong for one architecture is not a compile error and not a failed | |
| # call: the kernel raises a signal whose name is "bad system call" and the | |
| # program stops, which is a diagnostic that names the program rather than the | |
| # number. So the numbers are read from the system's own header, on both | |
| # architectures, and this workflow is where the reading is recorded. | |
| on: | |
| workflow_dispatch: | |
| push: | |
| paths: | |
| - '.github/workflows/numbers.yml' | |
| jobs: | |
| numbers: | |
| name: the kernel's numbers (${{ matrix.os }}) | |
| runs-on: ${{ matrix.os }} | |
| timeout-minutes: 15 | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| os: [macos-14, macos-15-intel] | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Where the table is | |
| run: | | |
| uname -m | |
| xcrun --show-sdk-path | |
| find "$(xcrun --show-sdk-path)/usr/include/sys" -name 'syscall.h' | |
| - name: Every number this implementation uses | |
| run: | | |
| h="$(xcrun --show-sdk-path)/usr/include/sys/syscall.h" | |
| for n in exit read write close wait4 chdir fchdir fork getpid getuid \ | |
| geteuid kill dup getegid getgid ioctl execve umask munmap \ | |
| mprotect madvise dup2 fsync gettimeofday readv writev \ | |
| ftruncate truncate mmap lseek __getcwd getcwd \ | |
| stat stat64 fstat fstat64 lstat lstat64 \ | |
| fstatat fstatat64 getdirentries getdirentries64 \ | |
| getentropy \ | |
| bsdthread_terminate thread_selfid \ | |
| open openat openat_nocancel renameat faccessat unlinkat \ | |
| readlinkat mkdirat rmdir unlink mkdir rename access \ | |
| ulock_wait ulock_wake __ulock_wait __ulock_wake \ | |
| futimens utimensat futimes utimes settimeofday \ | |
| fchmod fchmodat fcntl \ | |
| pipe sigaction \ | |
| socket connect accept accept_nocancel bind listen shutdown \ | |
| getsockname getpeername setsockopt getsockopt \ | |
| sendto recvfrom sendmsg recvmsg socketpair \ | |
| poll select pselect kevent; do | |
| printf '%-24s' "$n" | |
| grep -E "^#define[[:space:]]+SYS_${n}[[:space:]]" "$h" | head -1 || echo '(absent)' | |
| done | |
| - name: The whole table, for the record | |
| run: | | |
| h="$(xcrun --show-sdk-path)/usr/include/sys/syscall.h" | |
| wc -l "$h" | |
| grep -c '^#define' "$h" | |
| # Uploaded rather than printed in full: it is nine hundred lines. | |
| cp "$h" "syscall-$(uname -m).h" | |
| - uses: actions/upload-artifact@v4 | |
| with: | |
| name: syscall-numbers-${{ matrix.os }} | |
| path: syscall-*.h | |
| # Which numbers this kernel actually serves. | |
| # | |
| # A number that is in the system's table and not in the kernel's is not a | |
| # failed call: the kernel raises a signal whose name is "bad system call" and | |
| # the program stops, which is a diagnostic that names the program rather than | |
| # the number. Reading the table above does not distinguish the two, so this | |
| # job issues each call with arguments that cannot do harm and reports which | |
| # of them the kernel refuses to recognise. | |
| served: | |
| name: the numbers this kernel serves (${{ matrix.os }}) | |
| runs-on: ${{ matrix.os }} | |
| timeout-minutes: 15 | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| os: [macos-14, macos-15-intel] | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Issue each call and report what the kernel does with it | |
| run: | | |
| cat > served.c <<'EOF' | |
| #include <setjmp.h> | |
| #include <signal.h> | |
| #include <stdio.h> | |
| #include <string.h> | |
| #include <errno.h> | |
| static sigjmp_buf back; | |
| static void on_bad(int s) { (void)s; siglongjmp(back, 1); } | |
| extern long syscall(long, ...); | |
| /* Every number src/sys.h states, with arguments chosen so that the | |
| * call either fails harmlessly or does nothing. A call that returns | |
| * or fails is served; a call that does neither raised the signal. */ | |
| struct probe { const char* name; long n; long a, b, c, d; }; | |
| static const struct probe probes[] = { | |
| { "close", 6, -1, 0, 0, 0 }, | |
| { "wait4", 7, -1, 0, 1, 0 }, /* WNOHANG */ | |
| { "fchdir", 13, -1, 0, 0, 0 }, | |
| { "dup", 41, -1, 0, 0, 0 }, | |
| { "ioctl", 54, -1, 0, 0, 0 }, | |
| { "munmap", 73, 0, 0, 0, 0 }, | |
| { "mprotect", 74, 0, 0, 0, 0 }, | |
| { "madvise", 75, 0, 0, 0, 0 }, | |
| { "dup2", 90, -1, -1, 0, 0 }, | |
| { "fcntl", 92, -1, 50, 0, 0 }, /* F_GETPATH */ | |
| { "fsync", 95, -1, 0, 0, 0 }, | |
| { "gettimeofday", 116, 0, 0, 0, 0 }, | |
| { "readv", 120, -1, 0, 0, 0 }, | |
| { "writev", 121, -1, 0, 0, 0 }, | |
| { "utimes", 138, 0, 0, 0, 0 }, | |
| { "futimes", 139, -1, 0, 0, 0 }, | |
| { "mmap", 197, 0, 0, 0, 0 }, | |
| { "lseek", 199, -1, 0, 0, 0 }, | |
| { "ftruncate", 201, -1, 0, 0, 0 }, | |
| { "stat64", 338, 0, 0, 0, 0 }, | |
| { "fstat64", 339, -1, 0, 0, 0 }, | |
| { "lstat64", 340, 0, 0, 0, 0 }, | |
| { "getdirentries64",344, -1, 0, 0, 0 }, | |
| { "thread_selfid", 372, 0, 0, 0, 0 }, | |
| { "openat", 463, -2, 0, 0, 0 }, | |
| { "renameat", 465, -2, 0, -2, 0 }, | |
| { "faccessat", 466, -2, 0, 0, 0 }, | |
| { "fstatat64", 470, -2, 0, 0, 0 }, | |
| { "unlinkat", 472, -2, 0, 0, 0 }, | |
| { "readlinkat", 473, -2, 0, 0, 0 }, | |
| { "mkdirat", 475, -2, 0, 0, 0 }, | |
| { "ulock_wait", 515, 0, 0, 0, 0 }, | |
| { "ulock_wake", 516, 0, 0, 0, 0 }, | |
| /* The socket calls, with a descriptor that cannot be one. */ | |
| { "recvfrom", 29, -1, 0, 0, 0 }, | |
| { "accept", 30, -1, 0, 0, 0 }, | |
| { "getpeername", 31, -1, 0, 0, 0 }, | |
| { "getsockname", 32, -1, 0, 0, 0 }, | |
| { "select", 93, 0, 0, 0, 0 }, | |
| { "socket", 97, 0, 0, 0, 0 }, | |
| { "connect", 98, -1, 0, 0, 0 }, | |
| { "bind", 104, -1, 0, 0, 0 }, | |
| { "setsockopt", 105, -1, 0, 0, 0 }, | |
| { "listen", 106, -1, 0, 0, 0 }, | |
| { "getsockopt", 118, -1, 0, 0, 0 }, | |
| { "sendto", 133, -1, 0, 0, 0 }, | |
| { "shutdown", 134, -1, 0, 0, 0 }, | |
| { "poll", 230, 0, 0, 0, 0 }, | |
| }; | |
| int main(void) { | |
| struct sigaction sa; memset(&sa, 0, sizeof sa); | |
| sa.sa_handler = on_bad; | |
| sigaction(SIGSYS, &sa, 0); | |
| int refused = 0; | |
| for (unsigned i = 0; i < sizeof probes / sizeof *probes; i++) { | |
| if (sigsetjmp(back, 1) == 0) { | |
| errno = 0; | |
| long r = syscall(probes[i].n, probes[i].a, probes[i].b, | |
| probes[i].c, probes[i].d); | |
| printf("%-18s %4ld served (returned %ld, errno %d)\n", | |
| probes[i].name, probes[i].n, r, errno); | |
| } else { | |
| printf("%-18s %4ld REFUSED (bad system call)\n", | |
| probes[i].name, probes[i].n); | |
| refused++; | |
| } | |
| } | |
| printf("\n%d refused\n", refused); | |
| return refused ? 1 : 0; | |
| } | |
| EOF | |
| cc -O0 -o served served.c | |
| ./served |