brintos

brintos / linux-shallow public Read only

0
0
Text · 11.1 KiB · 2b07c18 Raw
243 lines · plain
1.. SPDX-License-Identifier: GPL-2.02 3====================================================================4Conventions for post-link wasm rewrite passes5====================================================================6 7This document is the single home for the project's growing set of8"post-link surgical rewrite" tools that operate on ``vmlinux.wasm``9between ``wasm-ld`` and the runnable artifact handed to HardwareJS.10 11The conventions in this document are referenced from12``docs/ARCHITECTURE.md`` §15 debt rows that depend on a post-link13pass, and any new post-link tool MUST be reviewed against the rules14here.15 16---------------------------------------------------------------------170. Why post-link surgery exists in this port at all18---------------------------------------------------------------------19 20The kernel image is built from a 6.12 LTS source tree that we do21not fork (§16 PR acceptance criteria). Several invariants the22kernel source assumes are normally upheld by:23 24* a GNU linker script (``vmlinux.lds.S``) consumed by ``ld``;25* ``wasm-ld`` and the rest of the LLVM toolchain;26* Binaryen's ``wasm-opt`` pass collection;27* the V8 / SpiderMonkey runtime.28 29Three of those four have gaps that we satisfy without modifying30upstream sources:31 32* ``wasm-ld`` does not consume GNU linker scripts. Boundary33  symbols like ``__per_cpu_start`` / ``__per_cpu_end`` /34  ``__initcall*_start/end`` / ``__setup_start/end`` therefore35  cannot be defined via ``vmlinux.lds.S``.36* ``wasm-opt`` has occasional bugs (e.g. the topmost-runtime37  asyncify-asserts FIXME — see ``sched-model.rst`` §3) whose fix38  upstream is months-to-years out, but whose effect on the kernel39  is immediate.40* The wasm module has no syntax for declaring a second imported41  memory in clang-emitted ``.wat`` form, despite Binaryen +42  V8 accepting the binary form. (See ``mm-model.rst``.)43 44For each, the K-phase boundary is "kernel proceeds further" and45not "upstream toolchain catches up," so the project lands a46surgical post-link rewrite that yields the desired ``vmlinux.wasm``47shape without forking the source.48 49---------------------------------------------------------------------501. The hierarchy of post-link tools51---------------------------------------------------------------------52 53::54 55  vmlinux.a  (LLVM IR / wasm objects, no symbols outside arch/wasm32)56     |57     v   wasm-ld + scripts/link-vmlinux-wasm.sh58  vmlinux.wasm.linked59     |60     v   (optional) wasm-opt --asyncify --pass-arg=asyncify-asserts61  vmlinux.wasm.asyncify-with-asserts62     |63     v   (optional) scripts/asyncify-strip-topmost-asserts.py64  vmlinux.wasm.asyncify65     |66     v   (planned K5/K6) scripts/wasm-percpu-bracket.py67  vmlinux.wasm.percpu68     |69     v   (planned K5/K6) scripts/wasm-initcall-bracket.py70  vmlinux.wasm.initcall71     |72     v   scripts/wasm-add-memory.py73  vmlinux.wasm        <-- the artifact HardwareJS loads74 75Pass order matters; the order above is fixed by an invariant each76pass would otherwise violate. New passes MUST declare which pass77they depend on (read-after) and which depend on them (write-before)78in their module docstring.79 80---------------------------------------------------------------------812. Substitutive vs. additive — which shape your pass is82---------------------------------------------------------------------83 84Every post-link pass falls into one of two shapes:85 86**Substitutive (the default, lower-risk):**87 88  Modifies bytes in-place, **never changing byte count**. Code/data89  section sizes, function-body sizes, and absolute offsets of every90  byte AFTER the rewrite point are preserved. This means: no LEB12891  re-encoding of section lengths, no function-body length92  recomputation, no relocation cascade across the rest of the93  binary, no DWARF offset shifts.94 95  ``scripts/asyncify-strip-topmost-asserts.py`` is the current96  representative. Its strategy: locate the 9-byte assertion pattern97  ``[0x23 LEB(state) 0x20 LEB(oldState) 0x47 0x04 0x40 0x00 0x0B]``98  and overwrite all 9 bytes with ``0x01`` (nop). Stack effect is99  preserved (``global.get`` and ``local.get`` push 2 i32s; the100  ``i32.ne`` / ``if`` / ``unreachable`` / ``end`` chain consumes101  them; replacing the WHOLE sequence with nops drops both pushes102  too, net stack effect = 0).103 104  Same-byte-count nop substitution preserving stack semantics is105  the **preferred shape** for any rewrite that is logically a106  "delete this fragment" operation. Pads with nops; arithmetic107  unchanged; offsets unchanged.108 109**Additive (acceptable only at section boundaries):**110 111  Inserts new content. Always changes byte count of the section112  it inserts into, which in turn re-encodes the section's LEB128113  length and shifts the section's start offset (and every byte114  after it). The pass MUST then update any offsets that crossed115  the insert point: function body length prefixes, table /116  memory / global / export references that index by ordinal (not117  affected) vs. by file offset (affected).118 119  ``scripts/wasm-add-memory.py`` is the current representative.120  It splices a second imported memory into the IMPORTS section121  (section 2). This is acceptable because:122 123  * The IMPORTS section is at the START of the module; nothing124    in CODE / DATA / EXPORTS references it by file offset.125  * All section indexes (memory 0, memory 1) are determined by126    ordinal within the new IMPORTS section, not by file offset,127    so referenced memidx values in the CODE section stay valid.128  * Names are the cross-module ABI (per129    ``docs/ARCHITECTURE.md`` §0(c)) so identity preservation is130    automatic.131 132  Additive insertion is permitted only at section boundaries133  where no internal offsets reference past the insert point.134  The pass MUST document this property in its module docstring.135 136**A pass that is logically substitutive should never expand137to additive without an explicit justification in its module138docstring** of why the additive form is necessary. Expanding to139additive cascades the cost of every following pass (each must140now recompute offsets from the new section sizes).141 142---------------------------------------------------------------------1433. Mandatory properties of a post-link pass144---------------------------------------------------------------------145 146A new post-link tool must:147 1483.1. **Produce a deterministic output** for a given input. Two149     runs of the pass against the same input bytes must yield150     byte-identical output. CI cross-checks this by re-running151     the pass and comparing.152 1533.2. **Fail loudly on input-shape mismatch.** If the pass relies154     on a specific Binaryen / wasm-ld / clang output shape (an155     export-name convention, a section ordering, a function-body156     prologue pattern), it must detect a mismatch and exit157     non-zero with a message that points the maintainer at the158     toolchain version expected vs. observed. Do not "best-159     effort" past an unexpected shape — the symptom would160     surface five subsystems later in the kernel boot.161 1623.3. **Ship a sibling probe under** ``linux-wasm/tools/``. The163     probe compiles a 1-3-function toy module, runs the same164     post-link pipeline on it, and asserts the pass's contract165     against the toy output. The probe runs in CI on every166     build. This catches Binaryen / clang / wasm-ld bumps that167     change the pass's input shape BEFORE vmlinux.wasm mysteriously168     traps. See ``docs/ARCHITECTURE.md`` §15.1 for the broader169     pattern.170 1713.4. **Document its pass-order dependencies** (read-after,172     write-before) in the module docstring.173 1743.5. **Declare its shape** in the module docstring: substitutive175     (same byte count) or additive (boundary insertion). If176     additive, declare which section it inserts into and why no177     internal-offset references cross the insert point.178 1793.6. **Reference a §15 row** that owns the debt the pass exists180     to satisfy. If the upstream gap closes, the §15 row closes181     and the pass either retires or moves into permanent182     toolchain-glue. (See §15.1 "Probe-driven debt retirement"183     for the retirement protocol.)184 185---------------------------------------------------------------------1864. Naming conventions187---------------------------------------------------------------------188 189* Live in ``linux-wasm/scripts/``.190* Filename starts with ``wasm-`` for additive / structural rewrites191  (``wasm-add-memory.py``, planned ``wasm-percpu-bracket.py``,192  ``wasm-initcall-bracket.py``).193* Filename starts with ``asyncify-`` for passes that operate on194  Asyncify-pass output (``asyncify-strip-topmost-asserts.py``,195  planned ``asyncify-whitelist.py``).196* Filename starts with ``link-`` for build-flow drivers197  (``link-vmlinux-wasm.sh``).198* Sibling probes live in199  ``linux-wasm/tools/k{N}-{pass-name}-probe/`` where N is the200  K-phase that introduced the pass.201 202---------------------------------------------------------------------2035. Listed passes (current + planned)204---------------------------------------------------------------------205 206==================================================== ============ ============ =========== =====================================207Pass                                                  Phase intro  Shape        Sibling probe   §15 row208==================================================== ============ ============ =========== =====================================209``scripts/wasm-add-memory.py``                       K1           additive     —           (none; permanent glue)210``scripts/wasm-inspect.py``                          K2           read-only    —           (none; read-only diagnostic)211``scripts/asyncify-strip-topmost-asserts.py``        K4           substitutive ``k4-asyncify-asserts-probe``   "asyncify-asserts enabled with named topmost-strip exceptions"212``scripts/asyncify-whitelist.py``                    K4           generator    —           (generates ``arch/wasm32/asyncify-whitelist.txt``)213``scripts/wasm-percpu-bracket.py`` (planned)         K5/K6        substitutive (planned)   "Linker-script-anchor symbols stubbed"214``scripts/wasm-initcall-bracket.py`` (planned)       K6           substitutive (planned)   "vmlinux.lds.S doesn't land __initcall_* ordering"215==================================================== ============ ============ =========== =====================================216 217When adding a new pass, append a row here in the same commit, or218the commit is incomplete (mirrors the §15 invariant).219 220---------------------------------------------------------------------2216. Anti-patterns222---------------------------------------------------------------------223 224* **Do not** rewrite by re-parsing → modifying → re-emitting the225  whole module. That cascades offsets and breaks every following226  pass. Operate on the smallest possible byte window.227 228* **Do not** rely on Binaryen or wasm-ld output details that229  aren't tested by the sibling probe. Toolchain bumps will230  surprise you and the surprise will arrive in vmlinux behavior,231  not in the pass itself.232 233* **Do not** silently no-op when input shape is unexpected. Fail234  loudly per §3.2.235 236* **Do not** combine multiple unrelated rewrites into one pass.237  Each pass owns one logical transform; pass ordering in §1 is238  how they compose.239 240* **Do not** mutate the input file in place. Always write a new241  output file. Build systems retry on partial failure; in-place242  mutation makes that retry corrupt the build.243