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 }