From 8b06537d92e3ab59694453154f17f51cce594cb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Sun, 11 Oct 2026 09:21:19 +0200 Subject: [PATCH 1/5] compiler_rt.elf.tls: fix AbiTcb for arc and csky Also simplify the logic since it turns out to really just depend on the variant. --- lib/compiler_rt/elf/tls.zig | 30 ++++++------------------------ 1 file changed, 6 insertions(+), 24 deletions(-) diff --git a/lib/compiler_rt/elf/tls.zig b/lib/compiler_rt/elf/tls.zig index 75dda8d300ec7e079979528af638fdc3e8e90347..fd1bdb381d7eca0a038c967bdb48c0fff10cbec5 100644 --- a/lib/compiler_rt/elf/tls.zig +++ b/lib/compiler_rt/elf/tls.zig @@ -158,30 +158,12 @@ const current_dtv_offset = switch (native_arch) { /// Per-thread storage for the ELF TLS ABI. const AbiTcb = switch (current_variant) { - .I_original, .I_modified => switch (native_arch) { - .aarch64, - .aarch64_be, - .alpha, - .arm, - .armeb, - .hppa, - .microblaze, - .microblazeel, - .sh, - .sheb, - .thumb, - .thumbeb, - .xtensa, - .xtensaeb, - => extern struct { - /// This is offset by `current_dtv_offset`. - dtv: usize, - _reserved: ?*anyopaque, - }, - else => extern struct { - /// This is offset by `current_dtv_offset`. - dtv: usize, - }, + .I_original => extern struct { + dtv: usize, + _reserved: ?*anyopaque, + }, + .I_modified => extern struct { + dtv: usize, }, .II => extern struct { /// This is self-referential. -- 2.54.0 From c692aced232da210469b2c04270edc5a980b72d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Sun, 11 Oct 2026 09:21:54 +0200 Subject: [PATCH 2/5] compiler_rt.elf.tls: fix potential underflow if PT_TLS p_align is zero Very unlikely with any sane linker, but technically allowed. --- lib/compiler_rt/elf/tls.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/compiler_rt/elf/tls.zig b/lib/compiler_rt/elf/tls.zig index fd1bdb381d7eca0a038c967bdb48c0fff10cbec5..dccd2018ce02a2b32050b239a30f9d30dc9d06bd 100644 --- a/lib/compiler_rt/elf/tls.zig +++ b/lib/compiler_rt/elf/tls.zig @@ -499,7 +499,7 @@ fn computeAreaDesc(phdrs: []elf.ElfN.Phdr) void { var block_size: usize = undefined; if (tls_phdr) |phdr| { - align_factor = phdr.@"align"; + align_factor = @max(phdr.@"align", 1); // The effective size in memory is represented by `memsz`; the length of the data stored // in the `PT.TLS` segment is `filesz` and may be less than the former. -- 2.54.0 From dae7c7c466500733ef74c72874e7755cc3f218b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Sun, 11 Oct 2026 09:18:36 +0200 Subject: [PATCH 3/5] compiler_rt.elf.tls: add ___tls_get_addr() which is used by 32-bit x86 As per usual, we have GNU to thank. --- lib/compiler_rt/elf/tls.zig | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/lib/compiler_rt/elf/tls.zig b/lib/compiler_rt/elf/tls.zig index dccd2018ce02a2b32050b239a30f9d30dc9d06bd..1d53b6de023feb126a9a459a642ed7798ff414c8 100644 --- a/lib/compiler_rt/elf/tls.zig +++ b/lib/compiler_rt/elf/tls.zig @@ -29,10 +29,14 @@ comptime { // function for the GD and LD models. This function is unlikely to actually be used, since the // linker should be able to relax every TLS access to the LE model and therefore eliminate all // calls to this function, but that isn't guaranteed. - if (native_arch == .s390x) - symbol(&__tls_get_offset, "__tls_get_offset") - else + if (native_arch == .s390x) { + symbol(&__tls_get_offset, "__tls_get_offset"); + } else { symbol(&__tls_get_addr, "__tls_get_addr"); + if (native_arch == .x86) { + symbol(&___tls_get_addr, "___tls_get_addr"); + } + } } /// Represents an ELF TLS variant. @@ -740,6 +744,18 @@ fn __tls_get_addr(ti: *const TlsIndex) callconv(.c) *anyopaque { return block[@intCast(ti.offset)..]; } +fn ___tls_get_addr() callconv(.naked) *anyopaque { + comptime assert(native_arch == .x86); + + // We receive the `TlsIndex` pointer in eax. + comptime assert(@sizeOf(AbiTcb) + @sizeOf(ZigTcb) + @offsetOf(Dtv, "tls_block") == 12); + asm volatile ( + \\ mov 4(%%eax), %%eax + \\ add %%gs:12, %%eax + \\ ret + ); +} + fn __tls_get_offset() callconv(.naked) noreturn { comptime assert(native_arch == .s390x); -- 2.54.0 From 19e06b06ddc1a434c3b0f31cd4f47e17b4295918 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Sun, 11 Oct 2026 10:23:26 +0200 Subject: [PATCH 4/5] compiler_rt.elf.tls: add __m68k_read_tp() which is used by compilers --- lib/compiler_rt/elf/tls.zig | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lib/compiler_rt/elf/tls.zig b/lib/compiler_rt/elf/tls.zig index 1d53b6de023feb126a9a459a642ed7798ff414c8..fd5f8fe6913253af4d950c4b921f088cd708cb93 100644 --- a/lib/compiler_rt/elf/tls.zig +++ b/lib/compiler_rt/elf/tls.zig @@ -33,6 +33,9 @@ comptime { symbol(&__tls_get_offset, "__tls_get_offset"); } else { symbol(&__tls_get_addr, "__tls_get_addr"); + if (native_arch == .m68k) { + symbol(&__m68k_read_tp, "__m68k_read_tp"); + } if (native_arch == .x86) { symbol(&___tls_get_addr, "___tls_get_addr"); } @@ -724,6 +727,12 @@ inline fn mmap(length: usize) usize { } } +fn __m68k_read_tp() callconv(.c) ?*anyopaque { + comptime assert(native_arch == .m68k); + + return @ptrFromInt(getThreadPointer()); +} + const TlsIndex = switch (native_arch) { .x86_64 => extern struct { module: u64, offset: u64 }, // Even for x32... else => extern struct { module: usize, offset: usize }, // ...but not MIPS N32! -- 2.54.0 From 6c3be9e0e7a90e63cb91d890f1a63e682f413d0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Sun, 11 Oct 2026 09:23:01 +0200 Subject: [PATCH 5/5] compiler_rt.elf.tls: fix a couple of bugs in __tls_get_addr() and __tls_get_offset() * We need to add current_dtv_offset in __tls_get_addr() because we aren't reading the actual DTV entry (where that value would be baked in). * In both __tls_get_addr() and __tls_get_offset(), we need to subtract area_info.abi_tcb.offset rather than area_info.block.size because we apply align_factor to the block size when computing the area layout. --- lib/compiler_rt/elf/tls.zig | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/compiler_rt/elf/tls.zig b/lib/compiler_rt/elf/tls.zig index fd5f8fe6913253af4d950c4b921f088cd708cb93..944cf394b65e1d629787fd09114dfee02bb080c5 100644 --- a/lib/compiler_rt/elf/tls.zig +++ b/lib/compiler_rt/elf/tls.zig @@ -743,14 +743,14 @@ fn __tls_get_addr(ti: *const TlsIndex) callconv(.c) *anyopaque { assert(ti.module == 1); // The executable's module ID is always 1 const tp = getThreadPointer(); - const block: [*]u8 = switch (current_variant) { - .I_original => @ptrFromInt(tp -% area_info.abi_tcb.offset +% area_info.block.offset), - .I_modified => @ptrFromInt(tp -% current_tp_offset), + const block = switch (current_variant) { + .I_original => tp -% area_info.abi_tcb.offset +% area_info.block.offset, + .I_modified => tp -% current_tp_offset, // The `.I_original` approach would also work for `.II`, but there is an // alternative strategy which is one less operation: - .II => @ptrFromInt(tp -% area_info.block.size), + .II => tp -% area_info.abi_tcb.offset, }; - return block[@intCast(ti.offset)..]; + return @ptrFromInt(block +% current_dtv_offset +% @as(usize, @intCast(ti.offset))); } fn ___tls_get_addr() callconv(.naked) *anyopaque { @@ -772,10 +772,10 @@ fn __tls_get_offset() callconv(.naked) noreturn { asm volatile ( \\ la %%r1, 0(%%r12, %%r2) \\ lg %%r2, 8(%%r1) - \\ lgrl %%r0, %[block_size] + \\ lgrl %%r0, %[abi_tcb_offset] \\ sgr %%r2, %%r0 \\ br %%r14 : - : [block_size] "s" (&area_info.block.size), + : [abi_tcb_offset] "s" (&area_info.abi_tcb.offset), ); } -- 2.54.0