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 {...@@ -29,10 +29,17 @@ comptime {
29 // function for the GD and LD models. This function is unlikely to actually be used, since the29 // function for the GD and LD models. This function is unlikely to actually be used, since the
30 // linker should be able to relax every TLS access to the LE model and therefore eliminate all30 // linker should be able to relax every TLS access to the LE model and therefore eliminate all
31 // calls to this function, but that isn't guaranteed.31 // calls to this function, but that isn't guaranteed.
32 if (native_arch == .s390x)32 if (native_arch == .s390x) {
33 symbol(&__tls_get_offset, "__tls_get_offset")33 symbol(&__tls_get_offset, "__tls_get_offset");
34 else34 } else {
35 symbol(&__tls_get_addr, "__tls_get_addr");35 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 }
36}43}
3744
38/// Represents an ELF TLS variant.45/// Represents an ELF TLS variant.
...@@ -158,30 +165,12 @@ const current_dtv_offset = switch (native_arch) {...@@ -158,30 +165,12 @@ const current_dtv_offset = switch (native_arch) {
158165
159/// Per-thread storage for the ELF TLS ABI.166/// Per-thread storage for the ELF TLS ABI.
160const AbiTcb = switch (current_variant) {167const AbiTcb = switch (current_variant) {
161 .I_original, .I_modified => switch (native_arch) {168 .I_original => extern struct {
162 .aarch64,169 dtv: usize,
163 .aarch64_be,170 _reserved: ?*anyopaque,
164 .alpha,171 },
165 .arm,172 .I_modified => extern struct {
166 .armeb,173 dtv: usize,
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 },
185 },174 },
186 .II => extern struct {175 .II => extern struct {
187 /// This is self-referential.176 /// This is self-referential.
...@@ -517,7 +506,7 @@ fn computeAreaDesc(phdrs: []elf.ElfN.Phdr) void {...@@ -517,7 +506,7 @@ fn computeAreaDesc(phdrs: []elf.ElfN.Phdr) void {
517 var block_size: usize = undefined;506 var block_size: usize = undefined;
518507
519 if (tls_phdr) |phdr| {508 if (tls_phdr) |phdr| {
520 align_factor = phdr.@"align";509 align_factor = @max(phdr.@"align", 1);
521510
522 // The effective size in memory is represented by `memsz`; the length of the data stored511 // The effective size in memory is represented by `memsz`; the length of the data stored
523 // in the `PT.TLS` segment is `filesz` and may be less than the former.512 // in the `PT.TLS` segment is `filesz` and may be less than the former.
...@@ -738,6 +727,12 @@ inline fn mmap(length: usize) usize {...@@ -738,6 +727,12 @@ inline fn mmap(length: usize) usize {
738 }727 }
739}728}
740729
730fn __m68k_read_tp() callconv(.c) ?*anyopaque {
731 comptime assert(native_arch == .m68k);
732
733 return @ptrFromInt(getThreadPointer());
734}
735
741const TlsIndex = switch (native_arch) {736const TlsIndex = switch (native_arch) {
742 .x86_64 => extern struct { module: u64, offset: u64 }, // Even for x32...737 .x86_64 => extern struct { module: u64, offset: u64 }, // Even for x32...
743 else => extern struct { module: usize, offset: usize }, // ...but not MIPS N32!738 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 {...@@ -748,14 +743,26 @@ fn __tls_get_addr(ti: *const TlsIndex) callconv(.c) *anyopaque {
748743
749 assert(ti.module == 1); // The executable's module ID is always 1744 assert(ti.module == 1); // The executable's module ID is always 1
750 const tp = getThreadPointer();745 const tp = getThreadPointer();
751 const block: [*]u8 = switch (current_variant) {746 const block = switch (current_variant) {
752 .I_original => @ptrFromInt(tp -% area_info.abi_tcb.offset +% area_info.block.offset),747 .I_original => tp -% area_info.abi_tcb.offset +% area_info.block.offset,
753 .I_modified => @ptrFromInt(tp -% current_tp_offset),748 .I_modified => tp -% current_tp_offset,
754 // The `.I_original` approach would also work for `.II`, but there is an749 // The `.I_original` approach would also work for `.II`, but there is an
755 // alternative strategy which is one less operation:750 // alternative strategy which is one less operation:
756 .II => @ptrFromInt(tp -% area_info.block.size),751 .II => tp -% area_info.abi_tcb.offset,
757 };752 };
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 );
759}766}
760767
761fn __tls_get_offset() callconv(.naked) noreturn {768fn __tls_get_offset() callconv(.naked) noreturn {
...@@ -765,10 +772,10 @@ fn __tls_get_offset() callconv(.naked) noreturn {...@@ -765,10 +772,10 @@ fn __tls_get_offset() callconv(.naked) noreturn {
765 asm volatile (772 asm volatile (
766 \\ la %%r1, 0(%%r12, %%r2)773 \\ la %%r1, 0(%%r12, %%r2)
767 \\ lg %%r2, 8(%%r1)774 \\ lg %%r2, 8(%%r1)
768 \\ lgrl %%r0, %[block_size]775 \\ lgrl %%r0, %[abi_tcb_offset]
769 \\ sgr %%r2, %%r0776 \\ sgr %%r2, %%r0
770 \\ br %%r14777 \\ br %%r14
771 :778 :
772 : [block_size] "s" (&area_info.block.size),779 : [abi_tcb_offset] "s" (&area_info.abi_tcb.offset),
773 );780 );
774}781}