1 /**
2  * This module provides OS specific helper function for DLL support
3  *
4  * Copyright: Copyright Digital Mars 2010 - 2012.
5  * License: Distributed under the
6  *      $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0).
7  *    (See accompanying file LICENSE)
8  * Authors:   Rainer Schuetze
9  * Source: $(DRUNTIMESRC core/sys/windows/_dll.d)
10  */
11 
12 module nulib.system.win32.dll;
13 version (Windows)  : import nulib.system.win32.winbase;
14 import nulib.system.win32.winnt;
15 import core.stdc.string;
16 import core.runtime;
17 
18 public import nulib.system.win32.threadaux;
19 
20 ///////////////////////////////////////////////////////////////////
21 // support fixing implicit TLS for dynamically loaded DLLs on Windows XP
22 
23 // in this special case, we have to treat _tlsstart and _tlsend as non-TLS variables
24 //  as they are used to simulate TLS when it is not set up under XP. In this case we must
25 //  not access tls_array[tls_index] as needed for thread local _tlsstart and _tlsend
26 extern (C) {
27     version (Win32) {
28         version (CRuntime_Microsoft) {
29             extern __gshared byte _tls_start;
30             extern __gshared byte _tls_end;
31             extern __gshared void* __xl_a;
32 
33             alias _tlsstart = _tls_start ;
34             alias _tls_end = _tlsend;
35             alias __xl_a = _tls_callbacks_a;
36         }
37         extern __gshared int _tls_index;
38     }
39 }
40 
41 private:
42 extern (C) {
43     import core.attribute : weak;
44 
45     void rt_moduleTlsCtor() @weak {
46 
47     }
48 
49     void rt_moduleTlsDtor() @weak {
50 
51     }
52 }
53 
54 struct dll_aux {
55     // don't let symbols leak into other modules
56     version (Win32) {
57         struct LdrpTlsListEntry {
58             LdrpTlsListEntry* next;
59             LdrpTlsListEntry* prev;
60             void* tlsstart;
61             void* tlsend;
62             void* ptr_tlsindex;
63             void* callbacks;
64             void* zerofill;
65             int tlsindex;
66         }
67 
68         alias fnRtlAllocateHeap = extern (Windows)
69         void* function(void* HeapHandle, uint Flags, size_t Size) nothrow;
70 
71         // find a code sequence and return the address after the sequence
72         static void* findCodeSequence(void* adr, int len, ref ubyte[] pattern) nothrow {
73             if (!adr)
74                 return null;
75 
76             ubyte* code = cast(ubyte*) adr;
77             for (int p = 0; p < len; p++) {
78                 if (code[p .. p + pattern.length] == pattern[0 .. $]) {
79                     ubyte* padr = code + p + pattern.length;
80                     return padr;
81                 }
82             }
83             return null;
84         }
85 
86         // find a code sequence and return the (relative) address that follows
87         static void* findCodeReference(void* adr, int len, ref ubyte[] pattern, bool relative) nothrow {
88             if (!adr)
89                 return null;
90 
91             ubyte* padr = cast(ubyte*) findCodeSequence(adr, len, pattern);
92             if (padr) {
93                 if (relative)
94                     return padr + 4 + *cast(int*) padr;
95                 return *cast(void**) padr;
96             }
97             return null;
98         }
99 
100         // crawl through ntdll to find function _LdrpAllocateTls@0 and references
101         //  to _LdrpNumberOfTlsEntries, _NtdllBaseTag and _LdrpTlsList
102         // LdrInitializeThunk
103         // -> _LdrpInitialize@12
104         // -> _LdrpInitializeThread@4
105         // -> _LdrpAllocateTls@0
106         // -> je chunk
107         //     _LdrpNumberOfTlsEntries - number of entries in TlsList
108         //     _NtdllBaseTag           - tag used for RtlAllocateHeap
109         //     _LdrpTlsList            - root of the double linked list with TlsList entries
110 
111         __gshared int* pNtdllBaseTag; // remembered for reusage in addTlsData
112 
113         __gshared ubyte[] jmp_LdrpInitialize = [0x33, 0xED, 0xE9]; // xor ebp,ebp; jmp _LdrpInitialize
114         __gshared ubyte[] jmp__LdrpInitialize = [0x5D, 0xE9]; // pop ebp; jmp __LdrpInitialize
115         __gshared ubyte[] jmp__LdrpInitialize_xp64 = [
116             0x5D, 0x90, 0x90, 0x90, 0x90, 0x90
117         ]; // pop ebp; nop; nop; nop; nop; nop;
118         __gshared ubyte[] call_LdrpInitializeThread = [0xFF, 0x75, 0x08, 0xE8]; // push [ebp+8]; call _LdrpInitializeThread
119         __gshared ubyte[] call_LdrpAllocateTls = [0x00, 0x00, 0xE8]; // jne 0xc3; call _LdrpAllocateTls
120         __gshared ubyte[] call_LdrpAllocateTls_svr03 = [0x65, 0xfc, 0x00, 0xE8]; // and [ebp+fc], 0; call _LdrpAllocateTls
121         __gshared ubyte[] jne_LdrpAllocateTls = [0x0f, 0x85]; // jne body_LdrpAllocateTls
122         __gshared ubyte[] mov_LdrpNumberOfTlsEntries = [0x8B, 0x0D]; // mov ecx, _LdrpNumberOfTlsEntries
123         __gshared ubyte[] mov_NtdllBaseTag = [0x51, 0x8B, 0x0D]; // push ecx; mov ecx, _NtdllBaseTag
124         __gshared ubyte[] mov_NtdllBaseTag_srv03 = [0x50, 0xA1]; // push eax; mov eax, _NtdllBaseTag
125         __gshared ubyte[] mov_LdrpTlsList = [0x8B, 0x3D]; // mov edi, _LdrpTlsList
126 
127         static LdrpTlsListEntry* addTlsListEntry(void** peb, void* tlsstart, void* tlsend, void* tls_callbacks_a, int* tlsindex) nothrow {
128             HANDLE hnd = GetModuleHandleA("NTDLL");
129             assert(hnd, "cannot get module handle for ntdll");
130             ubyte* fn = cast(ubyte*) GetProcAddress(hnd, "LdrInitializeThunk");
131             assert(fn, "cannot find LdrInitializeThunk in ntdll");
132 
133             void* pLdrpInitialize = findCodeReference(fn, 20, jmp_LdrpInitialize, true);
134             void* p_LdrpInitialize = findCodeReference(pLdrpInitialize, 40, jmp__LdrpInitialize, true);
135             if (!p_LdrpInitialize)
136                 p_LdrpInitialize = findCodeSequence(pLdrpInitialize, 40, jmp__LdrpInitialize_xp64);
137             void* pLdrpInitializeThread = findCodeReference(p_LdrpInitialize, 200, call_LdrpInitializeThread, true);
138             void* pLdrpAllocateTls = findCodeReference(pLdrpInitializeThread, 40, call_LdrpAllocateTls, true);
139             if (!pLdrpAllocateTls)
140                 pLdrpAllocateTls = findCodeReference(pLdrpInitializeThread, 100, call_LdrpAllocateTls_svr03, true);
141             void* pBodyAllocateTls = findCodeReference(pLdrpAllocateTls, 40, jne_LdrpAllocateTls, true);
142 
143             int* pLdrpNumberOfTlsEntries = cast(int*) findCodeReference(pBodyAllocateTls, 60, mov_LdrpNumberOfTlsEntries, false);
144             pNtdllBaseTag = cast(int*) findCodeReference(pBodyAllocateTls, 30, mov_NtdllBaseTag, false);
145             if (!pNtdllBaseTag)
146                 pNtdllBaseTag = cast(int*) findCodeReference(pBodyAllocateTls, 30, mov_NtdllBaseTag_srv03, false);
147             LdrpTlsListEntry* pLdrpTlsList = cast(LdrpTlsListEntry*) findCodeReference(pBodyAllocateTls, 80, mov_LdrpTlsList, false);
148 
149             if (!pLdrpNumberOfTlsEntries || !pNtdllBaseTag || !pLdrpTlsList)
150                 return null;
151 
152             fnRtlAllocateHeap fnAlloc = cast(fnRtlAllocateHeap) GetProcAddress(hnd, "RtlAllocateHeap");
153             if (!fnAlloc)
154                 return null;
155 
156             // allocate new TlsList entry (adding 0xC0000 to the tag is obviously a flag also usesd by
157             //  the nt-loader, could be the result of HEAP_MAKE_TAG_FLAGS(0,HEAP_NO_SERIALIZE|HEAP_GROWABLE)
158             //  but this is not documented in the msdn entry for RtlAlloateHeap
159             void* heap = peb[6];
160             LdrpTlsListEntry* entry = cast(LdrpTlsListEntry*)(*fnAlloc)(heap, *pNtdllBaseTag | 0xc0000, LdrpTlsListEntry
161                     .sizeof);
162             if (!entry)
163                 return null;
164 
165             // fill entry
166             entry.tlsstart = tlsstart;
167             entry.tlsend = tlsend;
168             entry.ptr_tlsindex = tlsindex;
169             entry.callbacks = tls_callbacks_a;
170             entry.zerofill = null;
171             entry.tlsindex = *pLdrpNumberOfTlsEntries;
172 
173             // and add it to the end of TlsList
174             *tlsindex = *pLdrpNumberOfTlsEntries;
175             entry.next = pLdrpTlsList;
176             entry.prev = pLdrpTlsList.prev;
177             pLdrpTlsList.prev.next = entry;
178             pLdrpTlsList.prev = entry;
179             (*pLdrpNumberOfTlsEntries)++;
180 
181             return entry;
182         }
183 
184         // reallocate TLS array and create a copy of the TLS data section
185         static bool addTlsData(void** teb, void* tlsstart, void* tlsend, int tlsindex) nothrow {
186             HANDLE hnd = GetModuleHandleA("NTDLL");
187             assert(hnd, "cannot get module handle for ntdll");
188 
189             fnRtlAllocateHeap fnAlloc = cast(fnRtlAllocateHeap) GetProcAddress(hnd, "RtlAllocateHeap");
190             if (!fnAlloc || !pNtdllBaseTag)
191                 return false;
192 
193             void** peb = cast(void**) teb[12];
194             void* heap = peb[6];
195 
196             auto sz = tlsend - tlsstart;
197             void* tlsdata = cast(void*)(*fnAlloc)(heap, *pNtdllBaseTag | 0xc0000, sz);
198             if (!tlsdata)
199                 return false;
200 
201             // no relocations! not even self-relocations. Windows does not do them.
202             core.stdc..string.memcpy(tlsdata, tlsstart, sz);
203 
204             // create copy of tls pointer array
205             void** array = cast(void**)(*fnAlloc)(heap, *pNtdllBaseTag | 0xc0000, (tlsindex + 1) * (void*)
206                     .sizeof);
207             if (!array)
208                 return false;
209 
210             if (tlsindex > 0 && teb[11])
211                 core.stdc..string.memcpy(array, teb[11], tlsindex * (void*).sizeof);
212             array[tlsindex] = tlsdata;
213             teb[11] = cast(void*) array;
214 
215             // let the old array leak, in case a oncurrent thread is still relying on it
216             return true;
217         }
218     } // Win32
219 
220     alias BOOLEAN = bool;
221 
222     struct UNICODE_STRING {
223         short Length;
224         short MaximumLength;
225         wchar* Buffer;
226     }
227 
228     struct LIST_ENTRY {
229         LIST_ENTRY* next;
230         LIST_ENTRY* prev;
231     }
232 
233     // the following structures can be found here:
234     // https://www.geoffchappell.com/studies/windows/win32/ntdll/structs/ldr_data_table_entry.htm
235     // perhaps this should be same as LDR_DATA_TABLE_ENTRY, which is introduced with PEB_LDR_DATA
236     struct LDR_MODULE {
237         LIST_ENTRY InLoadOrderModuleList;
238         LIST_ENTRY InMemoryOrderModuleList;
239         LIST_ENTRY InInitializationOrderModuleList;
240         PVOID BaseAddress;
241         PVOID EntryPoint;
242         SIZE_T SizeOfImage;
243         UNICODE_STRING FullDllName;
244         UNICODE_STRING BaseDllName;
245         ULONG Flags;
246         SHORT LoadCount; // obsolete after Version 6.1
247         SHORT TlsIndex;
248         LIST_ENTRY HashTableEntry;
249         ULONG TimeDateStamp;
250         PVOID EntryPointActivationContext;
251         PVOID PatchInformation;
252         LDR_DDAG_NODE* DdagNode; // starting with Version 6.2
253     }
254 
255     struct LDR_DDAG_NODE {
256         LIST_ENTRY Modules;
257         void* ServiceTagList; // LDR_SERVICE_TAG_RECORD
258         ULONG LoadCount;
259         ULONG ReferenceCount; // Version 10: ULONG LoadWhileUnloadingCount;
260         ULONG DependencyCount; // Version 10: ULONG LowestLink;
261     }
262 
263     struct PEB_LDR_DATA {
264         ULONG Length;
265         BOOLEAN Initialized;
266         PVOID SsHandle;
267         LIST_ENTRY InLoadOrderModuleList;
268         LIST_ENTRY InMemoryOrderModuleList;
269         LIST_ENTRY InInitializationOrderModuleList;
270     }
271 
272     static LDR_MODULE* findLdrModule(HINSTANCE hInstance, void** peb) nothrow @nogc {
273         PEB_LDR_DATA* ldrData = cast(PEB_LDR_DATA*) peb[3];
274         LIST_ENTRY* root = &ldrData.InLoadOrderModuleList;
275         for (LIST_ENTRY* entry = root.next; entry != root; entry = entry.next) {
276             LDR_MODULE* ldrMod = cast(LDR_MODULE*) entry;
277             if (ldrMod.BaseAddress == hInstance)
278                 return ldrMod;
279         }
280         return null;
281     }
282 
283     static bool setDllTlsUsage(HINSTANCE hInstance, void** peb) nothrow {
284         LDR_MODULE* thisMod = findLdrModule(hInstance, peb);
285         if (!thisMod)
286             return false;
287 
288         thisMod.TlsIndex = -1; // uses TLS (not the index itself)
289         thisMod.LoadCount = -1; // never unload
290         return true;
291     }
292 }
293 
294 public:
295 
296 /* *****************************************************
297  * Fix implicit thread local storage for the case when a DLL is loaded
298  * dynamically after process initialization.
299  * The link time variables are passed to allow placing this function into
300  * an RTL DLL itself.
301  * The problem is described in Bugzilla 3342 and
302  * http://www.nynaeve.net/?p=187, to quote from the latter:
303  *
304  * "When a DLL using implicit TLS is loaded, because the loader doesn't process the TLS
305  *  directory, the _tls_index value is not initialized by the loader, nor is there space
306  *  allocated for module's TLS data in the ThreadLocalStoragePointer arrays of running
307  *  threads. The DLL continues to load, however, and things will appear to work... until the
308  *  first access to a __declspec(thread) variable occurs, that is."
309  *
310  * _tls_index is initialized by the compiler to 0, so we can use this as a test.
311  * Returns:
312  *	true for success, false for failure
313  */
314 bool dll_fixTLS(HINSTANCE hInstance, void* tlsstart, void* tlsend, void* tls_callbacks_a, int* tlsindex) nothrow {
315     version (GNU_EMUTLS)
316         return true;
317     else version (Win64)
318         return true; // fixed
319     else version (Win32) {
320         /* If the OS has allocated a TLS slot for us, we don't have to do anything
321      * tls_index 0 means: the OS has not done anything, or it has allocated slot 0
322      * Vista and later Windows systems should do this correctly and not need
323      * this function.
324      */
325         if (*tlsindex != 0)
326             return true;
327 
328         void** peb;
329         asm pure nothrow @nogc {
330             mov EAX, FS:
331             [0x30];
332             mov peb, EAX;
333         }
334         dll_aux.LDR_MODULE* ldrMod = dll_aux.findLdrModule(hInstance, peb);
335         if (!ldrMod)
336             return false; // not in module list, bail out
337         if (ldrMod.TlsIndex != 0)
338             return true; // the OS has already setup TLS
339 
340         dll_aux.LdrpTlsListEntry* entry = dll_aux.addTlsListEntry(peb, tlsstart, tlsend, tls_callbacks_a, tlsindex);
341         if (!entry)
342             return false;
343 
344         scope (failure)
345             assert(0); // enforce nothrow, Bugzilla 13561
346 
347         if (!enumProcessThreads(
348                 function(uint id, void* context)nothrow{
349                 dll_aux.LdrpTlsListEntry* entry = cast(dll_aux.LdrpTlsListEntry*) context;
350                 return dll_aux.addTlsData(getTEB(id), entry.tlsstart, entry.tlsend, entry.tlsindex);
351             }, entry))
352             return false;
353 
354         ldrMod.TlsIndex = -1; // flag TLS usage (not the index itself)
355         ldrMod.LoadCount = -1; // prevent unloading of the DLL,
356         // since XP does not keep track of used TLS entries
357         return true;
358     }
359 }
360 
361 private extern (Windows) ULONGLONG VerSetConditionMask(ULONGLONG, DWORD, BYTE) nothrow @nogc;
362 
363 private bool isWindows8OrLater() nothrow @nogc {
364     OSVERSIONINFOEXW osvi;
365     osvi.dwOSVersionInfoSize = osvi.sizeof;
366     DWORDLONG dwlConditionMask = VerSetConditionMask(
367         VerSetConditionMask(
368             VerSetConditionMask(
369             0, VER_MAJORVERSION, VER_GREATER_EQUAL),
370             VER_MINORVERSION, VER_GREATER_EQUAL),
371         VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL);
372 
373     osvi.dwMajorVersion = 6;
374     osvi.dwMinorVersion = 2;
375     osvi.wServicePackMajor = 0;
376 
377     return VerifyVersionInfoW(&osvi, VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR, dwlConditionMask) != FALSE;
378 }
379 
380 /* *****************************************************
381  * Get the process reference count for the given DLL handle
382  * Params:
383  *   hInstance = DLL instance handle
384  * Returns:
385  *   the reference count for the DLL in the current process,
386  *   -1 if the DLL is implicitely loaded with the process
387  *   or -2 if the DLL handle is invalid
388  */
389 int dll_getRefCount(HINSTANCE hInstance) nothrow @nogc {
390     void** peb;
391     version (D_InlineAsm_X86_64) {
392         asm pure nothrow @nogc {
393             mov RAX, 0x60;
394             mov RAX, GS:
395             [RAX];
396             mov peb, RAX;
397         }
398     } else version (D_InlineAsm_X86) {
399         asm pure nothrow @nogc {
400             mov EAX, FS:
401             [0x30];
402             mov peb, EAX;
403         }
404     } else version (AArch64) {
405         asm pure nothrow @nogc {
406             "ldr %0, [x18,%1]" : "=r"(peb) : "r"(0x30);
407         }
408     } else version (GNU_InlineAsm) {
409         version (X86_64)
410             asm pure nothrow @nogc {
411             "movq %%gs:0x60, %0;" : "=r"(peb);
412         } else version (X86)
413             asm pure nothrow @nogc {
414             "movl %%fs:0x30, %0;" : "=r"(peb);
415         }
416     }
417     dll_aux.LDR_MODULE* ldrMod = dll_aux.findLdrModule(hInstance, peb);
418     if (!ldrMod)
419         return -2; // not in module list, bail out
420     if (isWindows8OrLater())
421         return ldrMod.DdagNode.LoadCount;
422     return ldrMod.LoadCount;
423 }
424 
425 /*****************************
426  * To be called from DllMain with reason DLL_PROCESS_ATTACH
427  *
428  * fixup TLS storage, initialize runtime and attach to threads
429  * Returns:
430  *	true = success, false = failure
431  */
432 bool dll_process_attach(HINSTANCE hInstance, bool attach_threads,
433     void* tlsstart, void* tlsend, void* tls_callbacks_a, int* tlsindex) {
434     version (Win32) {
435         if (!dll_fixTLS(hInstance, tlsstart, tlsend, tls_callbacks_a, tlsindex))
436             return false;
437     }
438 
439     Runtime.initialize();
440 
441     version (Shared) {
442         return true;
443     } else {
444         if (!attach_threads)
445             return true;
446 
447         // attach to all other threads
448         return enumProcessThreads(
449             function(uint id, void* context) {
450             if (!thread_findByAddr(id) && !findLowLevelThread(id)) {
451                 // if the OS has not prepared TLS for us, don't attach to the thread
452                 if (GetTlsDataAddress(id)) {
453                     thread_attachByAddr(id);
454                     thread_moduleTlsCtor(id);
455                 }
456             }
457             return true;
458         }, null);
459     } // !Shared
460 }
461 
462 version (Shared) version (DigitalMars) private extern (C) extern __gshared void* __ImageBase;
463 
464 /** same as above, but checking for shared runtime
465  */
466 pragma(inline, false) // version (Shared) only set when compiling druntime
467 bool dll_process_attach(HINSTANCE hInstance, bool attach_threads = true) {
468     version (Win64) {
469         return dll_process_attach(hInstance, attach_threads,
470             null, null, null, null);
471     } else version (Win32) {
472         return dll_process_attach(hInstance, attach_threads,
473             &_tlsstart, &_tlsend, &_tls_callbacks_a, &_tls_index);
474     }
475 }
476 
477 /**
478  * to be called from DllMain with reason DLL_PROCESS_DETACH
479  */
480 pragma(inline, false) // version (Shared) only set when compiling druntime
481 void dll_process_detach(HINSTANCE hInstance, bool detach_threads = true) {
482     version (Shared) { /* not needed */ } else {
483 
484         // notify core.thread.joinLowLevelThread that the DLL is about to be unloaded
485         thread_DLLProcessDetaching = true;
486 
487         // detach from all other threads
488         if (detach_threads)
489             enumProcessThreads(
490                 function(uint id, void* context) {
491                 if (id != GetCurrentThreadId()) {
492                     if (auto t = thread_findByAddr(id)) {
493                         thread_moduleTlsDtor(id);
494                         if (!t.isMainThread())
495                             thread_detachByAddr(id);
496                     }
497                 }
498                 return true;
499             }, null);
500     } // !Shared
501 
502     Runtime.terminate();
503 }
504 
505 /*****************************
506 * Check whether the D runtime is built as a DLL or linked statically
507 *
508 * Returns:
509 *	true = DLL, false = static library
510 */
511 pragma(inline, false) // version (Shared) only set when compiling druntime
512 bool isSharedDRuntime() {
513     version (Shared)
514         return true;
515     else
516         return false;
517 }
518 
519 /* Make sure that tlsCtorRun is itself a tls variable
520  */
521 static bool tlsCtorRun;
522 static this() {
523     tlsCtorRun = true;
524 }
525 
526 static ~this() {
527     tlsCtorRun = false;
528 }
529 
530 /**
531  * To be called from DllMain with reason DLL_THREAD_ATTACH
532  * Returns:
533  *	true for success, false for failure
534  */
535 pragma(inline, false) // version (Shared) only set when compiling druntime
536 bool dll_thread_attach(bool attach_thread = true, bool initTls = true, HINSTANCE hInstance = null) {
537     version (Shared) { /* not needed */ } else {
538         version (Shared)
539             version (DigitalMars) {
540                 if (hInstance && hInstance != &__ImageBase)
541                     return true;
542             }
543 
544         // if the OS has not prepared TLS for us, don't attach to the thread
545         //  (happened when running under x64 OS)
546         auto tid = GetCurrentThreadId();
547         if (!GetTlsDataAddress(tid))
548             return false;
549         if (!thread_findByAddr(tid) && !findLowLevelThread(tid)) {
550             // only attach to thread and initalize it if it is not in the thread list (so it's not created by "new Thread")
551             if (attach_thread)
552                 thread_attachThis();
553             if (initTls && !tlsCtorRun) // avoid duplicate calls
554                 rt_moduleTlsCtor();
555         }
556     } // !Shared
557     return true;
558 }
559 
560 /**
561  * To be called from DllMain with reason DLL_THREAD_DETACH
562  * Returns:
563  *	true for success, false for failure
564  */
565 pragma(inline, false) // version (Shared) only set when compiling druntime
566 bool dll_thread_detach(bool detach_thread = true, bool exitTls = true, HINSTANCE hInstance = null) {
567     version (Shared) { /* not needed */ } else {
568         version (Shared)
569             version (DigitalMars) {
570                 if (hInstance && hInstance != &__ImageBase)
571                     return true;
572             }
573 
574         // if the OS has not prepared TLS for us, we did not attach to the thread
575         if (!GetTlsDataAddress(GetCurrentThreadId()))
576             return false;
577         if (thread_findByAddr(GetCurrentThreadId())) {
578             if (exitTls && tlsCtorRun) // avoid dtors to be run twice
579                 rt_moduleTlsDtor();
580             if (detach_thread)
581                 thread_detachThis();
582         }
583     } // !Shared
584     return true;
585 }
586 
587 /**********************************
588  * A mixin to provide a $(D DllMain) which calls the necessary
589  * D runtime initialization and termination functions automatically.
590  *
591  * Example:
592  * ---
593  * module dllmain;
594  * import nulib.system.win32.dll;
595  * mixin SimpleDllMain;
596  * ---
597  */
598 mixin template SimpleDllMain() {
599     import nulib.system.win32.windef : HINSTANCE, BOOL, DWORD, LPVOID;
600 
601     extern (Windows)
602     BOOL DllMain(HINSTANCE hInstance, DWORD ulReason, LPVOID reserved) {
603         import nulib.system.win32.winnt;
604         import nulib.system.win32.dll : dll_process_attach, dll_process_detach,
605             dll_thread_attach, dll_thread_detach;
606 
607         switch (ulReason) {
608         default:
609             assert(0);
610         case DLL_PROCESS_ATTACH:
611             return dll_process_attach(hInstance, true);
612 
613         case DLL_PROCESS_DETACH:
614             dll_process_detach(hInstance, true);
615             return true;
616 
617         case DLL_THREAD_ATTACH:
618             return dll_thread_attach(true, true, hInstance);
619 
620         case DLL_THREAD_DETACH:
621             return dll_thread_detach(true, true, hInstance);
622         }
623     }
624 }