L4 was listed as "free -- H2 built all of this" and never actually tested,
which is a claim rather than a result. Tested now, and it holds:
ldlsd running ping 2/2; holds index 7 and DMA slots 3 and 4
killed interface DOWN, device released, bus mastering
cleared, DMA reclaimed -- back to vgpud's 3 blocks
ping fails honestly: sendto: -5
a second ldlsd ping 3/3, and the arena hands back the same addresses
pciuser_release_all() and netuser_release_all() sit beside each other in
exit() for the same reason, and that they apply unchanged to a foreign driver
is the whole point of having built them against our own.
With that, every milestone on ldls.md is done, so it leaves docs/plan/ for
docs/architecture/ -- the rule docs/plan/README.md states and the one that
keeps the two directories meaning different things. The new document describes
what is, not what was planned: the five rules, the four pieces, what it costs
(polling, no stack protector, locks that are no-ops), that it can be killed,
and a "what it does not do" that leads with one driver, one kernel version,
tried once.
Sixteen source files pointed at docs/plan/ldls.md and would have been dangling
the moment it moved. All repointed, and the milestone references -- which the
architecture doc deliberately does not have -- rewritten to name sections
instead, so a reader lands somewhere real.
The register row closes with it. hybrid.md freed a slot, ldls.md took it, and
it is now free again.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VQ7Qx8E1q1M1bbCq8QLHS2
239 lines
9 KiB
C
239 lines
9 KiB
C
// SPDX-License-Identifier: Apache-2.0
|
|
#pragma once
|
|
|
|
// Loading a relocatable object. docs/architecture/ldls.md, "The loader".
|
|
//
|
|
// A Linux `.ko` is an ordinary ELF64 relocatable object -- the same thing
|
|
// `gcc -c` produces -- and loading one is four steps with no mystery in any of
|
|
// them: index the sections, place the allocatable ones, resolve the undefined
|
|
// symbols against a table, and apply the relocations. There is no licence
|
|
// question anywhere in this file: ELF and the x86-64 relocation set are
|
|
// published specifications, which is the case [../docs/licensing.md] §5
|
|
// describes.
|
|
//
|
|
// **It is freestanding on purpose.** No libc, no allocation, no I/O: the
|
|
// caller hands over the whole image and a buffer, and everything happens in
|
|
// them. That is what lets the same source build as a host tool -- where a test
|
|
// takes a second -- and as the SchOS-side host, which is the discipline
|
|
// `mkschfs` and the kernel already share and the reason SchFS format bugs are
|
|
// found on the host.
|
|
//
|
|
// Types are spelled out rather than taken from a header, because the two sides
|
|
// have different ones and this file has to mean the same thing in both.
|
|
|
|
// The section table is the caller's, not ours.
|
|
//
|
|
// A fixed array needs a limit, and every limit that would fit in a struct is
|
|
// one a real object exceeds: measured over 300 modules the median is 50
|
|
// sections and the 95th percentile is 67, but an object built with
|
|
// -ffunction-sections runs to thousands -- nvidia-modeset has 7,069. Rather
|
|
// than pick a number that is generous until it is not, the caller says how
|
|
// many it is prepared for, and ko_sections_needed() says how many that is.
|
|
//
|
|
// A suggested size for a fixed table on the SchOS side, where allocating is
|
|
// less convenient. It is a caller's choice and not the loader's limit.
|
|
#define KO_SECHINT 128
|
|
|
|
// Undefined symbols the report will hold. Measured over the same corpus the
|
|
// median is 25 and the worst seen is 245, so this is room to spare -- and
|
|
// `undef_lost` says so rather than the list quietly ending.
|
|
#define KO_MAXUNDEF 512
|
|
|
|
// --- ELF64, the relocatable subset ------------------------------------------
|
|
//
|
|
// include/elf.h has `elfhdr` and `proghdr`, which are the ET_EXEC half. None of
|
|
// what a relocatable object needs is there, so it is here.
|
|
|
|
struct ko_ehdr {
|
|
unsigned char e_ident[16];
|
|
unsigned short e_type;
|
|
unsigned short e_machine;
|
|
unsigned int e_version;
|
|
unsigned long long e_entry;
|
|
unsigned long long e_phoff;
|
|
unsigned long long e_shoff;
|
|
unsigned int e_flags;
|
|
unsigned short e_ehsize;
|
|
unsigned short e_phentsize;
|
|
unsigned short e_phnum;
|
|
unsigned short e_shentsize;
|
|
unsigned short e_shnum;
|
|
unsigned short e_shstrndx;
|
|
};
|
|
|
|
struct ko_shdr {
|
|
unsigned int sh_name;
|
|
unsigned int sh_type;
|
|
unsigned long long sh_flags;
|
|
unsigned long long sh_addr;
|
|
unsigned long long sh_offset;
|
|
unsigned long long sh_size;
|
|
unsigned int sh_link;
|
|
unsigned int sh_info;
|
|
unsigned long long sh_addralign;
|
|
unsigned long long sh_entsize;
|
|
};
|
|
|
|
struct ko_sym {
|
|
unsigned int st_name;
|
|
unsigned char st_info;
|
|
unsigned char st_other;
|
|
unsigned short st_shndx;
|
|
unsigned long long st_value;
|
|
unsigned long long st_size;
|
|
};
|
|
|
|
struct ko_rela {
|
|
unsigned long long r_offset;
|
|
unsigned long long r_info;
|
|
long long r_addend;
|
|
};
|
|
|
|
#define KO_ET_REL 1
|
|
#define KO_EM_X86_64 62
|
|
|
|
#define KO_SHT_PROGBITS 1
|
|
#define KO_SHT_SYMTAB 2
|
|
#define KO_SHT_STRTAB 3
|
|
#define KO_SHT_RELA 4
|
|
#define KO_SHT_NOBITS 8
|
|
|
|
#define KO_SHF_WRITE 0x1
|
|
#define KO_SHF_ALLOC 0x2
|
|
#define KO_SHF_EXECINSTR 0x4
|
|
|
|
#define KO_SHN_UNDEF 0
|
|
#define KO_SHN_ABS 0xfff1
|
|
#define KO_SHN_COMMON 0xfff2
|
|
|
|
#define KO_R_SYM(i) ((unsigned int)((i) >> 32))
|
|
#define KO_R_TYPE(i) ((unsigned int)((i) & 0xffffffffu))
|
|
|
|
// The x86-64 relocations a compiled object actually contains. Anything else is
|
|
// refused by name rather than ignored -- a relocation silently skipped is a
|
|
// wrong address found much later.
|
|
#define KO_R_X86_64_NONE 0
|
|
#define KO_R_X86_64_64 1
|
|
#define KO_R_X86_64_PC32 2
|
|
#define KO_R_X86_64_PLT32 4
|
|
#define KO_R_X86_64_32 10
|
|
#define KO_R_X86_64_32S 11
|
|
#define KO_R_X86_64_PC64 24
|
|
|
|
// --- what the loader knows about a section ----------------------------------
|
|
|
|
struct ko_section {
|
|
const char *name;
|
|
unsigned int type;
|
|
unsigned long long flags;
|
|
unsigned long long size;
|
|
unsigned long long align;
|
|
const unsigned char *data; // 0 for NOBITS
|
|
int alloc; // takes space at run time
|
|
unsigned long long addr; // where it will run, once laid out
|
|
unsigned char *where; // where it is right now, once laid out
|
|
};
|
|
|
|
// Resolving a name to an address. Returns 0 and fills `out` on success, or
|
|
// non-zero to say it does not know it -- which is not an error at L0, it is
|
|
// the answer we are asking for.
|
|
typedef int (*ko_resolver)(void *ctx, const char *name,
|
|
unsigned long long *out);
|
|
|
|
struct ko {
|
|
const unsigned char *img;
|
|
unsigned long long len;
|
|
|
|
const struct ko_ehdr *eh;
|
|
const unsigned char *shtab; // raw, indexed with e_shentsize
|
|
int nsh;
|
|
|
|
const char *shstr; // section name strings
|
|
const struct ko_sym *sym;
|
|
int nsym;
|
|
const char *str; // symbol name strings
|
|
|
|
struct ko_section *sec; // the caller's, nsh entries used
|
|
|
|
const char *undef[KO_MAXUNDEF];
|
|
int nundef;
|
|
int undef_lost; // more than KO_MAXUNDEF of them
|
|
|
|
unsigned long long span; // bytes ko_layout needs
|
|
int laid_out;
|
|
|
|
// How many relocations of each type the object contains, counted by
|
|
// ko_reltypes(). The question "what does the loader actually have to
|
|
// handle" has an exact answer per module, and guessing at it is how a
|
|
// loader ends up refusing a driver on its last relocation.
|
|
unsigned int reltype[64];
|
|
unsigned int reltype_other;
|
|
unsigned int nrel;
|
|
|
|
char err[128];
|
|
};
|
|
|
|
// How many `struct ko_section` this object needs. -1 if it is not an ELF64
|
|
// object at all, which is worth knowing before allocating anything for it.
|
|
int ko_sections_needed(const void *image, unsigned long long len);
|
|
|
|
// Validate and index, using `sec` for the section table. Returns 0, or -1 with
|
|
// ko_err() set -- including when `nsec` is too small, which says how many were
|
|
// needed rather than just refusing.
|
|
int ko_open(struct ko *k, const void *image, unsigned long long len,
|
|
struct ko_section *sec, int nsec);
|
|
|
|
// Bytes ko_layout will need. Valid after ko_open.
|
|
unsigned long long ko_span(const struct ko *k);
|
|
|
|
// Place every allocatable section into `mem`, as if it will run at `base`.
|
|
// The two differ when the object is staged somewhere before being mapped;
|
|
// pass base == (address of mem) when they do not.
|
|
int ko_layout(struct ko *k, void *mem, unsigned long long cap,
|
|
unsigned long long base);
|
|
|
|
// Count the relocations by type, without applying any. Valid after ko_open.
|
|
void ko_reltypes(struct ko *k);
|
|
|
|
// Apply every relocation. Undefined symbols go to `r`; a name it does not know
|
|
// makes this fail, naming the first one.
|
|
int ko_relocate(struct ko *k, ko_resolver r, void *ctx);
|
|
|
|
// Neutralise every `%gs:`-relative load of a named symbol.
|
|
//
|
|
// Linux builds modules with `-mstack-protector-guard-reg=gs`, so the stack
|
|
// canary is fetched through the GS segment:
|
|
//
|
|
// mov %gs:__ref_stack_chk_guard(%rip), %rax (prologue)
|
|
// sub %gs:__ref_stack_chk_guard(%rip), %rax (epilogue, then jne)
|
|
//
|
|
// That is an ABI between the kernel and its own per-CPU area, and a host which
|
|
// is not that kernel cannot provide it. On SchOS the GS base belongs to the
|
|
// kernel, so a user-mode read of it faults on a kernel address -- which is
|
|
// exactly where 8139too's probe stopped the first time it ran there.
|
|
//
|
|
// So the instruction is rewritten to zero the destination register. The canary
|
|
// becomes zero, the check at the end of the function compares zero against
|
|
// zero, and `__stack_chk_fail` is never reached. What is lost is a hardening
|
|
// measure that was never going to work here; what is gained is a driver that
|
|
// runs.
|
|
//
|
|
// Call it **after** ko_relocate(), which will have written a displacement into
|
|
// bytes this then overwrites entirely. Returns how many it patched, or -1 if
|
|
// it found something at that offset it did not recognise -- in which case
|
|
// nothing is modified, because a loader that half-understands an instruction
|
|
// should not be editing it.
|
|
int ko_defuse_gs(struct ko *k, const char *sym);
|
|
|
|
// A value from the `.modinfo` section -- "license", "description", "author",
|
|
// "alias". Returns 0 if the object has no such key.
|
|
//
|
|
// Worth having at L0 rather than later: the first thing anyone should be able
|
|
// to ask about a module is what licence it declares, and that is a string in
|
|
// the file rather than anything that needs loading.
|
|
const char *ko_modinfo(const struct ko *k, const char *key);
|
|
|
|
// The module's own name, from `.gnu.linkonce.this_module`, or 0.
|
|
const char *ko_name(const struct ko *k);
|
|
|
|
const char *ko_err(const struct ko *k);
|