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
|
|
@ -95,7 +95,7 @@ The galling ones. The work exists, is tested, and nothing uses it.
|
|||
|---|---|
|
||||
| **`foundations.md`** | Item 1 built. Items 2 (a shareable memory object), 3 (`AF_UNIX` in the filesystem namespace), 4 and 5 open. **Wayland stops at item 2.** |
|
||||
| ~~**`hybrid.md`**~~ | **Closed.** All four mechanisms built; H1, H2 and H3 done. H4 — the USB eviction — is *withdrawn* rather than outstanding: USB is the bus peripherals arrive on, not a peripheral, and an eviction that has to leave an identical driver behind has moved nothing. See [hybrid.md](hybrid.md), "Decided: USB stays in the kernel". |
|
||||
| **`ldls.md`** | Not started. The plan exists; L0, the loader, is the first thing that would. |
|
||||
| **`ldls.md`** | L0 done — the loader lays out and relocates 2,440 stock modules with nothing unhandled, and reports the API surface of each. L1 (the stubs), L2 (the hardware) and L3 (a packet) open. Nothing is executed yet. |
|
||||
| **Schcom v0** | 715 lines that composite, over a wire format that is superseded. Frozen: it runs, nothing more is added, and it is kept only because it is the one thing in the tree that has ever put a client's pixels on screen. |
|
||||
| **`debugger.md`** | D0 through D4 built and described in [../architecture/debugging.md](../architecture/debugging.md). Only the network transport is open — a userspace daemon bridging TCP to `/dev/dbg`, which is deliberately last. |
|
||||
|
||||
|
|
|
|||
|
|
@ -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