authorgravatar for alex@alexrp.comAlex Rønne Petersen <alex@alexrp.com> 2026-10-12 10:27:24+02:00
committergravatar for alex@alexrp.comAlex Rønne Petersen <alex@alexrp.com> 2026-10-12 10:27:24+02:00
logccdb1afa6ef81e1390623377a672efabaad45ba7
treee00cb16ff6e9163e650ef37f8d9af9504c8542fa
parent993c75845605c7001c8b9e4526bc5344458570cb
parent6c3be9e0e7a90e63cb91d890f1a63e682f413d0d

Merge pull request 'compiler-rt: ELF static TLS fixes' (#37186) from alexrp/zig:tls-fixes into master

Reviewed-on: https://codeberg.org/ziglang/zig/pulls/37186 Reviewed-by: Andrew Kelley <andrew@ziglang.org>

1 files changed, 42 insertions(+), 35 deletions(-)

lib/compiler_rt/elf/tls.zig+42-35
......@@ -29,10 +29,17 @@ comptime {
2929 // function for the GD and LD models. This function is unlikely to actually be used, since the
3030 // linker should be able to relax every TLS access to the LE model and therefore eliminate all
3131 // calls to this function, but that isn't guaranteed.
32 if (native_arch == .s390x)
33 symbol(&__tls_get_offset, "__tls_get_offset")
34 else
32 if (native_arch == .s390x) {
33 symbol(&__tls_get_offset, "__tls_get_offset");
34 } else {
3535 symbol(&__tls_get_addr, "__tls_get_addr");
36 if (native_arch == .m68k) {
37 symbol(&__m68k_read_tp, "__m68k_read_tp");
38 }
39 if (native_arch == .x86) {
40 symbol(&___tls_get_addr, "___tls_get_addr");
41 }
42 }
3643}
3744
3845/// Represents an ELF TLS variant.
......@@ -158,30 +165,12 @@ const current_dtv_offset = switch (native_arch) {
158165
159166/// Per-thread storage for the ELF TLS ABI.
160167const AbiTcb = switch (current_variant) {
161 .I_original, .I_modified => switch (native_arch) {
162 .aarch64,
163 .aarch64_be,
164 .alpha,
165 .arm,
166 .armeb,
167 .hppa,
168 .microblaze,
169 .microblazeel,
170 .sh,
171 .sheb,
172 .thumb,
173 .thumbeb,
174 .xtensa,
175 .xtensaeb,
176 => extern struct {
177 /// This is offset by `current_dtv_offset`.
178 dtv: usize,
179 _reserved: ?*anyopaque,
180 },
181 else => extern struct {
182 /// This is offset by `current_dtv_offset`.
183 dtv: usize,
184 },
168 .I_original => extern struct {
169 dtv: usize,
170 _reserved: ?*anyopaque,
171 },
172 .I_modified => extern struct {
173 dtv: usize,
185174 },
186175 .II => extern struct {
187176 /// This is self-referential.
......@@ -517,7 +506,7 @@ fn computeAreaDesc(phdrs: []elf.ElfN.Phdr) void {
517506 var block_size: usize = undefined;
518507
519508 if (tls_phdr) |phdr| {
520 align_factor = phdr.@"align";
509 align_factor = @max(phdr.@"align", 1);
521510
522511 // The effective size in memory is represented by `memsz`; the length of the data stored
523512 // in the `PT.TLS` segment is `filesz` and may be less than the former.
......@@ -738,6 +727,12 @@ inline fn mmap(length: usize) usize {
738727 }
739728}
740729
730fn __m68k_read_tp() callconv(.c) ?*anyopaque {
731 comptime assert(native_arch == .m68k);
732
733 return @ptrFromInt(getThreadPointer());
734}
735
741736const TlsIndex = switch (native_arch) {
742737 .x86_64 => extern struct { module: u64, offset: u64 }, // Even for x32...
743738 else => extern struct { module: usize, offset: usize }, // ...but not MIPS N32!
......@@ -748,14 +743,26 @@ fn __tls_get_addr(ti: *const TlsIndex) callconv(.c) *anyopaque {
748743
749744 assert(ti.module == 1); // The executable's module ID is always 1
750745 const tp = getThreadPointer();
751 const block: [*]u8 = switch (current_variant) {
752 .I_original => @ptrFromInt(tp -% area_info.abi_tcb.offset +% area_info.block.offset),
753 .I_modified => @ptrFromInt(tp -% current_tp_offset),
746 const block = switch (current_variant) {
747 .I_original => tp -% area_info.abi_tcb.offset +% area_info.block.offset,
748 .I_modified => tp -% current_tp_offset,
754749 // The `.I_original` approach would also work for `.II`, but there is an
755750 // alternative strategy which is one less operation:
756 .II => @ptrFromInt(tp -% area_info.block.size),
751 .II => tp -% area_info.abi_tcb.offset,
757752 };
758 return block[@intCast(ti.offset)..];
753 return @ptrFromInt(block +% current_dtv_offset +% @as(usize, @intCast(ti.offset)));
754}
755
756fn ___tls_get_addr() callconv(.naked) *anyopaque {
757 comptime assert(native_arch == .x86);
758
759 // We receive the `TlsIndex` pointer in eax.
760 comptime assert(@sizeOf(AbiTcb) + @sizeOf(ZigTcb) + @offsetOf(Dtv, "tls_block") == 12);
761 asm volatile (
762 \\ mov 4(%%eax), %%eax
763 \\ add %%gs:12, %%eax
764 \\ ret
765 );
759766}
760767
761768fn __tls_get_offset() callconv(.naked) noreturn {
......@@ -765,10 +772,10 @@ fn __tls_get_offset() callconv(.naked) noreturn {
765772 asm volatile (
766773 \\ la %%r1, 0(%%r12, %%r2)
767774 \\ lg %%r2, 8(%%r1)
768 \\ lgrl %%r0, %[block_size]
775 \\ lgrl %%r0, %[abi_tcb_offset]
769776 \\ sgr %%r2, %%r0
770777 \\ br %%r14
771778 :
772 : [block_size] "s" (&area_info.block.size),
779 : [abi_tcb_offset] "s" (&area_info.abi_tcb.offset),
773780 );
774781}