The branch main has been updated by kib:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=76f5fccc5fbdf2a7b42f1e4aa71fe625be32fa13

commit 76f5fccc5fbdf2a7b42f1e4aa71fe625be32fa13
Author:     Konstantin Belousov <[email protected]>
AuthorDate: 2026-08-26 09:41:59 +0000
Commit:     Konstantin Belousov <[email protected]>
CommitDate: 2026-09-05 22:00:10 +0000

    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
 

Reply via email to