ldls: the loader, and what it measured
docs/plan/ldls.md milestone L0. ldls/ko.c indexes the sections of an ELF64
relocatable object, places the allocatable ones, resolves undefined symbols
against a caller's table and applies the relocations. Nothing is executed.
It is freestanding -- no libc, no allocation, no I/O -- so the same source
builds as a host tool now and as the SchOS-side host later. That is the
arrangement mkschfs and the kernel already share, and for the same reason:
the failures at this layer are format failures, and one found on the host
costs a second instead of a boot.
$ ldls-inspect 8139too.ko
module 8139too
license GPL
loads to 33988 bytes
relocations: 1980
R_X86_64_64 363, PC32 991, PLT32 428, 32S 169, PC64 29
undefined symbols: 94
stops at: unresolved: __fentry__
The licence is printed before anything is loaded, because it is the one
property that decides whether the object may be on the machine at all and it
is a plain string in .modinfo. Rule 1 is easier to keep when the answer is on
screen.
94 symbols is the measured API surface of 8139too on 6.18.46 -- the list,
exactly, rather than an estimate -- and it is L1's work queue. It is smaller
than it looks: __fentry__, __stack_chk_fail, __ref_stack_chk_guard,
__x86_return_thunk and __fortify_panic are compiler and instrumentation
artefacts that stub out in a line each.
Swept over every module in a stock 6.18.46 tree: 2,440 objects, all of them
laid out and relocated to completion, none with a relocation type the loader
does not handle. That sweep is also what retired the fixed section table --
nvidia-modeset has 7,069 sections and 74,305 relocations, against a median of
50 -- so the caller supplies the table and there is no limit to run into
rather than a generous one that fails later.
Loading is not running, and the doc says so: not one symbol is implemented,
and the distance between "relocates" and "transmits a packet" is the rest of
the plan.
No .ko is in this tree and .gitignore now refuses one outright. make
ldls-check runs 18 checks against ldls/test/refmod.c, which is ours, written
to contain one of each thing the loader has to get right.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VQ7Qx8E1q1M1bbCq8QLHS2
This commit is contained in:
parent
5263150d3d
commit
1be5f3a704
9 changed files with 1296 additions and 2 deletions
|
|
@ -144,7 +144,7 @@ One device, one process, claimed and released, on the `/dev/pci` discipline.
|
|||
|
||||
| | | |
|
||||
|---|---|---|
|
||||
| **L0** | **The loader.** Parse a real `.ko`, lay it out, relocate it, and **print every unresolved symbol**. Nothing executes. The output is the measured API surface. | |
|
||||
| **L0** | **The loader.** Parse a real `.ko`, lay it out, relocate it, and **print every unresolved symbol**. Nothing executes. The output is the measured API surface. | **done** — `ldls/ko.c`, `make ldls-check` |
|
||||
| **L1** | **Stubs.** Every symbol resolves, to a function that logs its name and stops. `init_module` runs and dies at the first real call — which yields the *call order*, the second thing you need and the other thing you cannot guess. | |
|
||||
| **L2** | **The hardware.** `probe()` receives a working `struct pci_dev`, maps its BARs, allocates coherent DMA and registers an interrupt, all through `/dev/pci`. The driver reaches its device. | |
|
||||
| **L3** | **A packet.** The process-backed netdev, wired to the driver's transmit and receive paths. **`ping` over a Linux driver.** This is the milestone that proves the whole idea and the one that cannot be skipped. | |
|
||||
|
|
@ -178,6 +178,72 @@ run.
|
|||
|
||||
---
|
||||
|
||||
## L0, and what it measured
|
||||
|
||||
`ldls/ko.c` is the loader: index the sections, place the allocatable ones,
|
||||
resolve the undefined symbols against a table, apply the relocations. It is
|
||||
freestanding — no libc, no allocation, no I/O — so the same source builds as a
|
||||
host tool today and as the SchOS-side host later, which is the arrangement
|
||||
`mkschfs` and the kernel already share.
|
||||
|
||||
```
|
||||
$ ldls-inspect 8139too.ko
|
||||
119288 bytes, 55 sections, 363 symbols
|
||||
module 8139too
|
||||
license GPL
|
||||
describes RealTek RTL-8139 Fast Ethernet driver
|
||||
loads to 33988 bytes
|
||||
|
||||
relocations: 1980
|
||||
R_X86_64_64 363
|
||||
R_X86_64_PC32 991
|
||||
R_X86_64_PLT32 428
|
||||
R_X86_64_32S 169
|
||||
R_X86_64_PC64 29
|
||||
|
||||
undefined symbols: 94
|
||||
...
|
||||
stops at: unresolved: __fentry__
|
||||
```
|
||||
|
||||
**The licence is printed first, before anything is loaded**, because it is the
|
||||
one property that decides whether the object may be on this machine at all and
|
||||
it is a plain string in `.modinfo`. Rule 1 is easier to keep when the answer is
|
||||
on screen.
|
||||
|
||||
### What the measurement says
|
||||
|
||||
**94 symbols** is the API surface of `8139too` on Linux 6.18.46. Not an
|
||||
estimate — the list, exactly, and it is L1's work queue. It is also smaller
|
||||
than it looks, because a good fraction of it is not API at all: `__fentry__`,
|
||||
`__stack_chk_fail`, `__ref_stack_chk_guard`, `__x86_return_thunk` and
|
||||
`__fortify_panic` are compiler and instrumentation artefacts that stub out in a
|
||||
line each.
|
||||
|
||||
**Five relocation types**, and a corpus run says that is the whole set: over
|
||||
**2,440 real modules** from a stock 6.18.46 tree, every one lays out and
|
||||
relocates to completion with nothing unhandled. That includes `nvidia-modeset`
|
||||
at 7,069 sections and 74,305 relocations, which is what retired the loader's
|
||||
fixed section table — the caller supplies it now, so there is no limit to run
|
||||
into rather than a generous one.
|
||||
|
||||
Relocating against stubs is a separate question from listing what is missing,
|
||||
and it is the one that says the loader is finished: with nothing resolved, the
|
||||
first unknown name stops everything and no relocation type past it is ever
|
||||
reached.
|
||||
|
||||
### Nothing foreign is in this tree
|
||||
|
||||
`make ldls-check` runs the loader against `ldls/test/refmod.c` — ours, compiled
|
||||
with `-c`, written to contain one of each thing the loader has to get right:
|
||||
undefined functions *and* undefined data, `.text` with calls, `.data` holding a
|
||||
pointer into `.rodata`, a `.bss` that must arrive zeroed, and a `.modinfo`
|
||||
shaped the way a module's is. The modules above were read from the host's own
|
||||
`/nix/store` and never copied here; `.gitignore` refuses `*.ko` outright so
|
||||
that cannot happen by accident.
|
||||
|
||||
---
|
||||
|
||||
## What it will not do
|
||||
|
||||
- **It is not a security boundary, and here that is worse than usual.** There
|
||||
|
|
@ -208,6 +274,11 @@ run.
|
|||
costs. A process has different rules, and where they differ the shim will be
|
||||
wrong in ways that show up as rare races rather than as failures to build.
|
||||
This is the deepest technical risk in the whole plan.
|
||||
- **Loading is not running.** L0 proves 2,440 modules can be laid out and
|
||||
relocated. That says the *format* is handled and says nothing whatever about
|
||||
whether any of them would work: not one symbol is implemented, and the
|
||||
distance between "relocates" and "transmits a packet" is the entire rest of
|
||||
this plan.
|
||||
- **It will not support "Linux drivers".** It will support *one driver, on one
|
||||
kernel version, that has been tried*. Anything beyond that is a claim, and
|
||||
the list of what has actually been run belongs in the architecture document
|
||||
|
|
|
|||
Loading…
Reference in a new issue