// SPDX-License-Identifier: Apache-2.0 // // Stubs: giving every undefined symbol an address that says who it was. // // docs/plan/ldls.md milestone L1. L0 measured 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);