================================================================================ K10 terminal-model — source-site snapshot ================================================================================ Generated by linux-wasm/tools/k10-terminal-model-probe/snapshot-sources.sh from Documentation/wasm/terminal-model.rst §9 + §11 (strategy B chosen). Pinned at K10-A2 sub-phase close (per §12 K10-A2). Snapshots across six trees: [LIN] upstream linux/ tree (init/main.c, include/linux/init.h, include/asm-generic/vmlinux.lds.h, drivers/tty/{pty, n_tty,tty_io,tty_jobctrl,tty_ioctl}.c) [WAS] our wasm32-port/ tree (kernel/init.c K6 populate_rootfs hand-call; kernel/sections.c K2 __setup_start/end shims; kernel/percpu_anchors.c sibling anchor pattern; kernel/ percpu.c sibling override-and-post-link-patch design) [TLC] linux-wasm/scripts/ + Documentation/ (link-vmlinux-wasm.sh pipeline order; wasm-add-memory.py sibling pure-Python pass; post-link-pass-conventions.rst §1 ordering doc) [UPS] wackywasm-tools/musl-1.2.5/src/ (termios/ + misc/ {openpty,forkpty,pty}.c surfaces K10-B/C demos exercise) [OVR] wackywasm-tools/musl-src/src_overrides/ (existing K7/K8/K9 src_overrides; K10 may add tty/wasm32/ if tcsetattr/tcgetattr stubs need musl-side wiring) [HWJ] hardwarejs/src/ (bootKernel.ts BootKernelOptions extension; kernelSpawnHandler.ts existing tty-related stubs; worker.mjs read/write that K10-B routes through slave-fd ldisc) Section legend: [DEF] the function definition (file:line + leading prose) [CALL] call sites in the relevant tree [SITE] inline excerpt of a load-bearing site (no function definition but a code shape we depend on) [K10-B] where K10-B-phases will insert NEW code (annotation only; does not exist in any tree yet) [K10-A3] where K10-A3 (strategy B post-link pass) will land (annotation only) Snapshot taken against: upstream linux: v6.12 upstream musl: 1.2.5 wackywasm-tools/musl-src: K9 src_overrides state (signal/wasm32/, internal/wasm32/, no tty/ yet) Strategy: B (per terminal-model.rst §11.2 + §11.2.1) Generated: 2026-05-26 (UTC, deterministic stamp from `date -u`) Note: snapshot file deliberately omits HH:MM:SS in the stamp so re-runs on the same day produce byte-identical output. Drift detection is at line-number granularity. ================================================================================ 1. linux/init/main.c — do_initcalls + do_initcall_level (K10-A4 consumer) ================================================================================ Module: upstream initcall dispatcher. K10-A4 lights this path up. do_initcalls() walks levels 0..7 (early/.../7s) plus the explicit rootfs level; for each level it walks [__initcall_start, __initcall_end) calling each initcall_t function in turn. On wasm32 today these symbols resolve to 0 (--allow-undefined makes them 0-imports), so do_initcalls walks [0, 0) per level — empty. The K6 populate_ rootfs hand-call in arch_kernel_init_pre_run_init compensates for THE SINGLE rootfs initcall but nothing else. K10-A3 strategy B emits per-level anchor symbols pointing at the active overlay segments holding the original passive bytes; do_initcalls then walks real ranges and dispatches every upstream initcall correctly. [DEF] linux/init/main.c::do_initcalls(void) init/main.c:1336 static void __init do_initcalls(void) [DEF] linux/init/main.c::do_initcall_level(int) init/main.c:1321 static void __init do_initcall_level(int level, char *command_line) [DEF] linux/init/main.c::do_one_initcall(initcall_t) init/main.c:1261 int __init_or_module do_one_initcall(initcall_t fn) [CALL] linux/init/main.c — __initcall_start / __initcall_end references: init/main.c:1292: __initcall0_start, init/main.c:1293: __initcall1_start, init/main.c:1294: __initcall2_start, init/main.c:1295: __initcall3_start, init/main.c:1296: __initcall4_start, init/main.c:1297: __initcall5_start, init/main.c:1298: __initcall6_start, init/main.c:1299: __initcall7_start, init/main.c:1300: __initcall_end, init/main.c:1376: for (fn = __initcall_start; fn < __initcall0_start; fn++) ================================================================================ 2. linux/include/linux/init.h — __define_initcall macro chain (K10-A3 source) ================================================================================ Module: upstream initcall registration. The __define_initcall(fn, id) macro emits a function-pointer entry into section .initcall.init. Each upstream subsystem registers via one of the wrapper macros (early_initcall, core_initcall, subsys_initcall, fs_initcall, device_initcall, late_initcall, plus their _sync variants). At link time wasm-ld groups all .initcall.init contributions per id into the corresponding data segment. K10-A1 confirmed 10 such segments survive --no-merge-data-segments + wasm-opt --asyncify, named exactly .initcall{early,0,1,2,4,5,6,7,7s,rootfs}.init in vmlinux.wasm. [DEF] linux/include/linux/init.h::__define_initcall + level wrappers: include/linux/init.h:282:#define __define_initcall(fn, id) ___define_initcall(fn, id, .initcall##id) include/linux/init.h:289:#define early_initcall(fn) __define_initcall(fn, early) include/linux/init.h:300:#define core_initcall(fn) __define_initcall(fn, 1) include/linux/init.h:302:#define postcore_initcall(fn) __define_initcall(fn, 2) include/linux/init.h:304:#define arch_initcall(fn) __define_initcall(fn, 3) include/linux/init.h:306:#define subsys_initcall(fn) __define_initcall(fn, 4) include/linux/init.h:308:#define fs_initcall(fn) __define_initcall(fn, 5) include/linux/init.h:310:#define rootfs_initcall(fn) __define_initcall(fn, rootfs) include/linux/init.h:311:#define device_initcall(fn) __define_initcall(fn, 6) include/linux/init.h:313:#define late_initcall(fn) __define_initcall(fn, 7) [CALL] linux/include/linux/init.h — initcall_entry_t / initcall_t typedefs: include/linux/init.h:115:typedef int (*initcall_t)(void); include/linux/init.h:119:typedef int initcall_entry_t; include/linux/init.h:121:static inline initcall_t initcall_from_entry(initcall_entry_t *entry) include/linux/init.h:126:typedef initcall_t initcall_entry_t; include/linux/init.h:128:static inline initcall_t initcall_from_entry(initcall_entry_t *entry) include/linux/init.h:134:extern initcall_entry_t __con_initcall_start[], __con_initcall_end[]; include/linux/init.h:142:extern int do_one_initcall(initcall_t fn); include/linux/init.h:158:extern initcall_entry_t __initcall_start[]; include/linux/init.h:159:extern initcall_entry_t __initcall0_start[]; include/linux/init.h:160:extern initcall_entry_t __initcall1_start[]; include/linux/init.h:161:extern initcall_entry_t __initcall2_start[]; include/linux/init.h:162:extern initcall_entry_t __initcall3_start[]; include/linux/init.h:163:extern initcall_entry_t __initcall4_start[]; include/linux/init.h:164:extern initcall_entry_t __initcall5_start[]; include/linux/init.h:165:extern initcall_entry_t __initcall6_start[]; include/linux/init.h:166:extern initcall_entry_t __initcall7_start[]; include/linux/init.h:167:extern initcall_entry_t __initcall_end[]; include/linux/init.h:266: static_assert(__same_type(initcall_t, &fn)); include/linux/init.h:269: static initcall_t __name __used \ ================================================================================ 3. linux/include/asm-generic/vmlinux.lds.h — INITCALLS macro (K10-A3 mirror) ================================================================================ Module: upstream linker-script primitive. INITCALLS / INIT_CALLS_ LEVEL define the per-level boundary symbols __initcall_start / __initcall_end via linker-script syntax. wasm-ld doesn't consume linker scripts — same templated debt class as the K2 sections.c shim and the K3 percpu_anchors.c sibling. K10-A3 emits the per-level boundaries via post-link binary surgery (synthesising active overlay segments at chosen addresses; symbol values follow). [DEF] linux/include/asm-generic/vmlinux.lds.h::INITCALLS / INIT_CALLS_LEVEL: include/asm-generic/vmlinux.lds.h:894:#define INIT_CALLS_LEVEL(level) \ include/asm-generic/vmlinux.lds.h:899:#define INIT_CALLS \ [SITE] linux/include/asm-generic/vmlinux.lds.h — KEEP(*(.initcall*.init)) shape: include/asm-generic/vmlinux.lds.h:896: KEEP(*(.initcall##level##.init)) \ include/asm-generic/vmlinux.lds.h:897: KEEP(*(.initcall##level##s.init)) \ include/asm-generic/vmlinux.lds.h:901: KEEP(*(.initcallearly.init)) \ ================================================================================ 4. linux/drivers/tty/pty.c — pty_init / ptmx_open (K10-B1 surface) ================================================================================ Module: upstream pty subsystem. pty_init runs as a subsys_initcall (level 4). Once K10-A4 lights up initcall dispatch, this fires automatically. Provides /dev/ptmx (the master multiplexer) and the /dev/pts/N slave path machinery. [DEF] linux/drivers/tty/pty.c::pty_init / ptmx_open / pts_alloc: drivers/tty/pty.c:941 static int __init pty_init(void) drivers/tty/pty.c:790 static int ptmx_open(struct inode *inode, struct file *filp) drivers/tty/pty.c:439 static int pty_install(struct tty_driver *driver, struct tty_struct *tty) [CALL] linux/drivers/tty/pty.c — initcall registrations: ================================================================================ 5. linux/drivers/tty/n_tty.c — N_TTY line discipline (K10-B2/B3 surface) ================================================================================ Module: upstream canonical-mode line discipline. n_tty_receive gets called when bytes arrive at the master side; n_tty_read serves bytes to the slave-fd-reading user process; signal generation (Ctrl-C → SIGINT, Ctrl-Z → SIGTSTP, Ctrl-\\ → SIGQUIT) happens in process_input_char via isig(). K10-B3 wires this to K8's signal infrastructure (kill_pgrp on the controlling tty's foreground pgrp). [DEF] linux/drivers/tty/n_tty.c::n_tty_{receive,read,write,ioctl}: drivers/tty/n_tty.c:2157 static ssize_t n_tty_read(struct tty_struct *tty, struct file *file, u8 *kbuf, drivers/tty/n_tty.c:2358 static ssize_t n_tty_write(struct tty_struct *tty, struct file *file, drivers/tty/n_tty.c:2503 static int n_tty_ioctl(struct tty_struct *tty, unsigned int cmd, [DEF] linux/drivers/tty/n_tty.c::n_tty_init / process_input_char / isig: drivers/tty/n_tty.c:2557 void __init n_tty_init(void) drivers/tty/n_tty.c:1090 static void isig(int sig, struct tty_struct *tty) [CALL] linux/drivers/tty/n_tty.c — VINTR / VSUSP / VQUIT / VEOF / VEOL references: ================================================================================ 6. linux/drivers/tty/tty_io.c — tty subsystem core (K10-B1 dependency) ================================================================================ Module: upstream tty class registration. tty_init registers the tty class. K10-B1 inherits unchanged. The arch wiring is via HardwareJS pushing bytes to the master tty's flip buffer (tty_insert_flip_string / tty_flip_buffer_push); the kernel-side tty_io.c machinery is upstream-clean. [DEF] linux/drivers/tty/tty_io.c::tty_init / tty_register_driver: drivers/tty/tty_io.c:3644 int __init tty_init(void) drivers/tty/tty_io.c:3440 int tty_register_driver(struct tty_driver *driver) drivers/tty/tty_io.c:3505 void tty_unregister_driver(struct tty_driver *driver) [CALL] linux/drivers/tty/ — tty_insert_flip_string / tty_flip_buffer_push: drivers/tty/amiserial.c:260: tty_flip_buffer_push(&info->tport); drivers/tty/ehv_bytechan.c:384: ret = tty_insert_flip_string(&bc->port, buffer, len); drivers/tty/ehv_bytechan.c:399: tty_flip_buffer_push(&bc->port); drivers/tty/goldfish.c:146: tty_flip_buffer_push(&qtty->port); drivers/tty/hvc/hvc_console.c:755: tty_flip_buffer_push(&hp->port); drivers/tty/hvc/hvcs.c:583: tty_insert_flip_string(&hvcsd->port, buf, got); drivers/tty/hvc/hvcs.c:593: tty_flip_buffer_push(&hvcsd->port); drivers/tty/hvc/hvsi.c:457: tty_flip_buffer_push(&hp->port); drivers/tty/hvc/hvsi.c:503: tty_flip_buffer_push(&hp->port); drivers/tty/hvc/hvsi.c:984: tty_flip_buffer_push(&hp->port); ================================================================================ 7. linux/drivers/tty/tty_jobctrl.c — controlling tty + foreground pgrp (K10-C1/C2) ================================================================================ Module: upstream job-control surface. TIOCSCTTY claims the tty as controlling for the calling session leader; tcsetpgrp sets the foreground pgrp; __tty_check_change generates SIGTTIN/SIGTTOU when a background pgrp tries to read/write. Builds on K9-C2's processGroupRegistry (the kernel-side task->signal->pgrp / ->session is consulted by these paths). [DEF] linux/drivers/tty/tty_jobctrl.c::tiocsctty / no_tty: drivers/tty/tty_jobctrl.c:365 static int tiocsctty(struct tty_struct *tty, struct file *file, int arg) drivers/tty/tty_jobctrl.c:338 void no_tty(void) [DEF] linux/drivers/tty/tty_jobctrl.c::tiocgpgrp / tiocspgrp / __tty_check_change: drivers/tty/tty_jobctrl.c:466 static int tiocgpgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t __user *p) drivers/tty/tty_jobctrl.c:493 static int tiocspgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t __user *p) drivers/tty/tty_jobctrl.c:33 int __tty_check_change(struct tty_struct *tty, int sig) [CALL] linux/drivers/tty/tty_jobctrl.c — kill_pgrp + SIGTTIN/SIGTTOU references: drivers/tty/tty_jobctrl.c:28: * not in the foreground, send a SIGTTOU. If the signal is blocked or drivers/tty/tty_jobctrl.c:51: if (sig == SIGTTIN) drivers/tty/tty_jobctrl.c:56: kill_pgrp(pgrp, sig, 1); drivers/tty/tty_jobctrl.c:71: return __tty_check_change(tty, SIGTTOU); drivers/tty/tty_jobctrl.c:185: * tty_signal_session_leader - sends SIGHUP to session leader drivers/tty/tty_jobctrl.c:189: * Send SIGHUP and SIGCONT to the session leader and its process group. drivers/tty/tty_jobctrl.c:218: send_signal_locked(SIGHUP, SEND_SIG_PRIV, p, PIDTYPE_TGID); drivers/tty/tty_jobctrl.c:234: kill_pgrp(tty_pgrp, SIGHUP, exit_session); drivers/tty/tty_jobctrl.c:249: * (1) Sends a SIGHUP and SIGCONT to the foreground process group drivers/tty/tty_jobctrl.c:280: kill_pgrp(tty_pgrp, SIGHUP, on_exit); drivers/tty/tty_jobctrl.c:282: kill_pgrp(tty_pgrp, SIGCONT, on_exit); drivers/tty/tty_jobctrl.c:296: kill_pgrp(old_pgrp, SIGHUP, on_exit); drivers/tty/tty_jobctrl.c:297: kill_pgrp(old_pgrp, SIGCONT, on_exit); ================================================================================ 8. linux/drivers/tty/tty_ioctl.c — termios + TCGETS/TCSETS (K10-B2 surface) ================================================================================ Module: upstream termios syscall path. tty_mode_ioctl handles TCGETS/TCSETS/TCSETSW/TCSETSF (the canonical/raw mode switching surface bash readline uses). K10-B2 inherits unchanged once ldisc is up. [DEF] linux/drivers/tty/tty_ioctl.c::tty_mode_ioctl / set_termios / get_termios: drivers/tty/tty_ioctl.c:769 int tty_mode_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) drivers/tty/tty_ioctl.c:448 static int set_termios(struct tty_struct *tty, void __user *arg, int opt) [CALL] linux/drivers/tty/tty_ioctl.c — TCGETS / TCSETS / TCSETSW / TCSETSF references: drivers/tty/tty_ioctl.c:802: case TCSETSF: drivers/tty/tty_ioctl.c:804: case TCSETSW: drivers/tty/tty_ioctl.c:806: case TCSETS: drivers/tty/tty_ioctl.c:809: case TCGETS: drivers/tty/tty_ioctl.c:815: case TCGETS: drivers/tty/tty_ioctl.c:832: case TCGETA: drivers/tty/tty_ioctl.c:834: case TCSETAF: drivers/tty/tty_ioctl.c:836: case TCSETAW: drivers/tty/tty_ioctl.c:838: case TCSETA: ================================================================================ 9. wasm32-port/kernel/init.c — populate_rootfs hand-call (K10-A4 retires) ================================================================================ Module: arch-side init. K6 introduced arch_kernel_init_pre_run_init(), called from the upstream kernel_init path before run_init_process(). Currently it hand-calls populate_rootfs() because do_initcalls() is a no-op on wasm32 (the §15 row K10-A4 retires). Once K10-A4 lands and do_initcalls walks every level via the K10-A3 anchor symbols, the hand-call MUST be removed (rootfs_initcall registration of populate_rootfs would otherwise fire twice). [DEF] wasm32-port/kernel/init.c::arch_kernel_init_pre_run_init: kernel/init.c:224 void arch_kernel_init_pre_run_init(void) [CALL] wasm32-port/kernel/init.c — populate_rootfs / do_initcalls / __initcall references: kernel/init.c:179: * Bridge for upstream's `populate_rootfs` rootfs_initcall. kernel/init.c:181: * Upstream registers populate_rootfs via `rootfs_initcall(...)`, kernel/init.c:183: * by init/main.c::do_initcalls(). On wasm32 the `__initcall*_start kernel/init.c:186: * NULLs and `do_initcalls()` is a no-op. populate_rootfs never kernel/init.c:214: "(arch hook bridges populate_rootfs because wasm32 doesn't " kernel/init.c:266: * initrd_start/end → populate_rootfs → kern_path() chain kernel/init.c:340: * so on wasm32 — where do_initcalls is a no-op (§15 row, kernel/init.c:341: * see sections.c) and populate_rootfs hasn't yet run — the kernel/init.c:350: * twice in K6: at K6-B1 (populate_rootfs not run) and again ================================================================================ 10. wasm32-port/kernel/sections.c — __setup_start/end shims (related debt) ================================================================================ Module: K2 templated debt sibling. Same wasm-ld-doesn't-consume- linker-scripts class as the initcall debt K10-A3 retires. sections.c stays as-is at K10 (its sentinel-array workaround for do_early_param + obsolete_checksetup is independent of the initcall path); a future K-phase may unify both into a single post-link pass. [SITE] wasm32-port/kernel/sections.c — full file (K10 reference): kernel/sections.c:1 // SPDX-License-Identifier: GPL-2.0 kernel/sections.c:2 /* kernel/sections.c:3 * arch/wasm32: shims for linker-script-defined section boundary symbols. kernel/sections.c:4 * kernel/sections.c:5 * On every other arch, Linux defines symbols like __setup_start / kernel/sections.c:6 * __setup_end / __initcall_start / __initcall_end inside the GNU kernel/sections.c:7 * linker script (include/asm-generic/vmlinux.lds.h) bracketing the kernel/sections.c:8 * corresponding `.init.setup`, `.initcall*.init`, etc. sections. kernel/sections.c:9 * kernel/sections.c:10 * wasm-ld does NOT consume GNU linker scripts and we don't run one in kernel/sections.c:11 * scripts/link-vmlinux-wasm.sh, so each of those symbol names ends up kernel/sections.c:12 * undefined. Building with --allow-undefined makes them link as kernel/sections.c:13 * 0-imported symbols; reading them at runtime returns 0; iterating kernel/sections.c:14 * `[0, 0)` is technically valid for plain `for` loops but the kernel/sections.c:15 * do-while in init/main.c::obsolete_checksetup runs the body once kernel/sections.c:16 * regardless and then dereferences a NULL `obs_kernel_param.str` — kernel/sections.c:17 * producing the K2-test "memory access out of bounds" trap. kernel/sections.c:18 * kernel/sections.c:19 * K2 fix: define each boundary symbol as a one-element sentinel kernel/sections.c:20 * range. Loops iterate exactly once, see an entry whose `.str` is a kernel/sections.c:21 * deliberately-unmatchable string, fall through, exit cleanly. No kernel/sections.c:22 * real handlers from upstream's `.init.setup` / `.initcallN.init` kernel/sections.c:23 * sections run yet — the wasm-ld-equivalent section anchoring is kernel/sections.c:24 * a §15 row deferred to K5 when virtio_console wants its kernel/sections.c:25 * `console=virtcon` handler to actually take effect. That row also kernel/sections.c:26 * unlocks subsystem initcall dispatch (rest_init / kernel_init's kernel/sections.c:27 * do_initcalls() walks the same kind of bounded table). kernel/sections.c:28 * kernel/sections.c:29 * This file is the centralised home for these shims so a future kernel/sections.c:30 * commit can replace them all together by emitting them from a real kernel/sections.c:31 * scripts/wasm-link-script.py post-processor. kernel/sections.c:32 */ kernel/sections.c:33 kernel/sections.c:34 #include kernel/sections.c:35 #include kernel/sections.c:36 kernel/sections.c:37 /* kernel/sections.c:38 * obs_kernel_param sentinel: every field is set such that kernel/sections.c:39 * obsolete_checksetup's do-while sees one non-matching entry and kernel/sections.c:40 * exits. .str is a long, distinctive string no upstream caller will kernel/sections.c:41 * pass; .setup_func is NULL but never reached because parameqn fails. kernel/sections.c:42 */ kernel/sections.c:43 static const char wasm32_setup_sentinel_str[] = kernel/sections.c:44 "__wasm32_section_sentinel_never_matches_any_param__"; kernel/sections.c:45 kernel/sections.c:46 const struct obs_kernel_param __setup_start[1] kernel/sections.c:47 __attribute__((used, aligned(__alignof__(struct obs_kernel_param)))) = { kernel/sections.c:48 { kernel/sections.c:49 .str = wasm32_setup_sentinel_str, kernel/sections.c:50 .setup_func = NULL, kernel/sections.c:51 .early = 0, kernel/sections.c:52 }, kernel/sections.c:53 }; kernel/sections.c:54 kernel/sections.c:55 /* kernel/sections.c:56 * __setup_end aliased to __setup_start means start == end, so: kernel/sections.c:57 * - `for (p = start; p < end; p++)` (do_early_param's path) skips. kernel/sections.c:58 * - `do { ... } while (p < end);` (obsolete_checksetup's path) kernel/sections.c:59 * runs once on the sentinel and exits. kernel/sections.c:60 * Both behaviours are safe and produce zero handler dispatches, kernel/sections.c:61 * which matches the K2 trade-off documented above. kernel/sections.c:62 */ kernel/sections.c:63 /* kernel/sections.c:64 * __setup_end must be a distinct symbol whose value is one entry past kernel/sections.c:65 * __setup_start[0], so that: kernel/sections.c:66 * do { ... p++; } while (p < __setup_end); kernel/sections.c:67 * runs exactly once on the sentinel and exits. kernel/sections.c:68 * kernel/sections.c:69 * We can't use __attribute__((alias)) — that would make __setup_end kernel/sections.c:70 * equal to __setup_start, and the do-while would run forever (p++ kernel/sections.c:71 * past start, then p < end is false, but the loop is do-while so kernel/sections.c:72 * "ok" — actually that DOES exit after one iter; same address). kernel/sections.c:73 * kernel/sections.c:74 * Empirically, the simpler scheme of "both point at the same single- kernel/sections.c:75 * element array" is sufficient: the do-while runs once, the for-loop kernel/sections.c:76 * is empty, neither dereferences past the sentinel. To avoid the kernel/sections.c:77 * "definition cannot also be an alias" clang error, we make kernel/sections.c:78 * __setup_end a separate full definition that aliases the same data kernel/sections.c:79 * via a shared backing object. kernel/sections.c:80 */ kernel/sections.c:81 extern const struct obs_kernel_param __setup_start[]; kernel/sections.c:82 const struct obs_kernel_param *const __setup_end_marker = &__setup_start[0]; kernel/sections.c:83 kernel/sections.c:84 /* kernel/sections.c:85 * Provide __setup_end as a real array symbol at the same address as kernel/sections.c:86 * __setup_start by re-exporting through inline assembly. wasm-ld kernel/sections.c:87 * accepts `.globl` and an absolute symbol equate. kernel/sections.c:88 */ kernel/sections.c:89 asm( kernel/sections.c:90 " .globl __setup_end\n" kernel/sections.c:91 " .type __setup_end,@object\n" kernel/sections.c:92 " .set __setup_end, __setup_start\n" kernel/sections.c:93 ); ================================================================================ 11. wasm32-port/kernel/percpu_anchors.c — K10-A3 anchor pattern (sibling) ================================================================================ Module: K3/K4 sibling anchor pattern. Defines __per_cpu_start (in section .data..percpu..aa_start, aligned PAGE_SIZE) and __per_cpu_end (in .data..percpu..zz_end). wasm-ld input-order determines emission order; the post-link pass (deferred from K3) is what would patch __per_cpu_end's address to bracket the actual percpu image. K10-A3's wasm-bracket-initcalls.py is the first concrete implementation of this pattern; initcall_anchors.c is its sibling for initcalls. [SITE] wasm32-port/kernel/percpu_anchors.c — anchor declarations (K3 originals): kernel/percpu_anchors.c:27: * a __attribute__((section(".data..percpu..aa_start"))) sorts FIRST kernel/percpu_anchors.c:29: * c __attribute__((section(".data..percpu..zz_end"))) sorts LAST kernel/percpu_anchors.c:69:__attribute__((section(".data..percpu..aa_start"), used, aligned(PAGE_SIZE))) kernel/percpu_anchors.c:114:extern char __per_cpu_load[1] __attribute__((alias("__per_cpu_start"))); kernel/percpu_anchors.c:116:extern char __per_cpu_end[1]; kernel/percpu_anchors.c:162:__attribute__((section(".data..percpu..zz_end"), used)) [DEF] wasm32-port/kernel/percpu_anchors.c::__per_cpu_start / __per_cpu_end: kernel/percpu_anchors.c:70:char __per_cpu_start[1] = { 0 }; kernel/percpu_anchors.c:163:char __per_cpu_end[1] = { 0 }; ================================================================================ 12. wasm32-port/kernel/percpu.c — K10-A3 override pattern (sibling) ================================================================================ Module: K4-R7 sibling. setup_per_cpu_areas overrides the generic mm/percpu.c version because __per_cpu_end - __per_cpu_start underestimates the static percpu image (input-order placement makes start+end adjacent before any actual .data..percpu segments). The post-link pass deferred from K3 would compute the real extent and patch __per_cpu_end. K10-A3 is the analogous post-link pass for initcalls; initcalls don't need a runtime override (the anchor symbol values land directly), so the K10-A3 pattern is simpler than K4-R7. [DEF] wasm32-port/kernel/percpu.c::setup_per_cpu_areas: kernel/percpu.c:75 void __init setup_per_cpu_areas(void) [CALL] wasm32-port/kernel/percpu.c — WASM32_STATIC_PCPU_SIZE / pcpu_setup_first_chunk references: kernel/percpu.c:36: * percpu data. WASM32_STATIC_PCPU_SIZE = 128 KiB is generous; the kernel/percpu.c:63:#define WASM32_STATIC_PCPU_SIZE (128UL * 1024UL) kernel/percpu.c:79: const size_t static_size = WASM32_STATIC_PCPU_SIZE; kernel/percpu.c:112: * pcpu_setup_first_chunk memcpy's static_size bytes from kernel/percpu.c:122: pcpu_setup_first_chunk(ai, base); ================================================================================ 13. linux-wasm/scripts/link-vmlinux-wasm.sh — pipeline order (K10-A3 hook) ================================================================================ The post-link pipeline K10-A3 hooks into. Per post-link-pass-conventions.rst §1, current ordering is: Pass 1: scripts/wasm-add-memory.py (splice env.user_memory) Pass 2: wasm-opt --asyncify --pass-arg=asyncify-asserts Pass 3: scripts/asyncify-strip-topmost-asserts.py K10-A3 inserts wasm-bracket-initcalls.py BETWEEN Pass 1 and Pass 2 (after multi-memory splice, before asyncify instrumentation). Rationale: Pass 1 ensures the binary parses under multi-memory; Pass 2 instruments function bodies but does NOT rewrite data segments or __wasm_init_memory's body shape; K10-A3's data-segment surgery must happen pre-asyncify so asyncify's reachability analysis sees the final segment-init code shape (NOPs replacing memory.init for the dropped passive .initcall*.init segments). [SITE] linux-wasm/scripts/link-vmlinux-wasm.sh — splice + asyncify pipeline (K10-A3 insertion site): link-vmlinux-wasm.sh:165 # post-link-pass-conventions.rst §1 for the post-link hierarchy. link-vmlinux-wasm.sh:166 # link-vmlinux-wasm.sh:167 # Pass 1: splice in env.user_memory as the second imported memory. link-vmlinux-wasm.sh:168 # Pass 2: --asyncify --pass-arg=asyncify-asserts (+ removelist). link-vmlinux-wasm.sh:169 # Pass 3: scripts/asyncify-strip-topmost-asserts.py. link-vmlinux-wasm.sh:170 # link-vmlinux-wasm.sh:171 # Why the splice has to come FIRST (changed at K4): link-vmlinux-wasm.sh:172 # Multi-memory ops with explicit memory immediate referencing memory 1 link-vmlinux-wasm.sh:173 # (e.g. `memory.copy 1, 0` from kernel→user uaccess paths, and link-vmlinux-wasm.sh:174 # i32.atomic.rmw.cmpxchg targeting memory 1 from futex code) are link-vmlinux-wasm.sh:175 # already present in the wasm-ld output (LLVM emits the ops; wasm-ld link-vmlinux-wasm.sh:176 # preserves them — see Documentation/wasm/memory-model.rst on the link-vmlinux-wasm.sh:177 # "wasm-ld is multi-memory-blind, but it doesn't reject the ops" link-vmlinux-wasm.sh:178 # observation from K3). Until env.user_memory is spliced into the link-vmlinux-wasm.sh:179 # imports, the module declares only ONE memory and the multi-memory link-vmlinux-wasm.sh:180 # ops point at a nonexistent index. wasm-opt's parser fails with link-vmlinux-wasm.sh:181 # "invalid memory index" at that point. K3 happened to work because link-vmlinux-wasm.sh:182 # K3's pipeline went straight from wasm-ld to wasm-add-memory.py link-vmlinux-wasm.sh:183 # (which is multi-memory-aware: we wrote it). K4 inserts wasm-opt link-vmlinux-wasm.sh:184 # between them, so the splice has to lead. link-vmlinux-wasm.sh:185 # link-vmlinux-wasm.sh:186 # Sizing: min=1 page (64 KiB), max=65536 pages (4 GiB; wasm32 ceiling). link-vmlinux-wasm.sh:187 # shared=true because per-process user_memory MUST be SAB-backed and link-vmlinux-wasm.sh:188 # the standalone-Worker sentinel matches that shape (cf. link-vmlinux-wasm.sh:189 # hardwarejs/src/physRam.ts; K1 instantiation traps recorded the link-vmlinux-wasm.sh:190 # rationale in docs/ARCHITECTURE.md §18). link-vmlinux-wasm.sh:191 info SPLICE "$OUTPUT +env.user_memory (1..65536 pages, shared)" link-vmlinux-wasm.sh:192 SPLICE_PY="$(dirname "$0")/wasm-add-memory.py" link-vmlinux-wasm.sh:193 python3 "$SPLICE_PY" \ link-vmlinux-wasm.sh:194 "$OUTPUT" \ link-vmlinux-wasm.sh:195 --add-imported-memory env,user_memory,1,65536,shared \ link-vmlinux-wasm.sh:196 -o "$OUTPUT.spliced" link-vmlinux-wasm.sh:197 mv -f "$OUTPUT.spliced" "$OUTPUT" link-vmlinux-wasm.sh:198 link-vmlinux-wasm.sh:199 # Pass 2/3: Asyncify (K4). link-vmlinux-wasm.sh:200 # [SITE] linux-wasm/scripts/link-vmlinux-wasm.sh — --shared-memory + --no-merge-data-segments flags (K10-A1 cause analysis): link-vmlinux-wasm.sh:72:# across every Worker in the system) hence --shared-memory. link-vmlinux-wasm.sh:90: "--shared-memory" link-vmlinux-wasm.sh:102: "--no-merge-data-segments" ================================================================================ 14. linux-wasm/scripts/wasm-add-memory.py — sibling Python pass (K10-A3 template) ================================================================================ The K3 sibling. Pure-Python post-link rewriter that adds an imported memory entry to the module. K10-A3's wasm-bracket-initcalls.py is structurally similar: * Walks DATA section + the "name" subsection 9 to identify .initcall.init segments by name (K10-A1 inspect- wasm.py validated the substrate). * Walks __wasm_init_memory's function body to find each memory.init (i32.const dest, i32.const 0, i32.const N, memory.init seg_idx mem_idx) quartet. * Builds the new active overlay segments + emits NOPs in place of the original passive memory.init quartets. * Recomputes section size LEB128s. [SITE] linux-wasm/scripts/wasm-add-memory.py — file structure: wasm-add-memory.py:55:def read_uleb128(buf: bytes, off: int) -> tuple[int, int]: wasm-add-memory.py:71:def write_uleb128(val: int) -> bytes: wasm-add-memory.py:93:def parse_sections(buf: bytes): wasm-add-memory.py:111:def encode_memory_limits(min_pages: int, max_pages: int | None, shared: bool) -> bytes: wasm-add-memory.py:127:def encode_import_entry(module: str, field: str, kind: int, payload: bytes) -> bytes: wasm-add-memory.py:140:def add_imported_memory(module_bytes: bytes, wasm-add-memory.py:194:def add_local_memory(module_bytes: bytes, wasm-add-memory.py:239:def parse_mem_spec(s: str) -> tuple[int, int | None, bool]: wasm-add-memory.py:248:def main() -> int: ================================================================================ 15. linux-wasm/Documentation/wasm/post-link-pass-conventions.rst — pipeline spec ================================================================================ Module: post-link pass ordering authority. K10-A3 amends §1 with the wasm-bracket-initcalls.py insertion point; its commit body cites this section. [SITE] post-link-pass-conventions.rst — section 1 (pass order): post-link-pass-conventions.rst:3:==================================================================== post-link-pass-conventions.rst:5:==================================================================== post-link-pass-conventions.rst:17:0. Why post-link surgery exists in this port at all post-link-pass-conventions.rst:50:1. The hierarchy of post-link tools post-link-pass-conventions.rst:81:2. Substitutive vs. additive — which shape your pass is post-link-pass-conventions.rst:143:3. Mandatory properties of a post-link pass post-link-pass-conventions.rst:186:4. Naming conventions post-link-pass-conventions.rst:203:5. Listed passes (current + planned) post-link-pass-conventions.rst:206:==================================================== ============ ============ =========== ===================================== post-link-pass-conventions.rst:208:==================================================== ============ ============ =========== ===================================== post-link-pass-conventions.rst:215:==================================================== ============ ============ =========== ===================================== post-link-pass-conventions.rst:221:6. Anti-patterns ================================================================================ 16. wackywasm-tools/musl-1.2.5/src/termios/ — tcsetattr / tcgetattr (K10-B2) ================================================================================ Module: musl termios surface. K10-B2 demos use these unchanged once the kernel-side TCGETS/TCSETS path lights up via initcall dispatch. [DEF] musl-1.2.5/src/termios/{tcgetattr,tcsetattr,cfmakeraw,cfsetospeed,tcdrain,tcflow,tcflush}.c: src/termios/cfgetospeed.c:5:speed_t cfgetospeed(const struct termios *tio) src/termios/cfmakeraw.c:4:void cfmakeraw(struct termios *t) src/termios/cfsetospeed.c:6:int cfsetospeed(struct termios *tio, speed_t speed) src/termios/tcdrain.c:5:int tcdrain(int fd) src/termios/tcflow.c:4:int tcflow(int fd, int action) src/termios/tcflush.c:4:int tcflush(int fd, int queue) src/termios/tcgetattr.c:4:int tcgetattr(int fd, struct termios *tio) src/termios/tcsendbreak.c:4:int tcsendbreak(int fd, int dur) src/termios/tcsetattr.c:5:int tcsetattr(int fd, int act, const struct termios *tio) ================================================================================ 17. wackywasm-tools/musl-1.2.5/src/misc/ — pty / openpty / forkpty (K10-B1) ================================================================================ Module: musl pty surface. posix_openpt / openpty / forkpty / ptsname / grantpt / unlockpt all live here. K10-B1 demos use them unchanged once the kernel-side /dev/ptmx + /dev/pts/ paths work via subsys_initcall fire (pty_init). [DEF] musl-1.2.5/src/misc/{pty,openpty,forkpty}.c: src/misc/forkpty.c:9:int forkpty(int *pm, char *name, const struct termios *tio, const struct winsize *ws) src/misc/login_tty.c:5:int login_tty(int fd) src/misc/openpty.c:10:int openpty(int *pm, int *ps, char *name, const struct termios *tio, const struct winsize *ws) src/misc/pty.c:8:int posix_openpt(int flags) src/misc/pty.c:15:int grantpt(int fd) src/misc/pty.c:20:int unlockpt(int fd) ================================================================================ 18. wackywasm-tools/musl-src/src_overrides/ — current state at K10-A2 ================================================================================ Module: existing K7/K8/K9 src_overrides. K10-B may add a tty/wasm32/ subtree if tcsetattr/tcgetattr need musl-side wiring (e.g., to inject the pre/post __wasm_signal_dispatch_check shape K8 added for sigaction.c). Default at K10-A2 is "no new src_overrides yet"; K10-B will commit against this baseline. [SITE] wackywasm-tools/musl-src/src_overrides/ — files at K10-A2: ../wackywasm-tools/musl-src/src_overrides/env/wasm32/__init_tls.c ../wackywasm-tools/musl-src/src_overrides/internal/wasm32/syscall_ret.c ../wackywasm-tools/musl-src/src_overrides/signal/wasm32/__wasm_signal_dispatch.c ../wackywasm-tools/musl-src/src_overrides/signal/wasm32/sigaction.c ../wackywasm-tools/musl-src/src_overrides/signal/wasm32/sigsuspend.c ../wackywasm-tools/musl-src/src_overrides/signal/wasm32/sigtimedwait.c ../wackywasm-tools/musl-src/src_overrides/thread/wasm32/__set_thread_area.c ../wackywasm-tools/musl-src/src_overrides/thread/wasm32/clone.c ../wackywasm-tools/musl-src/src_overrides/thread/wasm32/pthread_sigmask.c ================================================================================ 19. hardwarejs/src/bootKernel.ts — BootKernelOptions extension (K10-B1 site) ================================================================================ Module: HardwareJS public API. K10-B1 will add an optional terminalChannel field (mirror of K9-C1's stdinChannel), allowing the embedder (xterm.js, prestrike-web LaunchMachine) to attach to the master pty. Default at K10-A2 is the K9 stdinChannel surface (legacy, EOF on read when no channel is attached). [DEF] hardwarejs/src/bootKernel.ts::BootKernelOptions interface: bootKernel.ts:45:export interface BootKernelOptions { [CALL] hardwarejs/src/bootKernel.ts — onUserOutput / stdinChannel callback shape (K10-B1 mirror): ================================================================================ 20. hardwarejs/src/kernelSpawnHandler.ts — existing tty/termios stubs (K10-B2 site) ================================================================================ Module: K5/K7/K8/K9 syscall dispatcher. K10-B2 will turn the TCGETS/TCSETS/TIOCSCTTY/TIOCSPGRP ioctl stubs into real handlers (or rather, route them to the kernel's tty subsystem once it's up via initcall dispatch). The current state is mostly "ENOSYS" or "ENOTTY" stubs; K10-A2 captures the pre-implementation state for refutation localisation. [CALL] hardwarejs/src/kernelSpawnHandler.ts — existing TCGETS/TCSETS/TIOC* references: kernelSpawnHandler.ts:94:const __NR_ioctl = 16; kernelSpawnHandler.ts:1039: case __NR_ioctl: { kernelSpawnHandler.ts:1042: * decide line-buffered vs unbuffered. Returning -ENOTTY (-25) kernelSpawnHandler.ts:1044: * path. Any other ioctl is also -ENOTTY: K7 stdio is not a tty. kernelSpawnHandler.ts:1046: ret = -25; /* -ENOTTY */ ================================================================================ 21. hardwarejs/src/worker.mjs — fd 0 read path (K10-B2 ldisc routing site) ================================================================================ Module: user-Worker syscall fast path. K9-C1 wired SYS_read fd=0 to the per-tid stdinChannel queue. K10-B2 routes fd=0 reads through the kernel's slave-fd ldisc (when the process has a controlling terminal); the stdinChannel becomes the raw-bytes input source the master tty's flip buffer consumes. Backward compat preserved for K9-C1 demos via fall-through to stdinChannel when no controlling terminal exists. [CALL] hardwarejs/src/worker.mjs — SYS_read fd=0 path (K9-C1 baseline): ================================================================================ 22. K10-A1 empirical findings (pinned for K10-A3 / K10-A4) ================================================================================ K10-A1 ran tools/k10-terminal-model-probe/inspect-wasm.py against the current vmlinux.wasm (re-runnable from build artifacts; output not committed because vmlinux.wasm is build-tree-local). Pinned substrate facts: Total data segments: varies by kernel-side .config (~14k typical, of which the ten below are the .initcall*.init segments K10-A3 mirrors) .initcall*.init segments: 10 (early, 0, 1, 2, 4, 5, 6, 7, 7s, rootfs) All ten kind: "passive" (root cause: --shared- memory in link-vmlinux-wasm.sh forces wasm-ld to emit all data segments as passive + a generated __wasm_init_memory function with cmpxchg-once memory.init opcodes) Per-segment byte size: 4..160 bytes (initcall_t = 4-byte function pointer; sizes are #initcalls × 4) Per-segment alignment: 4 bytes (initcall_t natural alignment) Total memory.init opcodes in 1849 (across all 1849 passive __wasm_init_memory: data segments; the .initcall*.init contribution is exactly 10) __wasm_init_memory function function index 4 (post-link; index: pre-asyncify; subject to drift across toolchain bumps) K10-A3 strategy B emits per-level anchor symbols pointing at new active overlay segments at kernel-controlled offsets; zero-length passive segments + NOP'd memory.init quartets remain in __wasm_init_memory for emission-shape symmetry but copy nothing (4-byte segments → memory.init src=0 size=0). The single-Worker invariant (terminal-model.rst §11.2.1) makes the mixed active/passive emission shape zero-cost at runtime. ================================================================================ 23. K10-B / K10-A3 / K10-A4 insertion-point summary ================================================================================ The following sites do NOT exist at K10-A2 close — they are where K10-B / K10-A3 / K10-A4 will land NEW code: [K10-A3] linux-wasm/scripts/wasm-bracket-initcalls.py (NEW file, mirror of wasm-add-memory.py shape) — the post-link pass that synthesises active overlay segments + emits NOPs over the original passive memory.init quartets + writes initcall_anchors.c with the chosen extents. [K10-A3] linux-wasm/wasm32-port/kernel/initcall_anchors.c (NEW file, mirror of percpu_anchors.c shape) — the C source declaring __initcall_start / __initcall_end weak char[] with section attributes the post-link pass patches into the overlay. Generated/regenerated by the post-link pass; checked in as a stub with placeholder addresses for first-build bootstrap. [K10-A3] linux-wasm/scripts/link-vmlinux-wasm.sh — invocation of wasm-bracket-initcalls.py inserted between wasm-add-memory.py and wasm-opt --asyncify (per §13 pipeline order rationale). [K10-A4] linux-wasm/wasm32-port/kernel/init.c — arch_kernel_init_pre_run_init() loses its populate_rootfs() hand-call (do_initcalls() now dispatches it via rootfs_initcall registration). Replaced with the K10-A4 single-Worker assertion (build-time-or-runtime check that vmlinux is instantiated by exactly one Worker). [K10-B1] linux-wasm/wasm32-port/kernel/ (potentially new wasm-arch shims if kernel-side pty allocation needs any; default expectation: zero, since pty.c + tty_io.c + n_tty.c are upstream-clean once initcalls fire). [K10-B1] hardwarejs/src/ttyChannel.ts (NEW; mirror of stdinChannel.ts shape) — exposes a master-pty handle the embedder attaches xterm.js or similar to. K10-B wires it into bootKernel.ts BootKernelOptions. [K10-B2] hardwarejs/src/kernelSpawnHandler.ts — TCGETS / TCSETS / TIOCSCTTY / TIOCGPGRP / TIOCSPGRP ioctl handlers (router → kernel ioctl path). [K10-B3] hardwarejs/src/worker.mjs — fd 0 read fall-through: when the process has a controlling tty, route reads via the kernel's slave-fd path; otherwise fall back to the K9-C1 stdinChannel queue. [K10-C1] hello-k10-pty.c demo + vitest exercising pty round-trip + ldisc canonical mode. [K10-C2] hello-k10-jobctrl.c demo + vitest exercising Ctrl-C → SIGINT → kill_pgrp via foreground pgrp. ================================================================================ END OF SNAPSHOT ================================================================================