SchOS/ldls/ko.h
Nexoniarz 26bca020b6 licence: SchOS is GPL-2.0-or-later, no longer Apache-2.0
Nexoniarz is the sole author and SchOS was never published under
Apache-2.0, so this is a straight relicence. LICENSE is the FSF GPLv2 text,
every first-party SPDX header moves to GPL-2.0-or-later, and NOTICE,
README.md, docs/licensing.md and the site say so. projects/schfs-linux
follows the rest of the tree.

The GPL gives copyleft: a fork has to ship its source. It also lifts the
old "Linux drivers, never" rule, since GPL-2.0-only code combines with
GPL-2.0-or-later. "Or later" leaves room for GPLv3, but one binary still
has to pick one version, so docs/licensing.md now keeps Apache-2.0 and
GPLv3 code out of the kernel to keep Linux code possible there. BSD
4-clause code goes from "avoid" to "no", because the advertising clause
is GPL-incompatible.

LDLS still ships no .ko. That boundary is now project policy rather than
a legal requirement, and ldls.md, devices.md and licensing.md section 6
say so. refmod declares license=GPL, and ldls-check passes (27 checks).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 08:48:45 +02:00

268 lines
10 KiB
C

// SPDX-License-Identifier: GPL-2.0-or-later
#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);
// Turn every *other* `%gs:`-relative reference into an ordinary one.
//
// The stack guard is one per-CPU variable; there are others, and `current` is
// the one every driver touches. `mov %gs:const_current_task(%rip), %r8` reads
// a pointer out of the kernel's per-CPU area, and a host that is not that
// kernel has no such area -- so on SchOS it reads a kernel address from user
// mode and faults.
//
// Defusing is not available here: the guard's *value* is arbitrary, so zeroing
// a register is a correct answer, and `current`'s is not -- the driver will
// dereference it.
//
// What works instead is that **a per-CPU variable in a single-threaded host is
// just a variable**. Taking the `%gs` off leaves the same instruction reading
// the same width in the same direction, from storage the embedder supplies for
// that name. Eight bytes become seven and a nop, and the driver never learns
// the difference.
//
// `percpu` is asked for an address for each name and should return the same
// one every time it is asked for that name -- and should initialise it, since
// the driver now reads its *contents*: zero is right for a counter and wrong
// for `current`. Returning 0 refuses the object.
//
// Call after ko_relocate() and after ko_defuse_gs(), for the same reason.
// Returns how many it rewrote, or -1.
int ko_fixup_percpu(struct ko *k,
unsigned long long (*percpu)(void *ctx, const char *name),
void *ctx);
// 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);