Thread-Local Storage
Thread-local storage is addressed through the GS0 segment register. TLS references that remain inside the program and have a link-time constant offset use the local-exec model. TLS references that cross dynamic object boundaries, may be interposed, or otherwise require runtime resolution use the TLSDESC model.
TLS Base Model
GS0 is the TLS base register. A TLS offset is translated by GS0 segment pre-translation before page-table translation. The local-exec model encodes a link-time constant byte offset from the GS0 base; TLSDESC supplies runtime resolution when the reference crosses a dynamic-object boundary or remains interposable.
TLS Relocation Families
| Model | Relocations |
|---|---|
| local-exec | R_BEDROCK_TLS_OFFSET32S; R_BEDROCK_TLS_OFFSET64 |
| TLSDESC | R_BEDROCK_TLSDESC_GOTPCREL32S; R_BEDROCK_TLSDESC_GOTPCREL64; R_BEDROCK_TLSDESC_CALL; R_BEDROCK_TLSDESC |
Taking the address of a TLS object returns an ordinary pre-segment pointer to that thread instance. Every ABI-permitted GS0 and ordinary-data path consuming that coordinate resolves it to the same byte of that TLS object. Copying a TLS pointer to another thread retains its originating thread instance; the pointer becomes invalid when its originating thread terminates or the defining shared object is unloaded.
Model Selection
-
A TLS symbol defined in the same program image may use local-exec addressing when its byte offset from GS0 is link-time constant.
-
A TLS reference from a shared object or to an interposable TLS symbol uses TLSDESC.
-
The linker may relax a TLSDESC sequence to local-exec when symbol binding and final placement make the GS0-relative offset constant.
-
A source expression
S+kuses the descriptor forS; after resolution, the caller addskto the returned offset. The descriptor relocations encode the base symbolS.
TLSDESC Entry
TLSDESC Entry Layout
| Offset | Field | Type | Meaning |
|---|---|---|---|
+0x00 |
function |
u64 |
near-call address of the resolver or fast function |
+0x08 |
argument |
u64 |
signed direct GS0-relative offset or resolver-specific opaque token |
Each entry occupies 16 bytes in .got and has 16-byte alignment. The loader resolves the symbol binding, writes both words, and completes any memory ordering required to publish the pair before application code can observe it. Both words are immutable after publication and may subsequently be protected by PT_GNU_RELRO. The resolver and fast function may use loader-private near-call addresses.
The argument word belongs to the implementation of the selected function. A fast function may load [R0+8] and return it directly. An opaque token is loader-owned metadata and remains valid while the object that defines the TLS symbol is loaded. A resolver may perform dynamic per-thread lookup or allocation. The published descriptor remains immutable for both resolver and fast-function paths.
TLSDESC Call ABI
The canonical sequence is exactly:
LEA.Q [PC + descriptor@TLSDESC_GOTPCREL], R0
CALL [R0]
The LEA.Q places the descriptor address in R0. CALL [R0] reads the 64-bit near-call target from descriptor offset zero and preserves R0, so the called function receives the descriptor address in R0. The canonical sequence places the LEA.Q and CALL adjacently.
The function returns a valid signed byte offset from the GS0 TLS base in R0. It preserves GS0, R8–R15, F8–F15, V16–V31, and P8–P15. The linkage protocol catalog identifies the resolver scratch set. Zero is a valid TLS offset. Resolution failure terminates the resolver path.
TLSDESC Relocations
Descriptor address.
R_BEDROCK_TLSDESC_GOTPCREL32S and R_BEDROCK_TLSDESC_GOTPCREL64 apply to the PC-relative displacement field of the canonical LEA.Q and compute the address of the descriptor for STT_TLS symbol S. The source expression has semantic TLS addend zero. Under the PC-relative field-bias rule in Section 6.1, the RELA addend is exactly \(A=\delta\) and contains solely the instruction-field offset.
Call marker.
R_BEDROCK_TLSDESC_CALL has size zero and names the same TLS symbol as the paired TLSDESC GOTPCREL relocation on the immediately preceding LEA.Q. Its addend is zero and its place is byte zero of the exact immediately following CALL [R0]. It is a static-link relaxation marker consumed exclusively by the static linker. A producer places labels and control-flow targets outside both instructions of a relaxable sequence.
Descriptor initialization.
R_BEDROCK_TLSDESC applies only to STT_TLS symbols, has addend zero, and is placed at offset zero of an aligned descriptor. It directs the loader to initialize two adjacent 64-bit words with the selected {function, argument} pair for S; this operation initializes the pair directly. A link or load rejects an unresolved weak TLS symbol because GS0-relative offset zero represents a valid resolved offset. A weak symbol that resolves to a definition is handled normally.
The linker may merge descriptors only when they name the same resolved binding S and have the required zero semantic addend. If any unrelaxed use remains, a final dynamic link emits one aligned pair in .got and the corresponding dynamic R_BEDROCK_TLSDESC. The loader initializes the pair eagerly before publication. A fully static executable relaxes every TLSDESC use; any remaining use makes the static link fail. If all uses are relaxed, the final link discards the descriptor and its initialization relocation.
TLSDESC Local-Exec Relaxation
Relaxation is permitted only after final binding proves that S is non-preemptible and TLS(S) is a link-time constant. It replaces the entire canonical pair while preserving the section size and every following instruction address:
-
With
R_BEDROCK_TLSDESC_GOTPCREL32S, the original 7-byteLEA.Qand 4-byteCALL [R0]occupy 11 bytes. IfTLS(S)fits signed 32 bits, the linker emitsLEN 11, MOV.Q TLS(S), R0, appliesR_BEDROCK_TLS_OFFSET32Swith addend zero to its 32-bit immediate payload, and writes four zero padding bytes after the natural 7-byte encoding. -
With
R_BEDROCK_TLSDESC_GOTPCREL64, the original 11-byteLEA.Qand 4-byteCALL [R0]occupy 15 bytes. The linker emitsLEN 15, MOV.Q TLS(S), R0, appliesR_BEDROCK_TLS_OFFSET64with addend zero to its 64-bit immediate payload, and writes four zero padding bytes after the natural 11-byte encoding.
The linker consumes both code relocations. The removed call may eliminate normal caller-saved clobbers and unobservable resolver stack activity. Code relies only on the returned offset and defined preserved state. A descriptor is allocated exactly when an unrelaxed reference remains.