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
This commit is contained in:
Konstantin Belousov committed 2026-09-06 01:00:10 +03:00
1 parent d45d7aea61
commit 76f5fccc5f
3 files changed
+166 -6

No files matched your search

+92 -2
View File
@@ -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);
}
+33 -3
View File
@@ -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
+41 -1
View File
@@ -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