Files
vlibc/src/start/tcb.h
T

118 lines
5.7 KiB
C

#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 <stddef.h>
#include <stdint.h>
#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 */