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