authorgravatar for alex@alexrp.comAlex Rønne Petersen <alex@alexrp.com> 2024-08-03 20:18:14+02:00
committergravatar for alex@alexrp.comAlex Rønne Petersen <alex@alexrp.com> 2024-08-03 20:55:00+02:00
logcb1fffb29eae6eafd46c87d3950911f496288ab2
tree5ccef985fde813ba6d5380107e67e68365132b7e
parent1d8fca0060ecfe700df7722e6763de94e2bd0b51
signaturebadge-check Signed by SSH key SHA256:7B/LJ7bpR1eX8aCXSr4mtd5M45VMPKcx9zY8e95b5QM

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<x>` 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).

1 files changed, 5 insertions(+), 4 deletions(-)

lib/std/os/windows/tls.zig+5-4
...@@ -20,8 +20,6 @@ comptime {...@@ -20,8 +20,6 @@ comptime {
20}20}
2121
22// TODO this is how I would like it to be expressed22// TODO this is how I would like it to be expressed
23// TODO also note, ReactOS has a +1 on StartAddressOfRawData and AddressOfCallBacks. Investigate
24// why they do that.
25//export const _tls_used linksection(".rdata$T") = std.os.windows.IMAGE_TLS_DIRECTORY {23//export const _tls_used linksection(".rdata$T") = std.os.windows.IMAGE_TLS_DIRECTORY {
26// .StartAddressOfRawData = @intFromPtr(&_tls_start),24// .StartAddressOfRawData = @intFromPtr(&_tls_start),
27// .EndAddressOfRawData = @intFromPtr(&_tls_end),25// .EndAddressOfRawData = @intFromPtr(&_tls_end),
...@@ -35,7 +33,7 @@ pub const IMAGE_TLS_DIRECTORY = extern struct {...@@ -35,7 +33,7 @@ pub const IMAGE_TLS_DIRECTORY = extern struct {
35 StartAddressOfRawData: *?*anyopaque,33 StartAddressOfRawData: *?*anyopaque,
36 EndAddressOfRawData: *?*anyopaque,34 EndAddressOfRawData: *?*anyopaque,
37 AddressOfIndex: *u32,35 AddressOfIndex: *u32,
38 AddressOfCallBacks: *windows.PIMAGE_TLS_CALLBACK,36 AddressOfCallBacks: [*:null]windows.PIMAGE_TLS_CALLBACK,
39 SizeOfZeroFill: u32,37 SizeOfZeroFill: u32,
40 Characteristics: u32,38 Characteristics: u32,
41};39};
...@@ -43,7 +41,10 @@ export const _tls_used linksection(".rdata$T") = IMAGE_TLS_DIRECTORY{...@@ -43,7 +41,10 @@ export const _tls_used linksection(".rdata$T") = IMAGE_TLS_DIRECTORY{
43 .StartAddressOfRawData = &_tls_start,41 .StartAddressOfRawData = &_tls_start,
44 .EndAddressOfRawData = &_tls_end,42 .EndAddressOfRawData = &_tls_end,
45 .AddressOfIndex = &_tls_index,43 .AddressOfIndex = &_tls_index,
46 .AddressOfCallBacks = &__xl_a,44 // __xl_a is just a global variable containing a null pointer; the actual callbacks sit in
45 // between __xl_a and __xl_z. So we need to skip over __xl_a here. If there are no callbacks,
46 // this just means we point to __xl_z (the null terminator).
47 .AddressOfCallBacks = @as([*:null]windows.PIMAGE_TLS_CALLBACK, @ptrCast(&__xl_a)) + 1,
47 .SizeOfZeroFill = 0,48 .SizeOfZeroFill = 0,
48 .Characteristics = 0,49 .Characteristics = 0,
49};50};