diff --git a/src/core/sys/windows/threadaux.d b/src/core/sys/windows/threadaux.d index 17c7dd0246..ab92918912 100644 --- a/src/core/sys/windows/threadaux.d +++ b/src/core/sys/windows/threadaux.d @@ -319,6 +319,16 @@ public: return tls; } + // get the address of the entry in the TLS array for the current thread + // use C mangling to access it from msvc.c + extern(C) void** GetTlsEntryAdr() + { + if( void** teb = getTEB() ) + if( void** tlsarray = cast(void**) teb[11] ) + return tlsarray + _tls_index; + return null; + } + /////////////////////////////////////////////////////////////////// // run rt_moduleTlsCtor in the context of the given thread void thread_moduleTlsCtor( uint id ) diff --git a/src/ldc/msvc.c b/src/ldc/msvc.c index cda6ce5f5a..ee67acec9c 100644 --- a/src/ldc/msvc.c +++ b/src/ldc/msvc.c @@ -1,95 +1,178 @@ -/** - * Implementation of support routines for synchronized blocks. - * - * Copyright: Copyright The LDC Developers 2012 - * License: Boost License 1.0. - * Authors: Kai Nacke - */ - -/* Copyright The LDC Developers 2012. - * Distributed under the Boost Software License, Version 1.0. - * (See accompanying file LICENSE or copy at - * http://www.boost.org/LICENSE_1_0.txt) - */ - -/* ================================= Win32 ============================ */ - -#if _WIN32 - -#if _MSC_VER || __MINGW64__ - -#include -#include - -const char* _data_start__; -const char* _data_end__; -const char* _bss_start__; -const char* _bss_end__; - -EXTERN_C IMAGE_DOS_HEADER __ImageBase; - -static void init_data_seg(void) -{ - // Get handle to this module (.exe/.dll) - HMODULE hModule = (HMODULE) &__ImageBase; - char* imageBase = (char*) hModule; - - // Get the DOS header - PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER) hModule; - - // Get the address of the NT headers - PIMAGE_NT_HEADERS pNtHeaders = (PIMAGE_NT_HEADERS) (imageBase + pDosHeader->e_lfanew); - - // After the NT headers comes the sections table - PIMAGE_SECTION_HEADER pSectionHeader = (PIMAGE_SECTION_HEADER) (pNtHeaders + 1); - - // Iterate over all sections - for (int i = 0; i < pNtHeaders->FileHeader.NumberOfSections; i++) - { - BYTE* name = pSectionHeader->Name; - if (memcmp(name, ".data", 6) == 0) - { - _data_start__ = imageBase + pSectionHeader->VirtualAddress; - _data_end__ = _data_start__ + pSectionHeader->Misc.VirtualSize; - } - else if (memcmp(name, ".bss", 5) == 0) - { - _bss_start__ = imageBase + pSectionHeader->VirtualAddress; - _bss_end__ = _bss_start__ + pSectionHeader->Misc.VirtualSize; - } - - pSectionHeader++; - } -} - - -typedef int (__cdecl *_PF)(void); - -static int __cdecl ctor(void) -{ - init_data_seg(); - return 0; -} - -static int __cdecl dtor(void) -{ - return 0; -} - - -#pragma data_seg(push) - -#pragma section(".CRT$XIY", long, read) -#pragma section(".CRT$XTY", long, read) - -#pragma data_seg(".CRT$XIY") -__declspec(allocate(".CRT$XIY")) static _PF _ctor = &ctor; - -#pragma data_seg(".CRT$XTY") -__declspec(allocate(".CRT$XTY")) static _PF _dtor = &dtor; - -#pragma data_seg(pop) -#endif - -#endif - +/** + * Implementation of support routines for synchronized blocks. + * + * Copyright: Copyright The LDC Developers 2012 + * License: Boost License 1.0. + * Authors: Kai Nacke + */ + +/* Copyright The LDC Developers 2012. + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE or copy at + * http://www.boost.org/LICENSE_1_0.txt) + */ + +/* ================================= Win32 ============================ */ + +#if _WIN32 + +#if _MSC_VER || __MINGW64__ + +#include +#include + +const char* _data_start__; +const char* _data_end__; +const char* _bss_start__; +const char* _bss_end__; + +EXTERN_C IMAGE_DOS_HEADER __ImageBase; + +static void init_data_seg(void) +{ + // Get handle to this module (.exe/.dll) + HMODULE hModule = (HMODULE) &__ImageBase; + char* imageBase = (char*) hModule; + + // Get the DOS header + PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER) hModule; + + // Get the address of the NT headers + PIMAGE_NT_HEADERS pNtHeaders = (PIMAGE_NT_HEADERS) (imageBase + pDosHeader->e_lfanew); + + // After the NT headers comes the sections table + PIMAGE_SECTION_HEADER pSectionHeader = (PIMAGE_SECTION_HEADER) (pNtHeaders + 1); + + // Iterate over all sections + for (int i = 0; i < pNtHeaders->FileHeader.NumberOfSections; i++) + { + BYTE* name = pSectionHeader->Name; + if (memcmp(name, ".data", 6) == 0) + { + _data_start__ = imageBase + pSectionHeader->VirtualAddress; + _data_end__ = _data_start__ + pSectionHeader->Misc.VirtualSize; + } + else if (memcmp(name, ".bss", 5) == 0) + { + _bss_start__ = imageBase + pSectionHeader->VirtualAddress; + _bss_end__ = _bss_start__ + pSectionHeader->Misc.VirtualSize; + } + + pSectionHeader++; + } +} + + +typedef int (__cdecl *_PF)(void); + +static int __cdecl ctor(void) +{ + init_data_seg(); + return 0; +} + +static int __cdecl dtor(void) +{ + return 0; +} + + +#pragma data_seg(push) + +#pragma section(".CRT$XIY", long, read) +#pragma section(".CRT$XTY", long, read) + +#pragma data_seg(".CRT$XIY") +__declspec(allocate(".CRT$XIY")) static _PF _ctor = &ctor; + +#pragma data_seg(".CRT$XTY") +__declspec(allocate(".CRT$XTY")) static _PF _dtor = &dtor; + +#pragma data_seg(pop) + +/********************************************************* + * Windows before Windows 8.1 does not support TLS alignment to anything + * higher than 8/16 bytes for Win32 and Win64, respectively. + * Some optimizations in LLVM (e.g. using aligned XMM access) do require + * higher alignments, though. In addition, the programmer can use align() + * to specify even larger requirements. + * Fixing the alignment is done by adding a TLS callback that allocates + * a new copy of the TLS segment if the current one is not aligned properly. + */ + +__declspec(thread) void* originalTLS; // saves the address of the original TLS to restore it before termination + +extern void** GetTlsEntryAdr(); + +BOOL WINAPI fix_tlsAlignment(HINSTANCE hModule, DWORD fdwReason, LPVOID lpvReserved) +{ + if (fdwReason == DLL_PROCESS_DETACH || fdwReason == DLL_THREAD_DETACH) + { + if (originalTLS) + { + // restore original pointer + void** tlsAdr = GetTlsEntryAdr(); + void* allocAdr = ((void**) *tlsAdr)[-1]; + *tlsAdr = originalTLS; + HeapFree(GetProcessHeap(), 0, allocAdr); + } + } + else + { + // crawl through the image to find the TLS alignment + char* imageBase = (char*) hModule; + PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER) hModule; + PIMAGE_NT_HEADERS pNtHeaders = (PIMAGE_NT_HEADERS) (imageBase + pDosHeader->e_lfanew); + PIMAGE_SECTION_HEADER pSectionHeader = (PIMAGE_SECTION_HEADER) (pNtHeaders + 1); + PIMAGE_DATA_DIRECTORY dataDir = pNtHeaders->OptionalHeader.DataDirectory + IMAGE_DIRECTORY_ENTRY_TLS; + if (dataDir->VirtualAddress) // any TLS entry + { + PIMAGE_TLS_DIRECTORY tlsDir = (PIMAGE_TLS_DIRECTORY) (imageBase + dataDir->VirtualAddress); + int alignShift = ((tlsDir->Characteristics >> 20) & 0xf); + + if (alignShift) + { + int alignmentMask = (1 << (alignShift - 1)) - 1; + void** tlsAdr = GetTlsEntryAdr(); + if ((SIZE_T)*tlsAdr & alignmentMask) + { + // this implementation does about the same as Windows 8.1. + HANDLE heap = GetProcessHeap(); + SIZE_T tlsSize = tlsDir->EndAddressOfRawData - tlsDir->StartAddressOfRawData + tlsDir->SizeOfZeroFill; + SIZE_T allocSize = tlsSize + alignmentMask + sizeof(void*); + void* p = HeapAlloc(heap, 0, allocSize); + if (!p) + return 0; + + void* aligned = (void*) (((SIZE_T) p + alignmentMask + sizeof(PVOID)) & ~alignmentMask); + void* old = *tlsAdr; + ((void**) aligned)[-1] = p; // save base pointer for freeing + memcpy(aligned, old, tlsSize); + *tlsAdr = aligned; + originalTLS = old; + } + } + } + } + return 1; +} + +// the C++ TLS callbacks are written to ".CRT$XLC", but actually start after ".CRT$XLA". +// Using ".CRT$XLB" allows this to come first in the array of TLS callbacks. This +// guarantees that pointers saved within C++ TLS callbacks are not pointing into +// abandoned memory + +typedef BOOL WINAPI _TLSCB(HINSTANCE, DWORD, LPVOID); + +#pragma data_seg(push) + +#pragma section(".CRT$XLB", long, read) + +#pragma data_seg(".CRT$XLB") +__declspec(allocate(".CRT$XLB")) static _TLSCB* _pfix_tls = &fix_tlsAlignment; + +#pragma data_seg(pop) + +#endif + +#endif // _WIN32