From 8056a851517cd92590ddb9a4b76ce7b1f4aeb242 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Sat, 3 Aug 2024 19:40:12 +0200 Subject: [PATCH 1/5] std: Move start_windows_tls.zig to os/windows/tls.zig. Just to be consistent with Linux. --- lib/std/{start_windows_tls.zig => os/windows/tls.zig} | 0 lib/std/start.zig | 6 +++--- 2 files changed, 3 insertions(+), 3 deletions(-) rename lib/std/{start_windows_tls.zig => os/windows/tls.zig} (100%) diff --git a/lib/std/start_windows_tls.zig b/lib/std/os/windows/tls.zig similarity index 100% rename from lib/std/start_windows_tls.zig rename to lib/std/os/windows/tls.zig diff --git a/lib/std/start.zig b/lib/std/start.zig index fd8661660b0ccb03af87a9a833b92a2939cd5ad0..edbe4e9bca688e20e61ea25c6095ab9ce5594e72 100644 --- a/lib/std/start.zig +++ b/lib/std/start.zig @@ -176,7 +176,7 @@ fn _DllMainCRTStartup( lpReserved: std.os.windows.LPVOID, ) callconv(std.os.windows.WINAPI) std.os.windows.BOOL { if (!builtin.single_threaded and !builtin.link_libc) { - _ = @import("start_windows_tls.zig"); + _ = @import("os/windows/tls.zig"); } if (@hasDecl(root, "DllMain")) { @@ -427,7 +427,7 @@ fn _start() callconv(.Naked) noreturn { fn WinStartup() callconv(std.os.windows.WINAPI) noreturn { @setAlignStack(16); if (!builtin.single_threaded and !builtin.link_libc) { - _ = @import("start_windows_tls.zig"); + _ = @import("os/windows/tls.zig"); } std.debug.maybeEnableSegfaultHandler(); @@ -438,7 +438,7 @@ fn WinStartup() callconv(std.os.windows.WINAPI) noreturn { fn wWinMainCRTStartup() callconv(std.os.windows.WINAPI) noreturn { @setAlignStack(16); if (!builtin.single_threaded and !builtin.link_libc) { - _ = @import("start_windows_tls.zig"); + _ = @import("os/windows/tls.zig"); } std.debug.maybeEnableSegfaultHandler(); -- 2.54.0 From c2fcdc21c27803e5cbb6dfcb6f61aa0905f6df38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Sat, 3 Aug 2024 19:55:44 +0200 Subject: [PATCH 2/5] std.os.windows.tls: Change type of `_tls_start`/`_tls_end` to `*anyopaque`. If they're typed as `u8`, they can be aligned to anything. We want at least pointer size alignment. --- lib/std/os/windows/tls.zig | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/std/os/windows/tls.zig b/lib/std/os/windows/tls.zig index 48880b4811d3536a78b6e7dccbf48bf561fbc282..e8735a2707501646dd04da8e841d4cfdf29798c1 100644 --- a/lib/std/os/windows/tls.zig +++ b/lib/std/os/windows/tls.zig @@ -2,8 +2,8 @@ const std = @import("std"); const builtin = @import("builtin"); export var _tls_index: u32 = std.os.windows.TLS_OUT_OF_INDEXES; -export var _tls_start: u8 linksection(".tls") = 0; -export var _tls_end: u8 linksection(".tls$ZZZ") = 0; +export var _tls_start: ?*anyopaque linksection(".tls") = null; +export var _tls_end: ?*anyopaque linksection(".tls$ZZZ") = null; export var __xl_a: std.os.windows.PIMAGE_TLS_CALLBACK linksection(".CRT$XLA") = null; export var __xl_z: std.os.windows.PIMAGE_TLS_CALLBACK linksection(".CRT$XLZ") = null; @@ -31,8 +31,8 @@ comptime { //}; // This is the workaround because we can't do @intFromPtr at comptime like that. pub const IMAGE_TLS_DIRECTORY = extern struct { - StartAddressOfRawData: *anyopaque, - EndAddressOfRawData: *anyopaque, + StartAddressOfRawData: *?*anyopaque, + EndAddressOfRawData: *?*anyopaque, AddressOfIndex: *anyopaque, AddressOfCallBacks: *anyopaque, SizeOfZeroFill: u32, -- 2.54.0 From 0f1db901980f9329b3c45c016eb467f083493060 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Sat, 3 Aug 2024 19:56:42 +0200 Subject: [PATCH 3/5] std.os.windows.tls: Slightly improve type safety. --- lib/std/os/windows/tls.zig | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/std/os/windows/tls.zig b/lib/std/os/windows/tls.zig index e8735a2707501646dd04da8e841d4cfdf29798c1..658eff86816e24d7edd8e4009a481a71acea0820 100644 --- a/lib/std/os/windows/tls.zig +++ b/lib/std/os/windows/tls.zig @@ -1,11 +1,12 @@ const std = @import("std"); const builtin = @import("builtin"); +const windows = std.os.windows; export var _tls_index: u32 = std.os.windows.TLS_OUT_OF_INDEXES; export var _tls_start: ?*anyopaque linksection(".tls") = null; export var _tls_end: ?*anyopaque linksection(".tls$ZZZ") = null; -export var __xl_a: std.os.windows.PIMAGE_TLS_CALLBACK linksection(".CRT$XLA") = null; -export var __xl_z: std.os.windows.PIMAGE_TLS_CALLBACK linksection(".CRT$XLZ") = null; +export var __xl_a: windows.PIMAGE_TLS_CALLBACK linksection(".CRT$XLA") = null; +export var __xl_z: windows.PIMAGE_TLS_CALLBACK linksection(".CRT$XLZ") = null; comptime { if (builtin.target.cpu.arch == .x86 and builtin.zig_backend != .stage2_c) { @@ -33,8 +34,8 @@ comptime { pub const IMAGE_TLS_DIRECTORY = extern struct { StartAddressOfRawData: *?*anyopaque, EndAddressOfRawData: *?*anyopaque, - AddressOfIndex: *anyopaque, - AddressOfCallBacks: *anyopaque, + AddressOfIndex: *u32, + AddressOfCallBacks: *windows.PIMAGE_TLS_CALLBACK, SizeOfZeroFill: u32, Characteristics: u32, }; @@ -42,7 +43,7 @@ export const _tls_used linksection(".rdata$T") = IMAGE_TLS_DIRECTORY{ .StartAddressOfRawData = &_tls_start, .EndAddressOfRawData = &_tls_end, .AddressOfIndex = &_tls_index, - .AddressOfCallBacks = @as(*anyopaque, @ptrCast(&__xl_a)), + .AddressOfCallBacks = &__xl_a, .SizeOfZeroFill = 0, .Characteristics = 0, }; -- 2.54.0 From 1d8fca0060ecfe700df7722e6763de94e2bd0b51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Sat, 3 Aug 2024 19:57:06 +0200 Subject: [PATCH 4/5] std.os.windows.tls: Only define _tls_array when targeting MSVC. LLVM does not use it when emitting code for the MinGW ABI. --- lib/std/os/windows/tls.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/std/os/windows/tls.zig b/lib/std/os/windows/tls.zig index 658eff86816e24d7edd8e4009a481a71acea0820..e4fceebfc49811b626009ab38110e56553ffb9d5 100644 --- a/lib/std/os/windows/tls.zig +++ b/lib/std/os/windows/tls.zig @@ -9,7 +9,7 @@ export var __xl_a: windows.PIMAGE_TLS_CALLBACK linksection(".CRT$XLA") = null; export var __xl_z: windows.PIMAGE_TLS_CALLBACK linksection(".CRT$XLZ") = null; comptime { - if (builtin.target.cpu.arch == .x86 and builtin.zig_backend != .stage2_c) { + if (builtin.cpu.arch == .x86 and builtin.abi == .msvc and builtin.zig_backend != .stage2_c) { // The __tls_array is the offset of the ThreadLocalStoragePointer field // in the TEB block whose base address held in the %fs segment. asm ( -- 2.54.0 From cb1fffb29eae6eafd46c87d3950911f496288ab2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Sat, 3 Aug 2024 20:18:14 +0200 Subject: [PATCH 5/5] std.os.windows.tls: Set `AddressOfCallBacks` to `&__xl_a + 1`. `__xl_a` is just a global variable containing a null function pointer. There's nothing magical about it or its name at all. The section names used on `__xl_a` and `__xl_b` (`.CRT$XLA` and `.CRT$XLZ`) are the real magic here. The compiler emits TLS variables into `.CRT$XL` sections, where `x` is an uppercase letter between A and Z (exclusive). The linker then sorts those sections alphabetically (due to the `$`), and the result is a neat array of TLS initialization callbacks between `__xl_a` and `__xl_z`. That array is null-terminated, though! Normally, `__xl_z` serves as the null terminator; however, by pointing `AddressesOfCallBacks` to `__xl_a`, which just contains a null function pointer, we've effectively made it so that the PE loader will just immediately stop invoking TLS callbacks. Fix that by pointing to the first actual TLS callback instead (or `__xl_z` if there are none). --- lib/std/os/windows/tls.zig | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/std/os/windows/tls.zig b/lib/std/os/windows/tls.zig index e4fceebfc49811b626009ab38110e56553ffb9d5..f98e22b620c10a746b77e0467c37a0c2b71de19c 100644 --- a/lib/std/os/windows/tls.zig +++ b/lib/std/os/windows/tls.zig @@ -20,8 +20,6 @@ comptime { } // TODO this is how I would like it to be expressed -// TODO also note, ReactOS has a +1 on StartAddressOfRawData and AddressOfCallBacks. Investigate -// why they do that. //export const _tls_used linksection(".rdata$T") = std.os.windows.IMAGE_TLS_DIRECTORY { // .StartAddressOfRawData = @intFromPtr(&_tls_start), // .EndAddressOfRawData = @intFromPtr(&_tls_end), @@ -35,7 +33,7 @@ pub const IMAGE_TLS_DIRECTORY = extern struct { StartAddressOfRawData: *?*anyopaque, EndAddressOfRawData: *?*anyopaque, AddressOfIndex: *u32, - AddressOfCallBacks: *windows.PIMAGE_TLS_CALLBACK, + AddressOfCallBacks: [*:null]windows.PIMAGE_TLS_CALLBACK, SizeOfZeroFill: u32, Characteristics: u32, }; @@ -43,7 +41,10 @@ export const _tls_used linksection(".rdata$T") = IMAGE_TLS_DIRECTORY{ .StartAddressOfRawData = &_tls_start, .EndAddressOfRawData = &_tls_end, .AddressOfIndex = &_tls_index, - .AddressOfCallBacks = &__xl_a, + // __xl_a is just a global variable containing a null pointer; the actual callbacks sit in + // between __xl_a and __xl_z. So we need to skip over __xl_a here. If there are no callbacks, + // this just means we point to __xl_z (the null terminator). + .AddressOfCallBacks = @as([*:null]windows.PIMAGE_TLS_CALLBACK, @ptrCast(&__xl_a)) + 1, .SizeOfZeroFill = 0, .Characteristics = 0, }; -- 2.54.0