authorgravatar for alex@alexrp.comAlex Rønne Petersen <alex@alexrp.com> 2024-07-23 00:57:25+02:00
committergravatar for alex@alexrp.comAlex Rønne Petersen <alex@alexrp.com> 2024-07-30 02:32:32+02:00
logb52e05426160c193c29e01072736a319e5fb6f24
treea07dfb7454def797044aebb1c42c607e87378be7
parent38e0f049c531e83ec2eec80d50b624e6c3b8c486
signaturebadge-check Signed by SSH key SHA256:7B/LJ7bpR1eX8aCXSr4mtd5M45VMPKcx9zY8e95b5QM

std.os.linux.tls: Refactor and improve documentation.

* Elaborate on the sub-variants of Variant I. * Clarify the use of the TCB term. * Rename a bunch of stuff to be more accurate/descriptive. * Follow Zig's style around namespacing more. * Use a structure for the ABI TCB. No functional change intended.

3 files changed, 272 insertions(+), 191 deletions(-)

lib/std/Thread.zig+4-4
......@@ -1261,9 +1261,9 @@ const LinuxThreadImpl = struct {
12611261 bytes = std.mem.alignForward(usize, bytes, page_size);
12621262 stack_offset = bytes;
12631263
1264 bytes = std.mem.alignForward(usize, bytes, linux.tls.tls_image.alloc_align);
1264 bytes = std.mem.alignForward(usize, bytes, linux.tls.area_desc.alignment);
12651265 tls_offset = bytes;
1266 bytes += linux.tls.tls_image.alloc_size;
1266 bytes += linux.tls.area_desc.size;
12671267
12681268 bytes = std.mem.alignForward(usize, bytes, @alignOf(Instance));
12691269 instance_offset = bytes;
......@@ -1304,12 +1304,12 @@ const LinuxThreadImpl = struct {
13041304 };
13051305
13061306 // Prepare the TLS segment and prepare a user_desc struct when needed on x86
1307 var tls_ptr = linux.tls.prepareTLS(mapped[tls_offset..]);
1307 var tls_ptr = linux.tls.prepareArea(mapped[tls_offset..]);
13081308 var user_desc: if (target.cpu.arch == .x86) linux.user_desc else void = undefined;
13091309 if (target.cpu.arch == .x86) {
13101310 defer tls_ptr = @intFromPtr(&user_desc);
13111311 user_desc = .{
1312 .entry_number = linux.tls.tls_image.gdt_entry_number,
1312 .entry_number = linux.tls.area_desc.gdt_entry_number,
13131313 .base_addr = tls_ptr,
13141314 .limit = 0xfffff,
13151315 .flags = .{
lib/std/os/linux/tls.zig+267-186
......@@ -1,3 +1,14 @@
1//! This file implements the two TLS variants [1] used by ELF-based systems. Note that, in reality,
2//! Variant I has two sub-variants.
3//!
4//! It is important to understand that the term TCB (Thread Control Block) is overloaded here.
5//! Official ABI documentation uses it simply to mean the ABI TCB, i.e. a small area of ABI-defined
6//! data, usually one or two words (see the `AbiTcb` type below). People will also often use TCB to
7//! refer to the libc TCB, which can be any size and contain anything. (One could even omit it!) We
8//! refer to the latter as the Zig TCB; see the `ZigTcb` type below.
9//!
10//! [1] https://www.akkadia.org/drepper/tls.pdf
11
112const std = @import("std");
213const mem = std.mem;
314const elf = std.elf;
......@@ -7,56 +18,58 @@ const native_arch = @import("builtin").cpu.arch;
718const linux = std.os.linux;
819const posix = std.posix;
920
10// This file implements the two TLS variants [1] used by ELF-based systems.
11//
12// The variant I has the following layout in memory:
13// -------------------------------------------------------
14// | DTV | Zig | DTV | Alignment | TLS |
15// | storage | thread data | pointer | | block |
16// ------------------------^------------------------------
17// `-- The thread pointer register points here
18//
19// In this case we allocate additional space for our control structure that's
20// placed _before_ the DTV pointer together with the DTV.
21//
22// NOTE: Some systems such as power64 or mips use this variant with a twist: the
23// alignment is not present and the tp and DTV addresses are offset by a
24// constant.
25//
26// On the other hand the variant II has the following layout in memory:
27// ---------------------------------------
28// | TLS | TCB | Zig | DTV |
29// | block | | thread data | storage |
30// --------^------------------------------
31// `-- The thread pointer register points here
32//
33// The structure of the TCB is not defined by the ABI so we reserve enough space
34// for a single pointer as some architectures such as x86 and x86_64 need a
35// pointer to the TCB block itself at the address pointed by the tp.
36//
37// In this case the control structure and DTV are placed one after another right
38// after the TLS block data.
39//
40// At the moment the DTV is very simple since we only support static TLS, all we
41// need is a two word vector to hold the number of entries (1) and the address
42// of the first TLS block.
43//
44// [1] https://www.akkadia.org/drepper/tls.pdf
45
46const TLSVariant = enum {
47 VariantI,
48 VariantII,
21/// Represents an ELF TLS variant.
22///
23/// In all variants, the TP and the TLS blocks must be aligned to the `p_align` value in the
24/// `PT_TLS` ELF program header. Everything else has natural alignment.
25///
26/// The location of the DTV does not actually matter. For simplicity, we put it in the TLS area, but
27/// there is no actual ABI requirement that it reside there.
28const Variant = enum {
29 /// The original Variant I:
30 ///
31 /// ----------------------------------------
32 /// | DTV | Zig TCB | ABI TCB | TLS Blocks |
33 /// ----------------^-----------------------
34 /// `-- The TP register points here.
35 ///
36 /// The layout in this variant necessitates separate alignment of both the TP and the TLS
37 /// blocks.
38 ///
39 /// The first word in the ABI TCB points to the DTV. For some architectures, there may be a
40 /// second word with an unspecified meaning.
41 I_original,
42 /// The modified Variant I:
43 ///
44 /// ---------------------------------------------------
45 /// | DTV | Zig TCB | ABI TCB | [Offset] | TLS Blocks |
46 /// -------------------------------------^-------------
47 /// `-- The TP register points here.
48 ///
49 /// The offset (which can be zero) is applied to the TP only; there is never physical gap
50 /// between the ABI TCB and the TLS blocks. This implies that we only need to align the TP.
51 ///
52 /// The first (and only) word in the ABI TCB points to the DTV.
53 I_modified,
54 /// Variant II:
55 ///
56 /// ----------------------------------------
57 /// | TLS Blocks | ABI TCB | Zig TCB | DTV |
58 /// -------------^--------------------------
59 /// `-- The TP register points here.
60 ///
61 /// The first (and only) word in the ABI TCB points to the ABI TCB itself.
62 II,
4963};
5064
51const tls_variant = switch (native_arch) {
65const current_variant: Variant = switch (native_arch) {
5266 .arm,
5367 .armeb,
54 .thumb,
55 .thumbeb,
5668 .aarch64,
5769 .aarch64_be,
58 .riscv32,
59 .riscv64,
70 .thumb,
71 .thumbeb,
72 => .I_original,
6073 .mips,
6174 .mipsel,
6275 .mips64,
......@@ -65,73 +78,126 @@ const tls_variant = switch (native_arch) {
6578 .powerpcle,
6679 .powerpc64,
6780 .powerpc64le,
68 => TLSVariant.VariantI,
69 .x86_64, .x86, .sparc64 => TLSVariant.VariantII,
70 else => @compileError("undefined tls_variant for this architecture"),
71};
72
73// Controls how many bytes are reserved for the Thread Control Block
74const tls_tcb_size = switch (native_arch) {
75 // ARM EABI mandates enough space for two pointers: the first one points to
76 // the DTV while the second one is unspecified but reserved
77 .arm, .armeb, .thumb, .thumbeb, .aarch64, .aarch64_be => 2 * @sizeOf(usize),
78 // One pointer-sized word that points either to the DTV or the TCB itself
79 else => @sizeOf(usize),
81 .riscv32,
82 .riscv64,
83 => .I_modified,
84 .sparc64,
85 .x86,
86 .x86_64,
87 => .II,
88 else => @compileError("undefined TLS variant for this architecture"),
8089};
8190
82// Controls if the TP points to the end of the TCB instead of its beginning
83const tls_tp_points_past_tcb = switch (native_arch) {
84 .riscv32, .riscv64, .mips, .mipsel, .mips64, .mips64el, .powerpc, .powerpcle, .powerpc64, .powerpc64le => true,
85 else => false,
91/// The Offset value for the modified Variant I.
92const current_tp_offset = switch (native_arch) {
93 .mips,
94 .mipsel,
95 .mips64,
96 .mips64el,
97 .powerpc,
98 .powerpcle,
99 .powerpc64,
100 .powerpc64le,
101 => 0x7000,
102 else => 0,
86103};
87104
88// Some architectures add some offset to the tp and dtv addresses in order to
89// make the generated code more efficient
90
91const tls_tp_offset = switch (native_arch) {
92 .mips, .mipsel, .mips64, .mips64el, .powerpc, .powerpcle, .powerpc64, .powerpc64le => 0x7000,
105/// Usually only used by the modified Variant I.
106const current_dtv_offset = switch (native_arch) {
107 .mips,
108 .mipsel,
109 .mips64,
110 .mips64el,
111 .powerpc,
112 .powerpcle,
113 .powerpc64,
114 .powerpc64le,
115 => 0x8000,
116 .riscv32,
117 .riscv64,
118 => 0x800,
93119 else => 0,
94120};
95121
96const tls_dtv_offset = switch (native_arch) {
97 .mips, .mipsel, .mips64, .mips64el, .powerpc, .powerpcle, .powerpc64, .powerpc64le => 0x8000,
98 .riscv32, .riscv64 => 0x800,
99 else => 0,
122/// Per-thread storage for the ELF TLS ABI.
123const AbiTcb = switch (current_variant) {
124 .I_original, .I_modified => switch (native_arch) {
125 // ARM EABI mandates enough space for two pointers: the first one points to the DTV as
126 // usual, while the second one is unspecified.
127 .aarch64,
128 .aarch64_be,
129 .arm,
130 .armeb,
131 .thumb,
132 .thumbeb,
133 => extern struct {
134 /// This is offset by `current_dtv_offset`.
135 dtv: usize,
136 reserved: ?*anyopaque,
137 },
138 else => extern struct {
139 /// This is offset by `current_dtv_offset`.
140 dtv: usize,
141 },
142 },
143 .II => extern struct {
144 /// This is self-referential.
145 self: *AbiTcb,
146 },
100147};
101148
102// Per-thread storage for Zig's use
103const CustomData = struct {
149/// Per-thread storage for Zig's use. Currently unused.
150const ZigTcb = struct {
104151 dummy: usize,
105152};
106153
107// Dynamic Thread Vector
108const DTV = extern struct {
109 entries: usize,
110 tls_block: [1][*]u8,
154/// Dynamic Thread Vector as specified in the ELF TLS ABI. Ordinarily, there is a block pointer per
155/// dynamically-loaded module, but since we only support static TLS, we only need one block pointer.
156const Dtv = extern struct {
157 len: usize = 1,
158 tls_block: [*]u8,
111159};
112160
113// Holds all the information about the process TLS image
114const TLSImage = struct {
115 init_data: []const u8,
116 alloc_size: usize,
117 alloc_align: usize,
118 tcb_offset: usize,
119 dtv_offset: usize,
120 data_offset: usize,
121 data_size: usize,
122 // Only used on the x86 architecture
161/// Describes a process's TLS area. The area encompasses the DTV, both TCBs, and the TLS block, with
162/// the exact layout of these being dependent primarily on `current_variant`.
163const AreaDesc = struct {
164 size: usize,
165 alignment: usize,
166
167 dtv: struct {
168 /// Offset into the TLS area.
169 offset: usize,
170 },
171
172 abi_tcb: struct {
173 /// Offset into the TLS area.
174 offset: usize,
175 },
176
177 block: struct {
178 /// The initial data to be copied into the TLS block. Note that this may be smaller than
179 /// `size`, in which case any remaining data in the TLS block is simply left uninitialized.
180 init: []const u8,
181 /// Offset into the TLS area.
182 offset: usize,
183 /// This is the effective size of the TLS block, which may be greater than `init.len`.
184 size: usize,
185 },
186
187 /// Only used on the 32-bit x86 architecture (not x86_64, nor x32).
123188 gdt_entry_number: usize,
124189};
125190
126pub var tls_image: TLSImage = undefined;
191pub var area_desc: AreaDesc = undefined;
127192
128193pub fn setThreadPointer(addr: usize) void {
129194 @setRuntimeSafety(false);
130195 @disableInstrumentation();
196
131197 switch (native_arch) {
132198 .x86 => {
133199 var user_desc: linux.user_desc = .{
134 .entry_number = tls_image.gdt_entry_number,
200 .entry_number = area_desc.gdt_entry_number,
135201 .base_addr = addr,
136202 .limit = 0xfffff,
137203 .flags = .{
......@@ -148,7 +214,7 @@ pub fn setThreadPointer(addr: usize) void {
148214
149215 const gdt_entry_number = user_desc.entry_number;
150216 // We have to keep track of our slot as it's also needed for clone()
151 tls_image.gdt_entry_number = gdt_entry_number;
217 area_desc.gdt_entry_number = gdt_entry_number;
152218 // Update the %gs selector
153219 asm volatile ("movl %[gs_val], %%gs"
154220 :
......@@ -206,7 +272,7 @@ pub fn setThreadPointer(addr: usize) void {
206272 }
207273}
208274
209fn initTLS(phdrs: []elf.Phdr) void {
275fn computeAreaDesc(phdrs: []elf.Phdr) void {
210276 @setRuntimeSafety(false);
211277 @disableInstrumentation();
212278
......@@ -221,72 +287,85 @@ fn initTLS(phdrs: []elf.Phdr) void {
221287 }
222288 }
223289
224 var tls_align_factor: usize = undefined;
225 var tls_data: []const u8 = undefined;
226 var tls_data_alloc_size: usize = undefined;
290 var align_factor: usize = undefined;
291 var block_init: []const u8 = undefined;
292 var block_size: usize = undefined;
293
227294 if (tls_phdr) |phdr| {
228 // The effective size in memory is represented by p_memsz, the length of
229 // the data stored in the PT_TLS segment is p_filesz and may be less
230 // than the former
231 tls_align_factor = phdr.p_align;
232 tls_data = @as([*]u8, @ptrFromInt(img_base + phdr.p_vaddr))[0..phdr.p_filesz];
233 tls_data_alloc_size = phdr.p_memsz;
295 align_factor = phdr.p_align;
296
297 // The effective size in memory is represented by `p_memsz`; the length of the data stored
298 // in the `PT_TLS` segment is `p_filesz` and may be less than the former.
299 block_init = @as([*]u8, @ptrFromInt(img_base + phdr.p_vaddr))[0..phdr.p_filesz];
300 block_size = phdr.p_memsz;
234301 } else {
235 tls_align_factor = @alignOf(usize);
236 tls_data = &[_]u8{};
237 tls_data_alloc_size = 0;
302 align_factor = @alignOf(usize);
303
304 block_init = &[_]u8{};
305 block_size = 0;
238306 }
239307
240 // Offsets into the allocated TLS area
241 var tcb_offset: usize = undefined;
308 // Offsets into the allocated TLS area.
242309 var dtv_offset: usize = undefined;
243 var data_offset: usize = undefined;
244 // Compute the total size of the ABI-specific data plus our own control
245 // structures. All the offset calculated here assume a well-aligned base
246 // address.
247 const alloc_size = switch (tls_variant) {
248 .VariantI => blk: {
310 var abi_tcb_offset: usize = undefined;
311 var block_offset: usize = undefined;
312
313 // Compute the total size of the ABI-specific data plus our own `ZigTcb` structure. All the
314 // offsets calculated here assume a well-aligned base address.
315 const area_size = switch (current_variant) {
316 .I_original, .I_modified => blk: {
249317 var l: usize = 0;
250318 dtv_offset = l;
251 l += @sizeOf(DTV);
252 // Add some padding here so that the thread pointer (tcb_offset) is
253 // aligned to p_align and the CustomData structure can be found by
254 // simply subtracting its @sizeOf from the tp value
255 const delta = (l + @sizeOf(CustomData)) & (tls_align_factor - 1);
319 l += @sizeOf(Dtv);
320 // Add some padding here so that the TP (`abi_tcb_offset`) is aligned to `align_factor`
321 // and the `ZigTcb` structure can be found by simply subtracting `@sizeOf(ZigTcb)` from
322 // the TP.
323 const delta = (l + @sizeOf(ZigTcb)) & (align_factor - 1);
256324 if (delta > 0)
257 l += tls_align_factor - delta;
258 l += @sizeOf(CustomData);
259 tcb_offset = l;
260 l += alignForward(tls_tcb_size, tls_align_factor);
261 data_offset = l;
262 l += tls_data_alloc_size;
325 l += align_factor - delta;
326 l += @sizeOf(ZigTcb);
327 abi_tcb_offset = l;
328 l += alignForward(@sizeOf(AbiTcb), align_factor);
329 block_offset = l;
330 l += block_size;
263331 break :blk l;
264332 },
265 .VariantII => blk: {
333 .II => blk: {
266334 var l: usize = 0;
267 data_offset = l;
268 l += alignForward(tls_data_alloc_size, tls_align_factor);
269 // The thread pointer is aligned to p_align
270 tcb_offset = l;
271 l += tls_tcb_size;
272 // The CustomData structure is right after the TCB with no padding
273 // in between so it can be easily found
274 l += @sizeOf(CustomData);
275 l = alignForward(l, @alignOf(DTV));
335 block_offset = l;
336 l += alignForward(block_size, align_factor);
337 // The TP is aligned to `align_factor`.
338 abi_tcb_offset = l;
339 l += @sizeOf(AbiTcb);
340 // The `ZigTcb` structure is right after the `AbiTcb` with no padding in between so it
341 // can be easily found.
342 l += @sizeOf(ZigTcb);
343 // It doesn't really matter where we put the DTV, so give it natural alignment.
344 l = alignForward(l, @alignOf(Dtv));
276345 dtv_offset = l;
277 l += @sizeOf(DTV);
346 l += @sizeOf(Dtv);
278347 break :blk l;
279348 },
280349 };
281350
282 tls_image = TLSImage{
283 .init_data = tls_data,
284 .alloc_size = alloc_size,
285 .alloc_align = tls_align_factor,
286 .tcb_offset = tcb_offset,
287 .dtv_offset = dtv_offset,
288 .data_offset = data_offset,
289 .data_size = tls_data_alloc_size,
351 area_desc = .{
352 .size = area_size,
353 .alignment = align_factor,
354
355 .dtv = .{
356 .offset = dtv_offset,
357 },
358
359 .abi_tcb = .{
360 .offset = abi_tcb_offset,
361 },
362
363 .block = .{
364 .init = block_init,
365 .offset = block_offset,
366 .size = block_size,
367 },
368
290369 .gdt_entry_number = @as(usize, @bitCast(@as(isize, -1))),
291370 };
292371}
......@@ -306,78 +385,80 @@ inline fn alignPtrCast(comptime T: type, ptr: [*]u8) *T {
306385 return @ptrCast(@alignCast(ptr));
307386}
308387
309/// Initializes all the fields of the static TLS area and returns the computed
310/// architecture-specific value of the thread-pointer register
311///
312/// This function is inline because thread local storage is not set up yet.
313pub fn prepareTLS(area: []u8) usize {
388/// Initializes all the fields of the static TLS area and returns the computed architecture-specific
389/// value of the TP register.
390pub fn prepareArea(area: []u8) usize {
314391 @setRuntimeSafety(false);
315392 @disableInstrumentation();
316 // Clear the area we're going to use, just to be safe
393
394 // Clear the area we're going to use, just to be safe.
317395 @memset(area, 0);
318 // Prepare the DTV
319 const dtv = alignPtrCast(DTV, area.ptr + tls_image.dtv_offset);
320 dtv.entries = 1;
321 dtv.tls_block[0] = area.ptr + tls_dtv_offset + tls_image.data_offset;
322 // Prepare the TCB
323 const tcb_ptr = alignPtrCast([*]u8, area.ptr + tls_image.tcb_offset);
324 tcb_ptr.* = switch (tls_variant) {
325 .VariantI => area.ptr + tls_image.dtv_offset,
326 .VariantII => area.ptr + tls_image.tcb_offset,
396
397 // Prepare the ABI TCB.
398 const abi_tcb = alignPtrCast(AbiTcb, area.ptr + area_desc.abi_tcb.offset);
399 switch (current_variant) {
400 .I_original, .I_modified => abi_tcb.dtv = @intFromPtr(area.ptr + area_desc.dtv.offset),
401 .II => abi_tcb.self = abi_tcb,
402 }
403
404 // Prepare the DTV.
405 const dtv = alignPtrCast(Dtv, area.ptr + area_desc.dtv.offset);
406 dtv.len = 1;
407 dtv.tls_block = area.ptr + current_dtv_offset + area_desc.block.offset;
408
409 // Copy the initial data.
410 @memcpy(area[area_desc.block.offset..][0..area_desc.block.init.len], area_desc.block.init);
411
412 // Return the corrected value (if needed) for the TP register. Overflow here is not a problem;
413 // the pointer arithmetic involving the TP is done with wrapping semantics.
414 return @intFromPtr(area.ptr) +% switch (current_variant) {
415 .I_original, .II => area_desc.abi_tcb.offset,
416 .I_modified => area_desc.block.offset +% current_tp_offset,
327417 };
328 // Copy the data
329 @memcpy(area[tls_image.data_offset..][0..tls_image.init_data.len], tls_image.init_data);
330
331 // Return the corrected value (if needed) for the tp register.
332 // Overflow here is not a problem, the pointer arithmetic involving the tp
333 // is done with wrapping semantics.
334 return @intFromPtr(area.ptr) +% tls_tp_offset +%
335 if (tls_tp_points_past_tcb) tls_image.data_offset else tls_image.tcb_offset;
336418}
337419
338// The main motivation for the size chosen here is this is how much ends up being
339// requested for the thread local variables of the std.crypto.random implementation.
340// I'm not sure why it ends up being so much; the struct itself is only 64 bytes.
341// I think it has to do with being page aligned and LLVM or LLD is not smart enough
342// to lay out the TLS data in a space conserving way. Anyway I think it's fine
343// because it's less than 3 pages of memory, and putting it in the ELF like this
344// is equivalent to moving the mmap call below into the kernel, avoiding syscall
345// overhead.
346var main_thread_tls_buffer: [0x2100]u8 align(mem.page_size) = undefined;
347
348pub fn initStaticTLS(phdrs: []elf.Phdr) void {
420// The main motivation for the size chosen here is that this is how much ends up being requested for
421// the thread-local variables of the `std.crypto.random` implementation. I'm not sure why it ends up
422// being so much; the struct itself is only 64 bytes. I think it has to do with being page-aligned
423// and LLVM or LLD is not smart enough to lay out the TLS data in a space-conserving way. Anyway, I
424// think it's fine because it's less than 3 pages of memory, and putting it in the ELF like this is
425// equivalent to moving the `mmap` call below into the kernel, avoiding syscall overhead.
426var main_thread_area_buffer: [0x2100]u8 align(mem.page_size) = undefined;
427
428/// Computes the layout of the static TLS area, allocates the area, initializes all of its fields,
429/// and assigns the architecture-specific value to the TP register.
430pub fn initStatic(phdrs: []elf.Phdr) void {
349431 @setRuntimeSafety(false);
350432 @disableInstrumentation();
351433
352 initTLS(phdrs);
434 computeAreaDesc(phdrs);
353435
354 const tls_area = blk: {
355 // Fast path for the common case where the TLS data is really small,
356 // avoid an allocation and use our local buffer.
357 if (tls_image.alloc_align <= mem.page_size and
358 tls_image.alloc_size <= main_thread_tls_buffer.len)
359 {
360 break :blk main_thread_tls_buffer[0..tls_image.alloc_size];
436 const area = blk: {
437 // Fast path for the common case where the TLS data is really small, avoid an allocation and
438 // use our local buffer.
439 if (area_desc.alignment <= mem.page_size and area_desc.size <= main_thread_area_buffer.len) {
440 break :blk main_thread_area_buffer[0..area_desc.size];
361441 }
362442
363443 const begin_addr = mmap(
364444 null,
365 tls_image.alloc_size + tls_image.alloc_align - 1,
445 area_desc.size + area_desc.alignment - 1,
366446 posix.PROT.READ | posix.PROT.WRITE,
367447 .{ .TYPE = .PRIVATE, .ANONYMOUS = true },
368448 -1,
369449 0,
370450 );
371451 if (@as(isize, @bitCast(begin_addr)) < 0) @trap();
372 const alloc_tls_area: [*]align(mem.page_size) u8 = @ptrFromInt(begin_addr);
452
453 const area_ptr: [*]align(mem.page_size) u8 = @ptrFromInt(begin_addr);
373454
374455 // Make sure the slice is correctly aligned.
375 const begin_aligned_addr = alignForward(begin_addr, tls_image.alloc_align);
456 const begin_aligned_addr = alignForward(begin_addr, area_desc.alignment);
376457 const start = begin_aligned_addr - begin_addr;
377 break :blk alloc_tls_area[start..][0..tls_image.alloc_size];
458 break :blk area_ptr[start..][0..area_desc.size];
378459 };
379460
380 const tp_value = prepareTLS(tls_area);
461 const tp_value = prepareArea(area);
381462 setThreadPointer(tp_value);
382463}
383464
lib/std/start.zig+1-1
......@@ -456,7 +456,7 @@ fn posixCallMainAndExit(argc_argv_ptr: [*]usize) callconv(.C) noreturn {
456456 }
457457
458458 // Initialize the TLS area.
459 std.os.linux.tls.initStaticTLS(phdrs);
459 std.os.linux.tls.initStatic(phdrs);
460460 }
461461
462462 // The way Linux executables represent stack size is via the PT_GNU_STACK