Master the fundamental concepts of system calls & kernel interface through this focused micro-challenge.
You have read the whole brief, and the concepts above stay free on every task. Writing and running the code needs a plan.
Three hints are available for this task, revealed one at a time inside the code workspace so you can struggle productively before seeing them.
Every task includes starter code, theory, and hidden tests so you can implement and verify locally in the browser.
How it worksThe syscall ABI defines how CPU state crosses privilege rings. On x86-64 Linux, user code loads rax with the syscall number, places arguments in designated registers, executes syscall, and reads the return from rax.
x86-64 Linux passes syscall arguments in fixed registers:
raxrdi, Arg 2: rsi, Arg 3: rdxr10, Arg 5: r8, Arg 6: r9For example, read(3, buf, 1024) maps to NR 0, fd in rdi, buffer in rsi, count in rdx.
The r10-instead-of-rcx quirk you'll learn here exists because the x86-64 syscall instruction itself clobbers rcx and r11 to hold the return address and flags, a detail glibc's syscall wrappers and Go's runtime assembly both have to account for by hand. This ABI is exactly what objdump -d reveals when reverse-engineering a stripped binary, and mismatching it is a classic bug in hand-written syscall stubs for new libc ports.
Before you call the implementation done, walk failure modes on purpose. Test empty structures, single-element edge cases, maximum concurrency, and errno paths that must not crash the program. OS code usually fails in production when happy-path tests pass but invariants break under contention or memory pressure.
Keep structures small and name fields after kernel counterparts when possible. That lets you read man pages and kernel source side by side while you work. Print observable events during development; remove noisy logs once tests pass reliably.
You will document the ABI for three syscalls and write a tiny assembly stub that invokes one. This exercise requires explaining why rcx and r11 are clobbered by the syscall instruction itself.
Every architecture has its own system-call ABI: which register holds the syscall number, which registers hold the arguments, which instruction traps into the kernel, and which numbers name which calls. Build a translator for three ABIs. It decodes a register snapshot into a readable call, encodes a call into register assignments, and compares syscall numbers across ABIs.
| ABI | Number | Arguments 1-6 | Instruction | Result |
|---|---|---|---|---|
| x86_64 | rax | rdi rsi rdx r10 r8 r9 | syscall | rax |
| i386 | eax | ebx ecx edx esi edi ebp | int 0x80 | eax |
| arm64 | x8 | x0 x1 x2 x3 x4 x5 | svc #0 | x0 |
| Call (arguments) | x86_64 | i386 | arm64 |
|---|---|---|---|
| read(fd, buf, count) | 0 | 3 | 63 |
| write(fd, buf, count) | 1 | 4 | 64 |
| open(pathname, flags, mode) | 2 | 5 | none |
| openat(dirfd, pathname, flags, mode) | 257 | 295 | 56 |
| close(fd) | 3 | 6 | 57 |
| exit(status) | 60 | 1 | 93 |
| getpid() | 39 | 20 | 172 |
| brk(addr) | 12 | 45 | 214 |
| mmap(addr, length, prot, flags, fd, offset) | 9 | none, see mmap2 | 222 |
| mmap2(addr, length, prot, flags, fd, pgoffset) | none | 192 | none |
cLoading…
0xffffff9c decodes to -100. warning: R is not a syscall register on ABI. The special case rcx on x86_64 prints warning: 4th argument goes in r10, not rcx (syscall overwrites rcx). With no number register the result is ABI: no syscall number in REG, and an unknown number gives ABI: unknown syscall number N.encode ABI CALL: no such syscall on ABI, with (use openat) added for open. A wrong argument count prints expected N argument(s), got K.i386=192 (mmap2).cLoading…
Encode lines are " %-3s = %-18s ; %s", and the instruction line is " %-24s ; result in %s".
Input:
cLoading…
Output:
cLoading…
Hidden tests cover six-argument mmap on all three ABIs, the mmap2 page conversion and its error, i386 sign extension, the rcx mistake on x86_64, registers from the wrong ABI, missing number registers, calls missing on arm64, wrong argument counts, and unknown ABIs.