Skip to content
This repository was archived by the owner on Feb 8, 2024. It is now read-only.

Commit 8fc1b82

Browse files
committed
fix TLS alignment for Windows 8.0 or below
1 parent 8be1fea commit 8fc1b82

File tree

2 files changed

+96
-1
lines changed

2 files changed

+96
-1
lines changed

src/core/sys/windows/threadaux.d

+10
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,16 @@ public:
319319
return tls;
320320
}
321321

322+
// get the address of the entry in the TLS array for the current thread
323+
// use C mangling to access it from msvc.c
324+
extern(C) void** GetTlsEntryAdr()
325+
{
326+
if( void** teb = getTEB() )
327+
if( void** tlsarray = cast(void**) teb[11] )
328+
return tlsarray + _tls_index;
329+
return null;
330+
}
331+
322332
///////////////////////////////////////////////////////////////////
323333
// run rt_moduleTlsCtor in the context of the given thread
324334
void thread_moduleTlsCtor( uint id )

src/ldc/msvc.c

+86-1
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,92 @@ __declspec(allocate(".CRT$XIY")) static _PF _ctor = &ctor;
8989
__declspec(allocate(".CRT$XTY")) static _PF _dtor = &dtor;
9090

9191
#pragma data_seg(pop)
92-
#endif
92+
93+
/*********************************************************
94+
* Windows before Windows 8.1 does not support TLS alignment to anything
95+
* higher than 8/16 bytes for Win32 and Win64, respectively.
96+
* Some optimizations in LLVM (e.g. using aligned XMM access) do require
97+
* higher alignments, though. In addition, the programmer can use align()
98+
* to specify even larger requirements.
99+
* Fixing the alignment is done by adding a TLS callback that allocates
100+
* a new copy of the TLS segment if the current one is not aligned properly.
101+
*/
102+
103+
__declspec(thread) void* originalTLS; // saves the address of the original TLS to restore it before termination
104+
105+
extern void** GetTlsEntryAdr();
106+
int _tls_index;
107+
108+
BOOL WINAPI fix_tlsAlignment(HINSTANCE hModule, DWORD fdwReason, LPVOID lpvReserved)
109+
{
110+
if (fdwReason == DLL_PROCESS_DETACH || fdwReason == DLL_THREAD_DETACH)
111+
{
112+
if (originalTLS)
113+
{
114+
// restore original pointer
115+
void** tlsAdr = GetTlsEntryAdr();
116+
void* allocAdr = ((void**)*tlsAdr)[-1];
117+
*tlsAdr = originalTLS;
118+
HeapFree(GetProcessHeap(), 0, allocAdr);
119+
}
120+
}
121+
else
122+
{
123+
// crawl through the image to find the TLS alignment
124+
char* imageBase = (char*)hModule;
125+
PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)hModule;
126+
PIMAGE_NT_HEADERS pNtHeaders = (PIMAGE_NT_HEADERS)(imageBase + pDosHeader->e_lfanew);
127+
PIMAGE_SECTION_HEADER pSectionHeader = (PIMAGE_SECTION_HEADER)(pNtHeaders + 1);
128+
PIMAGE_DATA_DIRECTORY dataDir = pNtHeaders->OptionalHeader.DataDirectory + IMAGE_DIRECTORY_ENTRY_TLS;
129+
if (dataDir->VirtualAddress) // any TLS entry
130+
{
131+
PIMAGE_TLS_DIRECTORY tlsDir = (PIMAGE_TLS_DIRECTORY)(imageBase + dataDir->VirtualAddress);
132+
int alignShift = ((tlsDir->Characteristics >> 20) & 0xf);
133+
134+
if (alignShift)
135+
{
136+
int alignment = 1 << (alignShift - 1);
137+
void** tlsAdr = GetTlsEntryAdr();
138+
if ((SIZE_T)*tlsAdr & (alignment - 1))
139+
{
140+
// this implementation does about the same as Windows 8.1.
141+
HANDLE heap = GetProcessHeap();
142+
SIZE_T tlsSize = tlsDir->EndAddressOfRawData - tlsDir->StartAddressOfRawData + tlsDir->SizeOfZeroFill;
143+
SIZE_T allocSize = tlsSize + alignment + sizeof(void*);
144+
void* p = HeapAlloc(heap, 0, allocSize);
145+
if (!p)
146+
return 0;
147+
148+
void* aligned = (void*)(((SIZE_T)p + alignment + sizeof(PVOID)) & ~(alignment - 1));
149+
void* old = *tlsAdr;
150+
((void**)aligned)[-1] = p; // save base pointer for freeing
151+
memcpy(aligned, old, tlsSize);
152+
*tlsAdr = aligned;
153+
originalTLS = old;
154+
}
155+
}
156+
}
157+
}
158+
return 1;
159+
}
160+
161+
// the C++ TLS callbacks are written to ".CRT$XLC", but actually start after ".CRT$XLA".
162+
// Using ".CRT$XLB" allows this to come first in the array of TLS callbacks. This
163+
// guarantees that pointers saved within C++ TLS callbacks are not pointing into
164+
// abandoned memory
165+
166+
typedef BOOL WINAPI _TLSCB(HINSTANCE, DWORD, LPVOID);
167+
168+
#pragma data_seg(push)
169+
170+
#pragma section(".CRT$XLB", long, read)
171+
172+
#pragma data_seg(".CRT$XLB")
173+
__declspec(allocate(".CRT$XLB")) static _TLSCB* _pfix_tls = &fix_tlsAlignment;
174+
175+
#pragma data_seg(pop)
93176

94177
#endif
95178

179+
#endif // _WIN32
180+

0 commit comments

Comments
 (0)