SchOS/ldls/stub.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

55 lines
2.4 KiB
C

// SPDX-License-Identifier: GPL-2.0-or-later
//
// Stubs: giving every undefined symbol an address that says who it was.
//
// docs/architecture/ldls.md, rule 5. The loader measures what a module asks for; this is
// what answers. Every name resolves to a small block of generated code, and
// when the driver calls it the block says which name it was and what arguments
// it came with.
//
// The value is not the logging, it is the **order**. A module's undefined
// symbol list is a set and tells you nothing about sequence; what a driver
// does at init, and in what order, is the other thing that cannot be guessed
// and it falls out of running the thing.
//
// Freestanding, like ldls/ko.c, so the SchOS-side host gets the same code.
#define STUB_SIZE 32 // bytes per trampoline, generously aligned
// What a stub call reports. The embedder provides this; stub.c calls it from
// the thunk with the original argument registers intact, which is what lets a
// stub for `_printk` show the string the driver actually passed.
//
// Whatever it returns becomes the stub's return value, so returning 0 lets a
// driver carry on past a call and reveal the next one -- which is worth far
// more than stopping at the first.
unsigned long ldls_stub(long idx, unsigned long *args);
struct stubs {
unsigned char *code; // STUB_SIZE * n bytes, must be executable
int n;
};
// Build `n` trampolines into `code`. Each one, when called, ends up in
// ldls_stub() with its index.
//
// `code` has to be executable memory; this only writes the bytes.
void stub_build(struct stubs *s, unsigned char *code, int n);
// The address of stub `i`, to hand to the loader as a symbol value.
unsigned long long stub_addr(const struct stubs *s, int i);
// Some names must not be logged, because they are called so often that the log
// becomes the only thing you see, and because they have calling conventions of
// their own that a logging thunk would break.
//
// `__fentry__` is ftrace's hook, emitted at the top of every function, and it
// is required to preserve every register. `__x86_return_thunk` is the retpoline
// return. Both are correctly implemented as a bare `ret`, which is also the
// fastest thing they could possibly be.
//
// Returns 1 if the name got a bare-ret stub instead of a logging one.
int stub_is_bare(const char *name);
// Overwrite one stub with that bare `ret`.
void stub_make_bare(unsigned char *p);