authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-03-25 21:12:24-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-03-25 21:12:24-04:00
logf7f563ea53cf58c772003a46624b87dad9c4311d
tree170ff814067aef7ef31c08622d5c1053e6492f6b
parent5ec6a0ea028cd26e9042d4d7a944d5b085f921a4
signaturelock-open Commit is signed but in an unrecognized format.

Revert "Merge pull request #4807 from LemonBoy/tls-touchups"

This reverts commit ee6fda2297bf75432b8d7115ec4c60c213535bbe, reversing changes made to f313ab18aecea1ade0b6a90d671352a641ad351a. This caused a test failure: ``` behavior.misc.test "behavior-arm-linux-none-Debug-bare-multi thread local variable"...test failure /home/vsts/work/1/s/lib/std/testing.zig:191:14: 0x4608f in std.testing.expect (test) if (!ok) @panic("test failure"); ^ /home/vsts/work/1/s/test/stage1/behavior/misc.zig:616:11: 0x53e93 in behavior.misc.test "behavior-arm-linux-none-Debug-bare-multi thread local variable" (test) expect(S.t == 1235); ^ ```

3 files changed, 136 insertions(+), 142 deletions(-)

lib/std/os/linux/tls.zig+113-124
...@@ -1,9 +1,8 @@...@@ -1,9 +1,8 @@
1const std = @import("std");1const std = @import("std");
2const builtin = std.builtin;
3const os = std.os;2const os = std.os;
4const mem = std.mem;3const mem = std.mem;
5const elf = std.elf;4const elf = std.elf;
6const math = std.math;5const builtin = @import("builtin");
7const assert = std.debug.assert;6const assert = std.debug.assert;
87
9// This file implements the two TLS variants [1] used by ELF-based systems.8// This file implements the two TLS variants [1] used by ELF-based systems.
...@@ -61,11 +60,10 @@ const tls_tcb_size = switch (builtin.arch) {...@@ -61,11 +60,10 @@ const tls_tcb_size = switch (builtin.arch) {
61 else => @sizeOf(usize),60 else => @sizeOf(usize),
62};61};
6362
64// Controls the minimum alignment of the TCB end address. The effective value63// Controls if the TCB should be aligned according to the TLS segment p_align
65// used by the code is min(this_value, tls_segment.p_align)
66const tls_tcb_align_size = switch (builtin.arch) {64const tls_tcb_align_size = switch (builtin.arch) {
67 .arm, .armeb, .aarch64, .aarch64_be => 16,65 .arm, .armeb, .aarch64, .aarch64_be => true,
68 else => 1,66 else => false,
69};67};
7068
71// Controls if the TP points to the end of the TCB instead of its beginning69// Controls if the TP points to the end of the TCB instead of its beginning
...@@ -74,6 +72,13 @@ const tls_tp_points_past_tcb = switch (builtin.arch) {...@@ -74,6 +72,13 @@ const tls_tp_points_past_tcb = switch (builtin.arch) {
74 else => false,72 else => false,
75};73};
7674
75// Check if the architecture-specific parameters look correct
76comptime {
77 if (tls_tcb_align_size and tls_variant != TLSVariant.VariantI) {
78 @compileError("tls_tcb_align_size is only meaningful for variant I TLS");
79 }
80}
81
77// Some architectures add some offset to the tp and dtv addresses in order to82// Some architectures add some offset to the tp and dtv addresses in order to
78// make the generated code more efficient83// make the generated code more efficient
7984
...@@ -89,19 +94,17 @@ const tls_dtv_offset = switch (builtin.arch) {...@@ -89,19 +94,17 @@ const tls_dtv_offset = switch (builtin.arch) {
89};94};
9095
91// Per-thread storage for Zig's use96// Per-thread storage for Zig's use
92const CustomData = struct {97const CustomData = packed struct {};
93 padding: [16]usize,
94};
9598
96// Dynamic Thread Vector99// Dynamic Thread Vector
97const DTV = extern struct {100const DTV = packed struct {
98 entries: usize,101 entries: usize,
99 tls_block: [1][*]u8,102 tls_block: [1]usize,
100};103};
101104
102// Holds all the information about the process TLS image105// Holds all the information about the process TLS image
103const TLSImage = struct {106const TLSImage = struct {
104 data_src: []const u8,107 data_src: []u8,
105 alloc_size: usize,108 alloc_size: usize,
106 tcb_offset: usize,109 tcb_offset: usize,
107 dtv_offset: usize,110 dtv_offset: usize,
...@@ -110,13 +113,13 @@ const TLSImage = struct {...@@ -110,13 +113,13 @@ const TLSImage = struct {
110 gdt_entry_number: usize,113 gdt_entry_number: usize,
111};114};
112115
113pub var tls_image: TLSImage = undefined;116pub var tls_image: ?TLSImage = null;
114117
115pub fn setThreadPointer(addr: usize) void {118pub fn setThreadPointer(addr: usize) void {
116 switch (builtin.arch) {119 switch (builtin.arch) {
117 .i386 => {120 .i386 => {
118 var user_desc = std.os.linux.user_desc{121 var user_desc = std.os.linux.user_desc{
119 .entry_number = tls_image.gdt_entry_number,122 .entry_number = tls_image.?.gdt_entry_number,
120 .base_addr = addr,123 .base_addr = addr,
121 .limit = 0xfffff,124 .limit = 0xfffff,
122 .seg_32bit = 1,125 .seg_32bit = 1,
...@@ -131,7 +134,7 @@ pub fn setThreadPointer(addr: usize) void {...@@ -131,7 +134,7 @@ pub fn setThreadPointer(addr: usize) void {
131134
132 const gdt_entry_number = user_desc.entry_number;135 const gdt_entry_number = user_desc.entry_number;
133 // We have to keep track of our slot as it's also needed for clone()136 // We have to keep track of our slot as it's also needed for clone()
134 tls_image.gdt_entry_number = gdt_entry_number;137 tls_image.?.gdt_entry_number = gdt_entry_number;
135 // Update the %gs selector138 // Update the %gs selector
136 asm volatile ("movl %[gs_val], %%gs"139 asm volatile ("movl %[gs_val], %%gs"
137 :140 :
...@@ -168,7 +171,7 @@ pub fn setThreadPointer(addr: usize) void {...@@ -168,7 +171,7 @@ pub fn setThreadPointer(addr: usize) void {
168 }171 }
169}172}
170173
171fn initTLS() void {174pub fn initTLS() ?*elf.Phdr {
172 var tls_phdr: ?*elf.Phdr = null;175 var tls_phdr: ?*elf.Phdr = null;
173 var img_base: usize = 0;176 var img_base: usize = 0;
174177
...@@ -192,138 +195,124 @@ fn initTLS() void {...@@ -192,138 +195,124 @@ fn initTLS() void {
192 // Sanity check195 // Sanity check
193 assert(at_phent == @sizeOf(elf.Phdr));196 assert(at_phent == @sizeOf(elf.Phdr));
194197
195 // Find the TLS section198 // Search the TLS section
196 const phdrs = (@intToPtr([*]elf.Phdr, at_phdr))[0..at_phnum];199 const phdrs = (@intToPtr([*]elf.Phdr, at_phdr))[0..at_phnum];
197200
201 var gnu_stack: ?*elf.Phdr = null;
202
198 for (phdrs) |*phdr| {203 for (phdrs) |*phdr| {
199 switch (phdr.p_type) {204 switch (phdr.p_type) {
200 elf.PT_PHDR => img_base = at_phdr - phdr.p_vaddr,205 elf.PT_PHDR => img_base = at_phdr - phdr.p_vaddr,
201 elf.PT_TLS => tls_phdr = phdr,206 elf.PT_TLS => tls_phdr = phdr,
202 else => {},207 elf.PT_GNU_STACK => gnu_stack = phdr,
208 else => continue,
203 }209 }
204 }210 }
205211
206 // If the cpu is ARM-based, check if it supports the TLS register
207 if (comptime builtin.arch.isARM() and at_hwcap & std.os.linux.HWCAP_TLS == 0) {
208 // If the CPU does not support TLS via a coprocessor register,
209 // a kernel helper function can be used instead on certain linux kernels.
210 // See linux/arch/arm/include/asm/tls.h and musl/src/thread/arm/__set_thread_area.c.
211 @panic("TODO: Implement ARM fallback TLS functionality");
212 }
213
214 var tls_align_factor: usize = undefined;
215 var tls_data: []const u8 = undefined;
216 if (tls_phdr) |phdr| {212 if (tls_phdr) |phdr| {
217 tls_align_factor = phdr.p_align;213 // If the cpu is arm-based, check if it supports the TLS register
218 tls_data = @intToPtr([*]u8, img_base + phdr.p_vaddr)[0..phdr.p_memsz];214 if (builtin.arch == .arm and at_hwcap & std.os.linux.HWCAP_TLS == 0) {
219 } else {215 // If the CPU does not support TLS via a coprocessor register,
220 tls_align_factor = @alignOf(*usize);216 // a kernel helper function can be used instead on certain linux kernels.
221 tls_data = &[_]u8{};217 // See linux/arch/arm/include/asm/tls.h and musl/src/thread/arm/__set_thread_area.c.
218 @panic("TODO: Implement ARM fallback TLS functionality");
219 }
220
221 // Offsets into the allocated TLS area
222 var tcb_offset: usize = undefined;
223 var dtv_offset: usize = undefined;
224 var data_offset: usize = undefined;
225 var thread_data_offset: usize = undefined;
226 // Compute the total size of the ABI-specific data plus our own control
227 // structures
228 const alloc_size = switch (tls_variant) {
229 .VariantI => blk: {
230 var l: usize = 0;
231 dtv_offset = l;
232 l += @sizeOf(DTV);
233 thread_data_offset = l;
234 l += @sizeOf(CustomData);
235 l = mem.alignForward(l, phdr.p_align);
236 tcb_offset = l;
237 if (tls_tcb_align_size) {
238 l += mem.alignForward(tls_tcb_size, phdr.p_align);
239 } else {
240 l += tls_tcb_size;
241 }
242 data_offset = l;
243 l += phdr.p_memsz;
244 break :blk l;
245 },
246 .VariantII => blk: {
247 var l: usize = 0;
248 data_offset = l;
249 l += phdr.p_memsz;
250 l = mem.alignForward(l, phdr.p_align);
251 tcb_offset = l;
252 l += tls_tcb_size;
253 thread_data_offset = l;
254 l += @sizeOf(CustomData);
255 dtv_offset = l;
256 l += @sizeOf(DTV);
257 break :blk l;
258 },
259 };
260
261 tls_image = TLSImage{
262 .data_src = @intToPtr([*]u8, phdr.p_vaddr + img_base)[0..phdr.p_filesz],
263 .alloc_size = alloc_size,
264 .tcb_offset = tcb_offset,
265 .dtv_offset = dtv_offset,
266 .data_offset = data_offset,
267 .gdt_entry_number = @bitCast(usize, @as(isize, -1)),
268 };
222 }269 }
223270
224 // Offsets into the allocated TLS area271 return gnu_stack;
225 var tcb_offset: usize = undefined;
226 var dtv_offset: usize = undefined;
227 var data_offset: usize = undefined;
228 var thread_data_offset: usize = undefined;
229 // Compute the total size of the ABI-specific data plus our own control
230 // structures
231 const alloc_size = switch (tls_variant) {
232 .VariantI => blk: {
233 var l: usize = 0;
234 // Unneeded because l is zero
235 // l = mem.alignForward(l, @alignOf(DTV));
236 dtv_offset = l;
237 l += @sizeOf(DTV);
238 l = mem.alignForward(l, @alignOf(CustomData));
239 thread_data_offset = l;
240 l += @sizeOf(CustomData);
241 // Make sure the TP is aligned
242 l = mem.alignForward(l, tls_align_factor);
243 tcb_offset = l;
244 // Ensure there are at least tls_tcb_align_size bytes of padding
245 const min_align = math.max(tls_tcb_align_size, tls_align_factor);
246 l += mem.alignForward(tls_tcb_size, min_align);
247 data_offset = l;
248 l += mem.alignForward(tls_data.len, tls_align_factor);
249 break :blk l;
250 },
251 .VariantII => blk: {
252 var l: usize = 0;
253 data_offset = l;
254 l = mem.alignForward(tls_data.len, tls_align_factor);
255 // The TP is aligned to p_align
256 tcb_offset = l;
257 l += tls_tcb_size;
258 l = mem.alignForward(l, @alignOf(CustomData));
259 thread_data_offset = l;
260 l += @sizeOf(CustomData);
261 l = mem.alignForward(l, @alignOf(DTV));
262 dtv_offset = l;
263 l += @sizeOf(DTV);
264 break :blk l;
265 },
266 };
267
268 tls_image = TLSImage{
269 .data_src = tls_data,
270 .alloc_size = alloc_size,
271 .tcb_offset = tcb_offset,
272 .dtv_offset = dtv_offset,
273 .data_offset = data_offset,
274 .gdt_entry_number = @bitCast(usize, @as(isize, -1)),
275 };
276}272}
277273
278inline fn alignPtrCast(comptime T: type, ptr: [*]u8) *T {274pub fn copyTLS(addr: usize) usize {
279 return @ptrCast(*T, @alignCast(@alignOf(*T), ptr));275 const tls_img = tls_image.?;
280}
281276
282/// Initializes all the fields of the static TLS area and returns the computed277 // Be paranoid, clear the area we're going to use
283/// architecture-specific value of the thread-pointer register278 @memset(@intToPtr([*]u8, addr), 0, tls_img.alloc_size);
284pub fn prepareTLS(area: []u8) usize {
285 // Clear the area we're going to use, just to be safe
286 mem.set(u8, area, 0);
287 // Prepare the DTV279 // Prepare the DTV
288 const dtv = alignPtrCast(DTV, area.ptr + tls_image.dtv_offset);280 const dtv = @intToPtr(*DTV, addr + tls_img.dtv_offset);
289 dtv.entries = 1;281 dtv.entries = 1;
290 dtv.tls_block[0] = area.ptr + tls_dtv_offset + tls_image.data_offset;282 dtv.tls_block[0] = addr + tls_img.data_offset + tls_dtv_offset;
291 // Prepare the TCB283 // Set-up the TCB
292 const tcb_ptr = alignPtrCast([*]u8, area.ptr + tls_image.tcb_offset);284 // Force the alignment to 1 byte as the TCB may start from a non-aligned
293 tcb_ptr.* = switch (tls_variant) {285 // address under the variant II model
294 .VariantI => area.ptr + tls_image.dtv_offset,286 const tcb_ptr = @intToPtr(*align(1) usize, addr + tls_img.tcb_offset);
295 .VariantII => area.ptr + tls_image.tcb_offset,287 if (tls_variant == TLSVariant.VariantI) {
296 };288 tcb_ptr.* = addr + tls_img.dtv_offset;
289 } else {
290 tcb_ptr.* = addr + tls_img.tcb_offset;
291 }
297 // Copy the data292 // Copy the data
298 mem.copy(u8, area[tls_image.data_offset..], tls_image.data_src);293 @memcpy(@intToPtr([*]u8, addr + tls_img.data_offset), tls_img.data_src.ptr, tls_img.data_src.len);
299294
300 // Return the corrected (if needed) value for the tp register295 // Return the corrected (if needed) value for the tp register
301 return @ptrToInt(area.ptr) + tls_tp_offset +296 return addr + tls_tp_offset +
302 if (tls_tp_points_past_tcb) tls_image.data_offset else tls_image.tcb_offset;297 if (tls_tp_points_past_tcb) tls_img.data_offset else tls_img.tcb_offset;
303}298}
304299
305var main_thread_tls_buffer: [256]u8 align(32) = undefined;300var main_thread_tls_buffer: [256]u8 align(32) = undefined;
306301
307pub fn initStaticTLS() void {302pub fn allocateTLS(size: usize) usize {
308 initTLS();303 // Small TLS allocation, use our local buffer
304 if (size < main_thread_tls_buffer.len) {
305 return @ptrToInt(&main_thread_tls_buffer);
306 }
309307
310 var tls_area = blk: {308 const slice = os.mmap(
311 // Fast path for the common case where the TLS data is really small,309 null,
312 // avoid an allocation and use our local buffer310 size,
313 if (tls_image.alloc_size < main_thread_tls_buffer.len) {311 os.PROT_READ | os.PROT_WRITE,
314 break :blk main_thread_tls_buffer[0..tls_image.alloc_size];312 os.MAP_PRIVATE | os.MAP_ANONYMOUS,
315 }313 -1,
314 0,
315 ) catch @panic("out of memory");
316316
317 break :blk os.mmap(317 return @ptrToInt(slice.ptr);
318 null,
319 tls_image.alloc_size,
320 os.PROT_READ | os.PROT_WRITE,
321 os.MAP_PRIVATE | os.MAP_ANONYMOUS,
322 -1,
323 0,
324 ) catch @panic("out of memory");
325 };
326
327 const tp_value = prepareTLS(tls_area);
328 setThreadPointer(tp_value);
329}318}
lib/std/start.zig+7-1
...@@ -152,7 +152,13 @@ fn posixCallMainAndExit() noreturn {...@@ -152,7 +152,13 @@ fn posixCallMainAndExit() noreturn {
152 const auxv = @ptrCast([*]std.elf.Auxv, @alignCast(@alignOf(usize), envp.ptr + envp_count + 1));152 const auxv = @ptrCast([*]std.elf.Auxv, @alignCast(@alignOf(usize), envp.ptr + envp_count + 1));
153 std.os.linux.elf_aux_maybe = auxv;153 std.os.linux.elf_aux_maybe = auxv;
154 // Initialize the TLS area154 // Initialize the TLS area
155 std.os.linux.tls.initStaticTLS();155 const gnu_stack_phdr = std.os.linux.tls.initTLS() orelse @panic("ELF missing stack size");
156
157 if (std.os.linux.tls.tls_image) |tls_img| {
158 const tls_addr = std.os.linux.tls.allocateTLS(tls_img.alloc_size);
159 const tp = std.os.linux.tls.copyTLS(tls_addr);
160 std.os.linux.tls.setThreadPointer(tp);
161 }
156162
157 // TODO This is disabled because what should we do when linking libc and this code163 // TODO This is disabled because what should we do when linking libc and this code
158 // does not execute? And also it's causing a test failure in stack traces in release modes.164 // does not execute? And also it's causing a test failure in stack traces in release modes.
lib/std/thread.zig+16-17
...@@ -286,10 +286,11 @@ pub const Thread = struct {...@@ -286,10 +286,11 @@ pub const Thread = struct {
286 }286 }
287 // Finally, the Thread Local Storage, if any.287 // Finally, the Thread Local Storage, if any.
288 if (!Thread.use_pthreads) {288 if (!Thread.use_pthreads) {
289 // XXX: Is this alignment enough?289 if (os.linux.tls.tls_image) |tls_img| {
290 l = mem.alignForward(l, @alignOf(usize));290 l = mem.alignForward(l, @alignOf(usize));
291 tls_start_offset = l;291 tls_start_offset = l;
292 l += os.linux.tls.tls_image.alloc_size;292 l += tls_img.alloc_size;
293 }
293 }294 }
294 // Round the size to the page size.295 // Round the size to the page size.
295 break :blk mem.alignForward(l, mem.page_size);296 break :blk mem.alignForward(l, mem.page_size);
...@@ -395,21 +396,18 @@ pub const Thread = struct {...@@ -395,21 +396,18 @@ pub const Thread = struct {
395 else => return os.unexpectedErrno(@intCast(usize, err)),396 else => return os.unexpectedErrno(@intCast(usize, err)),
396 }397 }
397 } else if (std.Target.current.os.tag == .linux) {398 } else if (std.Target.current.os.tag == .linux) {
398 const flags: u32 = os.CLONE_VM | os.CLONE_FS | os.CLONE_FILES |399 var flags: u32 = os.CLONE_VM | os.CLONE_FS | os.CLONE_FILES | os.CLONE_SIGHAND |
399 os.CLONE_SIGHAND | os.CLONE_THREAD | os.CLONE_SYSVSEM |400 os.CLONE_THREAD | os.CLONE_SYSVSEM | os.CLONE_PARENT_SETTID | os.CLONE_CHILD_CLEARTID |
400 os.CLONE_PARENT_SETTID | os.CLONE_CHILD_CLEARTID |401 os.CLONE_DETACHED;
401 os.CLONE_DETACHED | os.CLONE_SETTLS;402 var newtls: usize = undefined;
402 // This structure is only needed when targeting i386403 // This structure is only needed when targeting i386
403 var user_desc: if (std.Target.current.cpu.arch == .i386) os.linux.user_desc else void = undefined;404 var user_desc: if (std.Target.current.cpu.arch == .i386) os.linux.user_desc else void = undefined;
404405
405 const tls_area = mmap_slice[tls_start_offset..];406 if (os.linux.tls.tls_image) |tls_img| {
406 const tp_value = os.linux.tls.prepareTLS(tls_area);
407
408 const newtls = blk: {
409 if (std.Target.current.cpu.arch == .i386) {407 if (std.Target.current.cpu.arch == .i386) {
410 user_desc = os.linux.user_desc{408 user_desc = os.linux.user_desc{
411 .entry_number = os.linux.tls.tls_image.gdt_entry_number,409 .entry_number = tls_img.gdt_entry_number,
412 .base_addr = tp_value,410 .base_addr = os.linux.tls.copyTLS(mmap_addr + tls_start_offset),
413 .limit = 0xfffff,411 .limit = 0xfffff,
414 .seg_32bit = 1,412 .seg_32bit = 1,
415 .contents = 0, // Data413 .contents = 0, // Data
...@@ -418,11 +416,12 @@ pub const Thread = struct {...@@ -418,11 +416,12 @@ pub const Thread = struct {
418 .seg_not_present = 0,416 .seg_not_present = 0,
419 .useable = 1,417 .useable = 1,
420 };418 };
421 break :blk @ptrToInt(&user_desc);419 newtls = @ptrToInt(&user_desc);
422 } else {420 } else {
423 break :blk tp_value;421 newtls = os.linux.tls.copyTLS(mmap_addr + tls_start_offset);
424 }422 }
425 };423 flags |= os.CLONE_SETTLS;
424 }
426425
427 const rc = os.linux.clone(426 const rc = os.linux.clone(
428 MainFuncs.linuxThreadMain,427 MainFuncs.linuxThreadMain,