#ifndef VLIBC_INTERNAL_START_TCB_H #define VLIBC_INTERNAL_START_TCB_H /* * vlibc — AUTHORITATIVE x86_64 TCB / thread-pointer layout (todo 3). * * This header defines the thread control block (TCB) layout ONCE, for the * whole library. Later layers reference these constants and NEVER redefine * them: * * #44 threading substrate: clones this layout for every new thread and * initializes the fenv slot per thread. * #45 sizes new-thread TLS from the static TLS block arithmetic below. * #62 ld.so: performs the dynamic-case equivalent of this bootstrap (the * FS-already-set guard in tls.c skips this code in that case). * #42 fenv: reads/writes the reserved fenv slot through the fixed * offsets below; the slot is per-thread lazy storage for the x87 * control word and MXCSR. * * ABI model — ELF TLS variant II on x86_64: * * The thread pointer (TP) is delivered by the %fs segment and points * DIRECTLY at the TCB (this is what "variant II" means; variant I, used by * e.g. 32-bit x86, points at the end of the TLS data instead). Static TLS * data live at NEGATIVE offsets from TP (%fs:N with N < 0, the LE model); * the TCB fields live at positive offsets. Slot 0 is required to hold the * TCB's own address because __builtin_thread_pointer() reads %fs:0 — that * is the entire mechanism by which C code recovers TP from a register. * * TCB field map (offsets are byte offsets from TP, i.e. from %fs:0): * * TP + 0 VLIBC_TCB_SELF_OFF TCB self pointer (TP itself). * TP + 8 VLIBC_TCB_ERRNO_OFF per-thread errno slot: 8 bytes, errno * occupies the low 4. This offset is OWNED * by src/internal/errno.h (task 1 fixed it * as an ABI constant) and is consumed * here, never re-derived. * TP + 16 VLIBC_TCB_FENV_OFF per-thread fenv reservation, 8 bytes: * +16 VLIBC_TCB_FENV_X87CW_OFF uint16 x87 control word, * reset default 0x037F (double * precision, round-to-nearest) * +18 (pad, reserved) * +20 VLIBC_TCB_FENV_MXCSR_OFF uint32 MXCSR, reset default * 0x1F80 (all exceptions masked) * TP + 24 end of the fixed layout; VLIBC_TCB_SIZE = 24. * * The layout grows FORWARD from TP if later layers need more TCB fields; * the fixed slots above are never renumbered. A DTV pointer is deliberately * NOT reserved: the static TLS model needs no DTV (every access is * %fs-relative with a link-time offset; there is no __tls_get_addr). Should * #62's dynamic TLS ever require a DTV, add it at TP + VLIBC_TCB_SIZE — * glibc's variant-II convention keeps the DTV at a negative offset relative * to a separately allocated block; vlibc deliberately keeps the simpler * grow-upward model documented here. * * Static TLS placement contract (tls.c implements this; the linker bakes * its TPOFF arithmetic against the same formula, verified empirically with * GNU ld): * * - tp = base + round_up(p_memsz, p_align), with base page-aligned. * - the PT_TLS image is copied to [base, base + p_filesz), the BSS tail * [base + p_filesz, base + p_memsz) is zeroed, and the TCB occupies * [tp, tp + VLIBC_TCB_SIZE). * - TP alignment equals p_align EXACTLY (never bumped to 16): the linker * computes tpoff = var_offset - round_up(p_memsz, p_align), so any * other rounding breaks every %fs offset when p_memsz is not a * multiple of the runtime's alignment. When the program has no PT_TLS * at all, p_align is taken as 16 and the block is TCB-only. * * TCB fields only need 8-byte alignment, and the linker guarantees * p_align >= the alignment of every TLS object, so the exact-p_align * rule is always sufficient. */ #include "../internal/errno.h" /* VLIBC_TCB_ERRNO_OFF */ #include #include #define VLIBC_TCB_SELF_OFF 0 /* VLIBC_TCB_ERRNO_OFF (8) is consumed from src/internal/errno.h. */ #define VLIBC_TCB_FENV_OFF 16 #define VLIBC_TCB_FENV_X87CW_OFF (VLIBC_TCB_FENV_OFF + 0) #define VLIBC_TCB_FENV_MXCSR_OFF (VLIBC_TCB_FENV_OFF + 4) #define VLIBC_TCB_SIZE 24 /* * New-thread TLS geometry (todo 44). All four are set by init_main_tls() * in src/start/tls.c and read by src/thread/tls.c (__vlibc_tls_setup): * * __vlibc_static_tls_size PT_TLS p_memsz of the main executable — the * one DEFAULT-VISIBILITY EXPORTED symbol of * the group: #62 grows it on dlopen, #45 reads * it when sizing new-thread TLS blocks. * __vlibc_static_tls_filesz PT_TLS p_filesz (.tdata part). * __vlibc_static_tls_align PT_TLS p_align (16 when there is no PT_TLS). * __vlibc_static_tls_template the PRIMAL TLS image address: the mapped * .tdata at p_vaddr, never the main thread's * LIVE TLS block (which mutates). In the * file-fallback path it is the main block * itself — the only copy that exists then. * * The three hidden ones keep their attribute on the definitions (as with * environ's hidden alias in src/start/environ.c); __vlibc_static_tls_size * deliberately carries none so it stays an exported ABI object. */ // NOLINTBEGIN(bugprone-reserved-identifier) extern size_t __vlibc_static_tls_size; extern uintptr_t __vlibc_static_tls_template; extern size_t __vlibc_static_tls_filesz; extern size_t __vlibc_static_tls_align; // NOLINTEND(bugprone-reserved-identifier) #endif /* VLIBC_INTERNAL_START_TCB_H */