From 76f5fccc5fbdf2a7b42f1e4aa71fe625be32fa13 Mon Sep 17 00:00:00 2001 From: Konstantin Belousov Date: Wed, 26 Aug 2026 12:41:59 +0300 Subject: [PATCH] amd64 rtld: implement support for TLSDESC relocation Discussed with: jrtc27 Sponsored by: The FreeBSD Foundation MFC after: 1 week Differential revision: https://reviews.freebsd.org/D59269 --- libexec/rtld-elf/amd64/reloc.c | 94 ++++++++++++++++++++++++++- libexec/rtld-elf/amd64/rtld_machdep.h | 36 +++++++++- libexec/rtld-elf/amd64/rtld_start.S | 42 +++++++++++- 3 files changed, 166 insertions(+), 6 deletions(-) diff --git a/libexec/rtld-elf/amd64/reloc.c b/libexec/rtld-elf/amd64/reloc.c index b1a2069edb2f..7d6487267352 100644 --- a/libexec/rtld-elf/amd64/reloc.c +++ b/libexec/rtld-elf/amd64/reloc.c @@ -53,6 +53,22 @@ #include "rtld.h" #include "rtld_tls.h" +void +md_obj_entry_init(Obj_Entry *obj) +{ + STAILQ_INIT(&obj->tlsdesc_dynargs); +} + +void +md_obj_entry_fini(Obj_Entry *obj) +{ + struct tlsdesc_dynarg *tda, *t; + + STAILQ_FOREACH_SAFE(tda, &obj->tlsdesc_dynargs, link, t) { + free(tda); + } +} + /* * Process the special R_X86_64_COPY relocations in the main program. These * copy data from a shared object into a region in the main program's BSS @@ -127,6 +143,61 @@ init_pltgot(Obj_Entry *obj) } } +static struct tlsdesc_dynarg * +reloc_tlsdesc_alloc(Obj_Entry *obj, Elf_Addr tlsoffs) +{ + struct tlsdesc_dynarg *tda; + + tda = xmalloc(sizeof(struct tlsdesc_dynarg)); + tda->tlsinfo.ti_module = obj->tlsindex; + tda->tlsinfo.ti_offset = tlsoffs; + STAILQ_INSERT_TAIL(&obj->tlsdesc_dynargs, tda, link); + return (tda); +} + +static void +reloc_tlsdesc(const Obj_Entry *obj, const Elf_Rela *rela, + struct tlsdesc *where, int flags, RtldLockState *lockstate) +{ + const Elf_Sym *def; + const Obj_Entry *defobj; + Elf_Addr offs; + + dbg("reloc_tlsdesc obj %s rela %p where %p", obj->path, rela, where); + offs = 0; + if (ELF_R_SYM(rela->r_info) != 0) { + def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, flags, + NULL, lockstate); + if (def == NULL) + rtld_die(); + if (ELF_ST_TYPE(def->st_info) == STT_GNU_IFUNC) { + _rtld_error("%s: IFUNC for TLSDESC reloc", + obj->path); + rtld_die(); + } + offs = def->st_value; + obj = defobj; + if (def->st_shndx == SHN_UNDEF) { + /* Weak undefined thread variable */ + where->entry = rtld_tlsdesc_undef; + where->addend = rela->r_addend; + return; + } + } + offs += rela->r_addend; + + if (obj->tlsoffset != 0) { + /* Variable is in initialy allocated TLS segment */ + where->entry = rtld_tlsdesc_static; + where->offset = offs - obj->tlsoffset; + } else { + /* TLS offest is unknown at load time, use dynamic resolving */ + where->entry = rtld_tlsdesc_dynamic; + where->arg = reloc_tlsdesc_alloc(__DECONST(Obj_Entry *, obj), + offs); + } +} + /* Process the non-PLT relocations. */ int reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld, int flags, @@ -313,6 +384,10 @@ reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld, int flags, case R_X86_64_IRELATIVE: obj->irelative_nonplt = true; break; + case R_X86_64_TLSDESC: + reloc_tlsdesc(obj, rela, (struct tlsdesc *)where, + flags, lockstate); + break; /* * missing: @@ -334,7 +409,7 @@ done: /* Process the PLT relocations. */ int -reloc_plt(Obj_Entry *obj, int flags __unused, RtldLockState *lockstate __unused) +reloc_plt(Obj_Entry *obj, int flags, RtldLockState *lockstate) { const Elf_Rela *relalim; const Elf_Rela *rela; @@ -344,10 +419,10 @@ reloc_plt(Obj_Entry *obj, int flags __unused, RtldLockState *lockstate __unused) for (rela = obj->pltrela; rela < relalim; rela++) { Elf_Addr *where; + where = (Elf_Addr *)(obj->relocbase + rela->r_offset); switch (ELF_R_TYPE(rela->r_info)) { case R_X86_64_JMP_SLOT: /* Relocate the GOT slot pointing into the PLT. */ - where = (Elf_Addr *)(obj->relocbase + rela->r_offset); *where += (Elf_Addr)obj->relocbase; break; @@ -355,6 +430,11 @@ reloc_plt(Obj_Entry *obj, int flags __unused, RtldLockState *lockstate __unused) obj->irelative = true; break; + case R_X86_64_TLSDESC: + reloc_tlsdesc(obj, rela, (struct tlsdesc *)where, + SYMLOOK_IN_PLT | flags, lockstate); + break; + default: _rtld_error("Unknown relocation type %x in PLT", (unsigned int)ELF_R_TYPE(rela->r_info)); @@ -580,3 +660,13 @@ calculate_first_tls_offset(size_t size, size_t align, size_t offset) { return (calculate_tls_offset(0, 0, size, align, offset)); } + +ptrdiff_t +rtld_tlsdesc_dynamic_impl(struct tlsdesc_dynarg *tda, struct tcb *tcb) +{ + tls_index *ti; + + ti = &tda->tlsinfo; + return ((ptrdiff_t)tls_get_addr_common(tcb, ti->ti_module, + ti->ti_offset) - (ptrdiff_t)tcb); +} diff --git a/libexec/rtld-elf/amd64/rtld_machdep.h b/libexec/rtld-elf/amd64/rtld_machdep.h index 53a3cd29f967..7eadba62b9f6 100644 --- a/libexec/rtld-elf/amd64/rtld_machdep.h +++ b/libexec/rtld-elf/amd64/rtld_machdep.h @@ -35,10 +35,14 @@ struct Struct_Obj_Entry; -#define MD_OBJ_ENTRY +#define MD_OBJ_ENTRY \ + STAILQ_HEAD(, tlsdesc_dynarg) tlsdesc_dynargs; -#define MD_OBJ_ENTRY_INIT(obj) -#define MD_OBJ_ENTRY_FINI(obj) +#define MD_OBJ_ENTRY_INIT(obj) md_obj_entry_init(obj) +#define MD_OBJ_ENTRY_FINI(obj) md_obj_entry_fini(obj) + +void md_obj_entry_init(struct Struct_Obj_Entry *obj); +void md_obj_entry_fini(struct Struct_Obj_Entry *obj); /* Return the address of the .dynamic section in the dynamic linker. */ Elf_Dyn *rtld_dynamic_addr(void); @@ -80,9 +84,35 @@ typedef struct { void *__tls_get_addr(tls_index *ti) __exported; +#define __NONE_ABI /* Cannot be expressed in C */ + +struct tlsdesc_dynarg +{ + tls_index tlsinfo; + STAILQ_ENTRY(tlsdesc_dynarg) link; +}; + +struct tlsdesc +{ + ptrdiff_t (*entry)(struct tlsdesc *) __NONE_ABI; + union { + Elf_Ssize addend; + Elf_Size offset; + struct tlsdesc_dynarg *arg; + }; +}; + #define md_abi_variant_hook(x) size_t calculate_first_tls_offset(size_t size, size_t align, size_t offset); size_t calculate_tls_offset(size_t prev_offset, size_t prev_size, size_t size, size_t align, size_t offset); + +ptrdiff_t rtld_tlsdesc_dynamic_impl(struct tlsdesc_dynarg *tda, + struct tcb *tcb); + +ptrdiff_t rtld_tlsdesc_static(struct tlsdesc *) __NONE_ABI; +ptrdiff_t rtld_tlsdesc_undef(struct tlsdesc *) __NONE_ABI; +ptrdiff_t rtld_tlsdesc_dynamic(struct tlsdesc *) __NONE_ABI; + #endif diff --git a/libexec/rtld-elf/amd64/rtld_start.S b/libexec/rtld-elf/amd64/rtld_start.S index dcc168a33c57..b1a619028fd0 100644 --- a/libexec/rtld-elf/amd64/rtld_start.S +++ b/libexec/rtld-elf/amd64/rtld_start.S @@ -184,15 +184,55 @@ rtld_tlsdesc_undef: .globl rtld_tlsdesc_undef .type rtld_tlsdesc_undef,@function .cfi_startproc + movq 8(%rax), %rax + subq %fs:0, %rax ret .cfi_endproc .size rtld_tlsdesc_undef, . - rtld_tlsdesc_undef rtld_tlsdesc_dynamic: - .globl rtld_tlsdesc_static + .globl rtld_tlsdesc_dynamic .type rtld_tlsdesc_dynamic,@function .cfi_startproc + pushq %rbp + /* XXXKIB cfi */ + movq %rsp,%rbp + pushq %rdx + pushq %rcx + pushq %rsi + pushq %rdi + movq 8(%rax), %rdi /* arg->arg */ + movq %fs:0, %rsi /* tcb */ + + movq 8(%rsi), %rcx /* dtv */ + movq (%rcx), %rdx /* dtv_gen */ + cmpq %rdx, tls_dtv_generation(%rip) + jne 2f + movq (%rdi), %rax /* arg->tlsinfo.ti_module */ + dec %rax + movq 16(%rcx, %rax, 8), %rax /* dtv_tls */ + testq %rax, %rax + je 2f + addq 8(%rdi), %rax /* arg->offset */ + subq %rsi, %rax +1: + popq %rdi + popq %rsi + popq %rcx + popq %rdx + popq %rbp ret +2: + pushq %r8 + pushq %r9 + pushq %r10 + pushq %r11 + call rtld_tlsdesc_dynamic_impl + popq %r11 + popq %r10 + popq %r9 + popq %r8 + jmp 1b .cfi_endproc .size rtld_tlsdesc_dynamic, . - rtld_tlsdesc_dynamic