git: 2d049e4de292 - stable/15 - i386 rtld: implement support for TLSDESC relocation

From: Konstantin Belousov <kib_at_FreeBSD.org>
Date: Sat, 12 Sep 2026 00:28:07 UTC
The branch stable/15 has been updated by kib:

URL: https://cgit.FreeBSD.org/src/commit/?id=2d049e4de2925a495c54e502c77598f2f833eb5f

commit 2d049e4de2925a495c54e502c77598f2f833eb5f
Author:     Konstantin Belousov <kib@FreeBSD.org>
AuthorDate: 2026-08-25 12:26:50 +0000
Commit:     Konstantin Belousov <kib@FreeBSD.org>
CommitDate: 2026-09-11 06:59:53 +0000

    i386 rtld: implement support for TLSDESC relocation
    
    (cherry picked from commit d45d7aea6197a91b794808d508295a74ce5be0cd)
---
 libexec/rtld-elf/amd64/rtld_start.S  | 25 ++++++++++
 libexec/rtld-elf/i386/reloc.c        | 95 +++++++++++++++++++++++++++++++++++-
 libexec/rtld-elf/i386/rtld_machdep.h | 31 ++++++++++--
 libexec/rtld-elf/i386/rtld_start.S   | 36 ++++++++++++++
 4 files changed, 183 insertions(+), 4 deletions(-)

diff --git a/libexec/rtld-elf/amd64/rtld_start.S b/libexec/rtld-elf/amd64/rtld_start.S
index 7f85a9f94d5c..dcc168a33c57 100644
--- a/libexec/rtld-elf/amd64/rtld_start.S
+++ b/libexec/rtld-elf/amd64/rtld_start.S
@@ -171,4 +171,29 @@ rtld_dynamic_addr:
 	.cfi_endproc
 	.size	rtld_dynamic_addr, . - rtld_dynamic_addr
 
+rtld_tlsdesc_static:
+	.globl	rtld_tlsdesc_static
+	.type	rtld_tlsdesc_static,@function
+	.cfi_startproc
+	movq	8(%rax), %rax
+	ret
+	.cfi_endproc
+	.size	rtld_tlsdesc_static, . - rtld_tlsdesc_static
+
+rtld_tlsdesc_undef:
+	.globl	rtld_tlsdesc_undef
+	.type	rtld_tlsdesc_undef,@function
+	.cfi_startproc
+	ret
+	.cfi_endproc
+	.size	rtld_tlsdesc_undef, . - rtld_tlsdesc_undef
+
+rtld_tlsdesc_dynamic:
+	.globl	rtld_tlsdesc_static
+	.type	rtld_tlsdesc_dynamic,@function
+	.cfi_startproc
+	ret
+	.cfi_endproc
+	.size	rtld_tlsdesc_dynamic, . - rtld_tlsdesc_dynamic
+
 	.section .note.GNU-stack,"",%progbits
diff --git a/libexec/rtld-elf/i386/reloc.c b/libexec/rtld-elf/i386/reloc.c
index 060b3cb147e3..5dd495f2056c 100644
--- a/libexec/rtld-elf/i386/reloc.c
+++ b/libexec/rtld-elf/i386/reloc.c
@@ -51,6 +51,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_386_COPY relocations in the main program.  These
  * copy data from a shared object into a region in the main program's BSS
@@ -125,6 +141,60 @@ 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_Rel *rel,
+    struct tlsdesc *where, int flags, RtldLockState *lockstate)
+{
+	const Elf_Sym *def;
+	const Obj_Entry *defobj;
+	Elf_Addr offs;
+
+	dbg("reloc_tlsdesc obj %s rel %p where %p", obj->path, rel, where);
+	offs = 0;
+	if (ELF_R_SYM(rel->r_info) != 0) {
+		def = find_symdef(ELF_R_SYM(rel->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;
+			return;
+		}
+	}
+	offs += where->offset;
+
+	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,
@@ -271,6 +341,10 @@ reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld, int flags,
 		case R_386_IRELATIVE:
 			obj->irelative_nonplt = true;
 			break;
+		case R_386_TLS_DESC:
+			reloc_tlsdesc(obj, rel, (struct tlsdesc *)where,
+			    flags, lockstate);
+			break;
 		default:
 			_rtld_error(
 		"%s: Unsupported relocation type %d in non-PLT relocations",
@@ -295,10 +369,10 @@ reloc_plt(Obj_Entry *obj, int flags __unused, RtldLockState *lockstate __unused)
 	for (rel = obj->pltrel; rel < rellim; rel++) {
 		Elf_Addr *where;
 
+		where = (Elf_Addr *)(obj->relocbase + rel->r_offset);
 		switch (ELF_R_TYPE(rel->r_info)) {
 		case R_386_JMP_SLOT:
 			/* Relocate the GOT slot pointing into the PLT. */
-			where = (Elf_Addr *)(obj->relocbase + rel->r_offset);
 			*where += (Elf_Addr)obj->relocbase;
 			break;
 
@@ -306,6 +380,11 @@ reloc_plt(Obj_Entry *obj, int flags __unused, RtldLockState *lockstate __unused)
 			obj->irelative = true;
 			break;
 
+		case R_386_TLS_DESC:
+			reloc_tlsdesc(obj, rel, (struct tlsdesc *)where,
+			    SYMLOOK_IN_PLT | flags, lockstate);
+			break;
+
 		default:
 			_rtld_error("Unknown relocation type %x in PLT",
 			    ELF_R_TYPE(rel->r_info));
@@ -552,3 +631,17 @@ calculate_first_tls_offset(size_t size, size_t align, size_t offset)
 {
 	return (calculate_tls_offset(0, 0, size, align, offset));
 }
+
+__GNU_ABI ptrdiff_t
+rtld_tlsdesc_dynamic_impl(struct tlsdesc *tdp)
+{
+	struct tlsdesc_dynarg *tda;
+	struct tcb *tcb;
+	tls_index *ti;
+
+	tda = tdp->arg;
+	ti = &tda->tlsinfo;
+	tcb = _tcb_get();
+	return ((ptrdiff_t)tls_get_addr_common(tcb, ti->ti_module,
+	    ti->ti_offset) - (ptrdiff_t)tcb);
+}
diff --git a/libexec/rtld-elf/i386/rtld_machdep.h b/libexec/rtld-elf/i386/rtld_machdep.h
index 574eafe432db..50c2a1bef94e 100644
--- a/libexec/rtld-elf/i386/rtld_machdep.h
+++ b/libexec/rtld-elf/i386/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. */
 #define rtld_dynamic(obj) \
@@ -83,10 +87,31 @@ typedef struct {
 void *___tls_get_addr(tls_index *ti) __GNU_ABI __exported;
 void *__tls_get_addr(tls_index *ti) __exported;
 
+struct tlsdesc_dynarg
+{
+	tls_index tlsinfo;
+	STAILQ_ENTRY(tlsdesc_dynarg) link;
+};
+
+struct tlsdesc
+{
+	ptrdiff_t (*entry)(struct tlsdesc *) __GNU_ABI;
+	union {
+		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 *tdp) __GNU_ABI;
+
+ptrdiff_t rtld_tlsdesc_static(struct tlsdesc *) __GNU_ABI;
+ptrdiff_t rtld_tlsdesc_undef(struct tlsdesc *) __GNU_ABI;
+ptrdiff_t rtld_tlsdesc_dynamic(struct tlsdesc *) __GNU_ABI;
+
 #endif
diff --git a/libexec/rtld-elf/i386/rtld_start.S b/libexec/rtld-elf/i386/rtld_start.S
index 9dbe32d27628..d81f2d17fff4 100644
--- a/libexec/rtld-elf/i386/rtld_start.S
+++ b/libexec/rtld-elf/i386/rtld_start.S
@@ -95,4 +95,40 @@ _rtld_bind_start:
 	leal	4(%esp),%esp		# Discard reloff, do not change eflags
 	ret				# "Return" to target address
 
+rtld_tlsdesc_static:
+	.globl	rtld_tlsdesc_static
+	.type	rtld_tlsdesc_static,@function
+	.cfi_startproc
+	movl	4(%eax), %eax
+	ret
+	.cfi_endproc
+	.size	rtld_tlsdesc_static, . - rtld_tlsdesc_static
+
+rtld_tlsdesc_undef:
+	.globl	rtld_tlsdesc_undef
+	.type	rtld_tlsdesc_undef,@function
+	.cfi_startproc
+	movl	4(%eax), %eax
+	subl	%gs:0, %eax
+	ret
+	.cfi_endproc
+	.size	rtld_tlsdesc_undef, . - rtld_tlsdesc_undef
+
+rtld_tlsdesc_dynamic:
+	.globl	rtld_tlsdesc_dynamic
+	.type	rtld_tlsdesc_dynamic,@function
+	.cfi_startproc
+	pushl	%ebp
+	/* XXXKIB cfi */
+	movl	%esp, %ebp
+	pushl	%edx
+	pushl	%ecx
+	call	rtld_tlsdesc_dynamic_impl
+	popl	%ecx
+	popl	%edx
+	popl	%ebp
+	ret
+	.cfi_endproc
+	.size	rtld_tlsdesc_dynamic, . - rtld_tlsdesc_dynamic
+
 	.section .note.GNU-stack,"",%progbits