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
79
ldls/test/refmod.c
Normal file
79
ldls/test/refmod.c
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
// The loader's test object. **Ours, on purpose.**
|
||||
//
|
||||
// docs/plan/ldls.md rule 1 says nothing GPL ever enters an image we build, and
|
||||
// the same rule applies to the tree itself: there is no `.ko` committed here
|
||||
// and there never will be. So the thing `make ldls-check` loads is this file,
|
||||
// compiled with `-c`, which produces exactly what a `.ko` is -- an ELF64
|
||||
// relocatable object for x86-64.
|
||||
//
|
||||
// It is written to contain one of each thing the loader has to get right,
|
||||
// because a test object that only exercises the easy path is a test that
|
||||
// passes on a loader that does not work:
|
||||
//
|
||||
// - undefined symbols, of both the function and the data kind
|
||||
// - a `.text` with calls needing PC32/PLT32
|
||||
// - `.data` with an absolute pointer needing R_X86_64_64
|
||||
// - `.rodata` a pointer points into, so section-relative values matter
|
||||
// - `.bss`, which must arrive zeroed and is NOBITS with no file content
|
||||
// - a `.modinfo` shaped the way a module's is, so ko_modinfo() is exercised
|
||||
//
|
||||
// It is never linked and never run. Compiling it is the whole of its job.
|
||||
|
||||
// Things this object does not define. A real module's undefined list is
|
||||
// hundreds of names from the kernel it was built against; five is enough to
|
||||
// prove the loader reports them rather than to pretend this is a driver.
|
||||
extern int ldls_test_alloc(unsigned long size);
|
||||
extern void ldls_test_free(void *p);
|
||||
extern int ldls_test_print(const char *s);
|
||||
extern int ldls_test_counter; // undefined *data*, not a function
|
||||
|
||||
// .rodata, and something in .data pointing at it -- which is the case that
|
||||
// needs a section-relative symbol value to come out right.
|
||||
static const char greeting[] = "ldls reference module";
|
||||
const char *ldls_test_greeting = greeting;
|
||||
|
||||
// .bss. The loader must zero this rather than copy it, because NOBITS has no
|
||||
// bytes in the file to copy.
|
||||
static char scratch[256];
|
||||
char *ldls_test_scratch = scratch;
|
||||
|
||||
// .data with a known value, so a test can check the bytes survived layout.
|
||||
int ldls_test_magic = 0x1D15;
|
||||
|
||||
// .text with calls, which is where PC32 and PLT32 come from.
|
||||
int
|
||||
ldls_test_entry(void)
|
||||
{
|
||||
int n = ldls_test_alloc(sizeof(scratch));
|
||||
|
||||
if (n == 0)
|
||||
return -1;
|
||||
|
||||
ldls_test_print(greeting);
|
||||
ldls_test_counter++;
|
||||
scratch[0] = (char)ldls_test_magic;
|
||||
ldls_test_free(scratch);
|
||||
return ldls_test_counter;
|
||||
}
|
||||
|
||||
// A second function, called by the first, so at least one relocation resolves
|
||||
// inside the object and does not need the resolver at all.
|
||||
int
|
||||
ldls_test_twice(void)
|
||||
{
|
||||
return ldls_test_entry() + ldls_test_entry();
|
||||
}
|
||||
|
||||
// Shaped like a module's `.modinfo`: NUL-terminated key=value strings in their
|
||||
// own section. A real one is produced by MODULE_LICENSE() and friends; this is
|
||||
// the same bytes by hand, because the macros that emit them are not ours.
|
||||
#define MODINFO __attribute__((section(".modinfo"), used, aligned(1)))
|
||||
MODINFO static const char mi_license[] = "license=Apache-2.0";
|
||||
MODINFO static const char mi_description[] = "description=LDLS loader test object";
|
||||
MODINFO static const char mi_name[] = "name=refmod";
|
||||
|
||||
// Modules define this; ko_name() looks for it to tell a kernel module from an
|
||||
// ordinary object file.
|
||||
int __this_module;
|
||||
Loading…
Reference in a new issue