brintos

brintos / linux-shallow public Read only

0
0
Text · 9.1 KiB · a3aff66 Raw
215 lines · plain
1.. SPDX-License-Identifier: GPL-2.02 3================================================================================4binfmt_wasm: userspace WebAssembly binary container for arch/wasm325================================================================================6 7:Author: Linux-in-the-Browser project8:Status: STABLE for ABI version 19:Implementation: ``arch/wasm32/kernel/binfmt_wasm.c`` and10   ``hardwarejs/src/binfmt.ts``11 12Scope13=====14 15This document specifies the userspace binary container format that16``arch/wasm32`` understands. It is the **only** userspace binary17format supported by the wasm32 port. There is no ELF on wasm32; there18is no thin-ELF wrapper around the wasm payload either. The container19is a *raw WebAssembly module*, distinguished from a generic wasm20module by the presence of a single project-owned custom section.21 22This format is part of the public ABI of the project. The custom23section name and the payload layout described below are NOT permitted24to change in a non-breaking way. Breaking changes require bumping the25``abi_version`` field; the kernel rejects unknown ``abi_version`` with26``-ENOEXEC``.27 28Rationale29=========30 31We chose raw wasm + custom section over a thin-ELF wrapper for four32reasons:33 341. WebAssembly is the ISA. A thin-ELF wrapper would be ELF cosplay to35   make ``file(1)`` happy; the "code" is not machine code for any36   ``e_machine``.372. HardwareJS, the userspace loader, reads our metadata via stock38   ``WebAssembly.Module.customSections()``. No custom parser.393. Standard wasm tooling (``wasm2wat``, ``wasm-objdump``, ``wabt``,40   ``binaryen``) Just Works on the artifacts.414. Custom sections are a first-class wasm construct and MUST be42   ignored by runtimes that don't recognise them. Our artifacts are43   therefore also valid plain wasm modules that can be inspected in44   any wasm runtime.45 46Custom section name47===================48 49::50 51    wasm.linux.exec52 53The name is part of the ABI. The kernel matches it exactly,54case-sensitive, as a UTF-8 byte sequence.55 56(Rationale for this particular name: reverse-DNS-ish and unlikely to57collide with names other ecosystems already use — Emscripten uses58``emscripten_metadata``, the wasm spec defines ``name``, etc.)59 60Custom section payload format61=============================62 63Binary, little-endian, fixed-prefix header followed by length-prefixed64extensions. Total payload MUST be ≤ 256 bytes for ABI version 1;65larger payloads are reserved for future versions and the kernel66rejects them on ABI version 1 with ``-ENOEXEC``.67 68Header (24 bytes, fixed)::69 70    offset  size  field                  notes71    ------  ----  ---------------------  -----------------------------------72     0      4     magic                  ASCII "WLEX" (0x57 0x4c 0x45 0x58)73     4      4     abi_version            u32 LE; MUST be 174     8      4     flags                  u32 LE; reserved, MUST be 075    12      4     required_stack_size    u32 LE; bytes, 0 = use loader default76    16      4     required_heap_init     u32 LE; wasm pages (64 KiB each), 0 = loader default77    20      4     required_heap_max      u32 LE; wasm pages, 0 = loader default78 79After the header, a length-prefixed string (entry symbol name):80 81::82 83    24      1     entry_symbol_len       u8; 0..127.  See below.84    25      N     entry_symbol           N bytes of ASCII, NOT null-terminated85 86Where ``N == entry_symbol_len``. For ABI version 1, ``entry_symbol``87MUST be exactly ``"_start"`` and ``entry_symbol_len`` MUST be exactly88``6``. The field is reserved for future versions but encoded now so89that older kernels reject newer entry conventions explicitly.90 91After the entry symbol, an optional syscall capability declaration:92 93::94 95    25+N    1     cap_len                u8; 0 means "no declaration, all caps allowed"96    26+N    M     cap_bitmap             ceil((max_syscall_nr+1)/8) bytes; bit i set => syscall i required97                                          M == cap_len; if cap_len == 0, the field is omitted entirely98 99For ABI version 1, the kernel ignores ``cap_bitmap`` (treats every100binary as if it required every syscall). The field is reserved so101that adding capability enforcement in a later version is a non-102breaking change for binaries that set ``cap_len == 0``.103 104Any bytes after the capability bitmap (up to the 256-byte payload105cap) MUST be zero in ABI version 1. The kernel does NOT inspect106them but reserves the right to in future versions.107 108Field semantics109---------------110 111* ``magic``: 4-byte literal ``"WLEX"`` (Wasm Linux EXecutable). Distinguishes112  this section from accidental name reuse by other tooling.113* ``abi_version``: monotonically increasing. ABI version 1 covers the114  layout above. Breaking layout changes bump this. Backwards-compatible115  additions (e.g. consuming previously-reserved fields) MAY land116  without a bump only if a clean default exists for older binaries.117* ``flags``: reserved; the kernel rejects nonzero flags in ABI 1.118* ``required_stack_size``: hint to the loader. The loader is allowed119  to round up, never down. Zero means "use loader default" (currently120  1 MiB).121* ``required_heap_init`` / ``required_heap_max``: hints to the122  loader for the wasm memory's ``initial`` / ``maximum`` page counts.123  Zero means "use loader default" (currently 32 init / 16384 max,124  i.e. 2 MiB / 1 GiB). The loader is allowed to round up the125  initial size if the wasm module's own ``memory`` import declares126  a larger minimum.127* ``entry_symbol``: fixed at ``"_start"`` in ABI 1. The named export128  is called with no arguments after ``__wasm_call_ctors`` (if exported)129  runs.130* ``cap_bitmap``: reserved enforcement; bit i corresponds to syscall131  number i; the syscall table is the upstream x86_64 syscall table132  (mirrored at ``arch/wasm32/include/asm/unistd_64.h``).133 134Detection sequence135==================136 137``binfmt_wasm.c::load_wasm_binary()`` performs, in this order:138 1391. ``bprm->buf`` (which holds the first ``BINPRM_BUF_SIZE`` bytes of140   the file) must contain the wasm magic at offset 0:141   ``\x00 \x61 \x73 \x6d \x01 \x00 \x00 \x00``. If not, return142   ``-ENOEXEC``.1432. Walk the WebAssembly section table starting at offset 8. For each144   section: read the 1-byte section id, the LEB128 section length.145   If id == 0 (custom section), read the LEB128 name length, then146   the name. If the name is exactly ``"wasm.linux.exec"``, found.147   Otherwise skip ``length`` bytes and continue. If end-of-file is148   reached without finding the section, return ``-ENOEXEC``.1493. Within the custom section, verify ``magic == "WLEX"``. If not,150   return ``-ENOEXEC`` (another tool happened to use the same custom151   section name — extremely unlikely, but explicit).1524. Verify ``abi_version == 1``. If not, return ``-ENOEXEC``.1535. Verify ``flags == 0``. If not, return ``-ENOEXEC``.1546. Verify ``entry_symbol == "_start"`` (length 6, byte equality). If155   not, return ``-ENOEXEC``.1567. Hand off to the HardwareJS-spawn-Worker pathway with the parsed157   metadata (stack size, heap pages). The kernel does NOT itself158   load the wasm bytes into memory; HardwareJS does.159 160Step 1's check for the wasm magic prefix at file offset 0 makes161``binfmt_wasm`` the only handler that ever fires for our binaries,162even if other binfmts are registered. There is no fallback ELF163binfmt on wasm32; ABI version 1 reserves no second-chance behaviour.164 165Hand-off to HardwareJS166======================167 168After successful detection, ``binfmt_wasm.c``:169 1701. Calls the upstream helpers it needs to install the new process:171   ``begin_new_exec(bprm)``, ``setup_new_exec(bprm)``,172   ``setup_arg_pages(bprm, ...)``, ``copy_string_kernel(...)`` for173   argv/envp.1742. Builds a small argv/envp blob in the kernel's shared memory.1753. Issues the host call176   ``linux.spawn_worker(pid, blob_ptr, blob_len, argv_ptr, envp_ptr,177   required_stack_size, required_heap_init, required_heap_max)``.1784. The host call does not return on success — control transfers to179   the new Worker. The calling Worker is terminate-and-respawn'd by180   HardwareJS (the PID is preserved; the same ``task_struct`` is181   reused on the kernel side).182 183On any failure between step 1 and step 3, ``binfmt_wasm.c`` does the184upstream cleanup (``send_sig(SIGSEGV, current, 0)``) and returns the185appropriate errno.186 187Generation188==========189 190The wasm binary is produced by ``wackywasm-tools/bin/wasmld``, which191appends the ``wasm.linux.exec`` custom section as a post-link step192(simplest: a separate ``wasm-objcopy``-equivalent run, or a tiny193``wasm-meta`` helper script shipped in ``wackywasm-tools/bin/``). The194defaults are: ``abi_version = 1``, ``flags = 0``,195``required_stack_size = 0`` (use loader default), ``cap_len = 0``.196 197For ABI version 1, the entry symbol is hard-coded to ``"_start"``;198``wasmld`` always emits it as such.199 200Backward / forward compatibility201================================202 203* The kernel ABI version is bumped on any layout change. Older204  kernels reject newer ``abi_version`` values explicitly with205  ``-ENOEXEC``.206* The HardwareJS loader follows the same versioning; the kernel and207  HardwareJS MUST agree on the ABI version they implement. A version208  mismatch between the kernel and HardwareJS is treated as a fatal209  configuration error at boot, not a per-exec error.210 211History212=======213 214* ABI version 1 (this document): initial format. Spec'd 2026-05-22.215