xref: /netbsd-src/external/gpl3/gcc/dist/libphobos/libdruntime/gcc/sections/pecoff.d (revision b1e838363e3c6fc78a55519254d99869742dd33c)
1 // PE/COFF-specific support for sections.
2 // Copyright (C) 2021-2022 Free Software Foundation, Inc.
3 
4 // GCC is free software; you can redistribute it and/or modify it under
5 // the terms of the GNU General Public License as published by the Free
6 // Software Foundation; either version 3, or (at your option) any later
7 // version.
8 
9 // GCC is distributed in the hope that it will be useful, but WITHOUT ANY
10 // WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 // FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 // for more details.
13 
14 // Under Section 7 of GPL version 3, you are granted additional
15 // permissions described in the GCC Runtime Library Exception, version
16 // 3.1, as published by the Free Software Foundation.
17 
18 // You should have received a copy of the GNU General Public License and
19 // a copy of the GCC Runtime Library Exception along with this program;
20 // see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
21 // <http://www.gnu.org/licenses/>.
22 
23 module gcc.sections.pecoff;
24 
25 version (Windows):
26 
27 import core.memory;
28 import core.stdc.stdlib;
29 import core.sys.windows.winbase;
30 import core.sys.windows.windef;
31 import core.sys.windows.winnt;
32 import rt.minfo;
33 import core.internal.container.array;
34 import core.internal.container.hashtab;
35 import gcc.sections.common;
36 
37 version (GNU_EMUTLS)
38     import gcc.emutls;
39 
40 alias DSO SectionGroup;
41 struct DSO
42 {
opApplyDSO43     static int opApply(scope int delegate(ref DSO) dg)
44     {
45         foreach (dso; _loadedDSOs)
46         {
47             if (auto res = dg(*dso))
48                 return res;
49         }
50         return 0;
51     }
52 
opApplyReverseDSO53     static int opApplyReverse(scope int delegate(ref DSO) dg)
54     {
55         foreach_reverse (dso; _loadedDSOs)
56         {
57             if (auto res = dg(*dso))
58                 return res;
59         }
60         return 0;
61     }
62 
immutableDSO63     @property immutable(ModuleInfo*)[] modules() const nothrow @nogc
64     {
65         return _moduleGroup.modules;
66     }
67 
68     @property ref inout(ModuleGroup) moduleGroup() inout return nothrow @nogc
69     {
70         return _moduleGroup;
71     }
72 
73     @property inout(void[])[] gcRanges() inout nothrow @nogc
74     {
75         return _gcRanges[];
76     }
77 
78 private:
79 
80     invariant()
81     {
82         safeAssert(_moduleGroup.modules.length > 0, "No modules for DSO.");
83     }
84 
85     void** _slot;
86     ModuleGroup _moduleGroup;
87     Array!(void[]) _gcRanges;
88 
versionDSO89     version (Shared)
90     {
91         Array!(void[]) _codeSegments; // array of code segments
92         Array!(DSO*) _deps; // D libraries needed by this DSO
93         void* _handle; // corresponding handle
94     }
95 }
96 
97 /****
98  * Boolean flag set to true while the runtime is initialized.
99  */
100 __gshared bool _isRuntimeInitialized;
101 
102 /****
103  * Gets called on program startup just before GC is initialized.
104  */
initSections()105 void initSections() nothrow @nogc
106 {
107     _isRuntimeInitialized = true;
108 }
109 
110 /***
111  * Gets called on program shutdown just after GC is terminated.
112  */
finiSections()113 void finiSections() nothrow @nogc
114 {
115     _isRuntimeInitialized = false;
116 }
117 
118 alias ScanDG = void delegate(void* pbeg, void* pend) nothrow;
119 
version(Shared)120 version (Shared)
121 {
122     import gcc.sections : pinLoadedLibraries, unpinLoadedLibraries,
123            inheritLoadedLibraries, cleanupLoadedLibraries;
124 
125     /***
126      * Called once per thread; returns array of thread local storage ranges
127      */
128     Array!(ThreadDSO)* initTLSRanges() @nogc nothrow
129     {
130         return &_loadedDSOs();
131     }
132 
133     void finiTLSRanges(Array!(ThreadDSO)* tdsos) @nogc nothrow
134     {
135         // Nothing to do here. tdsos used to point to the _loadedDSOs instance
136         // in the dying thread's TLS segment and as such is not valid anymore.
137         // The memory for the array contents was already reclaimed in
138         // cleanupLoadedLibraries().
139     }
140 
141     void scanTLSRanges(Array!(ThreadDSO)* tdsos, scope ScanDG dg) nothrow
142     {
143         version (GNU_EMUTLS)
144             _d_emutls_scan(dg);
145         else
146             static assert(0, "Native TLS unimplemented");
147     }
148 
149     // interface for core.thread to inherit loaded libraries
150     pragma(mangle, gcc.sections.pinLoadedLibraries.mangleof)
151     void* pinLoadedLibraries() nothrow @nogc
152     {
153         auto res = cast(Array!(ThreadDSO)*)calloc(1, Array!(ThreadDSO).sizeof);
154         res.length = _loadedDSOs.length;
155         foreach (i, ref tdso; _loadedDSOs)
156         {
157             (*res)[i] = tdso;
158             if (tdso._addCnt)
159             {
160                 // Increment the DLL ref for explicitly loaded libraries to pin them.
161                 char[MAX_PATH] buf;
162                 char[] buffer = buf[];
163                 const success = .LoadLibraryA(nameForDSO(tdso._pdso, buffer)) !is null;
164                 safeAssert(success, "Failed to increment DLL ref.");
165                 (*res)[i]._addCnt = 1; // new array takes over the additional ref count
166             }
167         }
168         return res;
169     }
170 
171     pragma(mangle, gcc.sections.unpinLoadedLibraries.mangleof)
172     void unpinLoadedLibraries(void* p) nothrow @nogc
173     {
174         auto pary = cast(Array!(ThreadDSO)*)p;
175         // In case something failed we need to undo the pinning.
176         foreach (ref tdso; *pary)
177         {
178             if (tdso._addCnt)
179             {
180                 auto handle = tdso._pdso._handle;
181                 safeAssert(handle !is null, "Invalid library handle.");
182                 .FreeLibrary(handle);
183             }
184         }
185         pary.reset();
186         .free(pary);
187     }
188 
189     // Called before TLS ctors are ran, copy over the loaded libraries
190     // of the parent thread.
191     pragma(mangle, gcc.sections.inheritLoadedLibraries.mangleof)
192     void inheritLoadedLibraries(void* p) nothrow @nogc
193     {
194         safeAssert(_loadedDSOs.empty, "DSOs have already been registered for this thread.");
195         _loadedDSOs.swap(*cast(Array!(ThreadDSO)*)p);
196         .free(p);
197     }
198 
199     // Called after all TLS dtors ran, decrements all remaining DLL refs.
200     pragma(mangle, gcc.sections.cleanupLoadedLibraries.mangleof)
201     void cleanupLoadedLibraries() nothrow @nogc
202     {
203         foreach (ref tdso; _loadedDSOs)
204         {
205             if (tdso._addCnt == 0) continue;
206 
207             auto handle = tdso._pdso._handle;
208             safeAssert(handle !is null, "Invalid DSO handle.");
209             for (; tdso._addCnt > 0; --tdso._addCnt)
210                 .FreeLibrary(handle);
211         }
212 
213         // Free the memory for the array contents.
214         _loadedDSOs.reset();
215     }
216 }
217 else
218 {
219     /***
220      * Called once per thread; returns array of thread local storage ranges
221      */
222     Array!(void[])* initTLSRanges() nothrow @nogc
223     {
224         return null;
225     }
226 
227     void finiTLSRanges(Array!(void[])* rngs) nothrow @nogc
228     {
229     }
230 
231     void scanTLSRanges(Array!(void[])* rngs, scope ScanDG dg) nothrow
232     {
233         version (GNU_EMUTLS)
234             _d_emutls_scan(dg);
235         else
236             static assert(0, "Native TLS unimplemented");
237     }
238 }
239 
240 private:
241 
version(Shared)242 version (Shared)
243 {
244     /*
245      * Array of thread local DSO metadata for all libraries loaded and
246      * initialized in this thread.
247      *
248      * Note:
249      *     A newly spawned thread will inherit these libraries.
250      * Note:
251      *     We use an array here to preserve the order of
252      *     initialization.  If that became a performance issue, we
253      *     could use a hash table and enumerate the DSOs during
254      *     loading so that the hash table values could be sorted when
255      *     necessary.
256      */
257     struct ThreadDSO
258     {
259         DSO* _pdso;
260         static if (_pdso.sizeof == 8) uint _refCnt, _addCnt;
261         else static if (_pdso.sizeof == 4) ushort _refCnt, _addCnt;
262         else static assert(0, "unimplemented");
263         alias _pdso this;
264     }
265 
266     @property ref Array!(ThreadDSO) _loadedDSOs() @nogc nothrow
267     {
268         static Array!(ThreadDSO) x;
269         return x;
270     }
271 
272     /*
273      * Set to true during rt_loadLibrary/rt_unloadLibrary calls.
274      */
275     bool _rtLoading;
276 
277     /*
278      * Hash table to map the native handle (as returned by dlopen)
279      * to the corresponding DSO*, protected by a mutex.
280      */
281     __gshared CRITICAL_SECTION _handleToDSOMutex;
282     @property ref HashTab!(void*, DSO*) _handleToDSO() @nogc nothrow
283     {
284         __gshared HashTab!(void*, DSO*) x;
285         return x;
286     }
287 }
288 else
289 {
290     /*
291      * Static DSOs loaded by the runtime linker. This includes the
292      * executable. These can't be unloaded.
293      */
294     @property ref Array!(DSO*) _loadedDSOs() @nogc nothrow
295     {
296         __gshared Array!(DSO*) x;
297         return x;
298     }
299 
300     enum _rtLoading = false;
301 }
302 
303 ///////////////////////////////////////////////////////////////////////////////
304 // Compiler to runtime interface.
305 ///////////////////////////////////////////////////////////////////////////////
306 
307 /****
308  * This data structure is generated by the compiler, and then passed to
309  * _d_dso_registry().
310  */
311 struct CompilerDSOData
312 {
313     size_t _version;                                       // currently 1
314     void** _slot;                                          // can be used to store runtime data
315     immutable(object.ModuleInfo*)* _minfo_beg, _minfo_end; // array of modules in this object file
316 }
317 
toRange(T)318 T[] toRange(T)(T* beg, T* end) { return beg[0 .. end - beg]; }
319 
320 /* For each shared library and executable, the compiler generates code that
321  * sets up CompilerDSOData and calls _d_dso_registry().
322  * A pointer to that code is inserted into both the .ctors and .dtors
323  * segment so it gets called by the loader on startup and shutdown.
324  */
_d_dso_registry(CompilerDSOData * data)325 extern(C) void _d_dso_registry(CompilerDSOData* data)
326 {
327     // only one supported currently
328     safeAssert(data._version >= 1, "Incompatible compiler-generated DSO data version.");
329 
330     // no backlink => register
331     if (*data._slot is null)
332     {
333         immutable firstDSO = _loadedDSOs.empty;
334         if (firstDSO) initLocks();
335 
336         DSO* pdso = cast(DSO*).calloc(1, DSO.sizeof);
337         assert(typeid(DSO).initializer().ptr is null);
338         pdso._slot = data._slot;
339         *data._slot = pdso; // store backlink in library record
340 
341         pdso._moduleGroup = ModuleGroup(toRange(data._minfo_beg, data._minfo_end));
342 
343         HMODULE handle = void;
344         const moduleFound = findModuleHandleForAddr(data._slot, handle);
345         safeAssert(moduleFound, "Failed to find image header.");
346 
347         scanSegments(handle, pdso);
348 
349         version (Shared)
350         {
351             getDependencies(handle, pdso._deps);
352             pdso._handle = handle;
353             setDSOForHandle(pdso, pdso._handle);
354 
355             if (!_rtLoading)
356             {
357                 /* This DSO was not loaded by rt_loadLibrary which
358                  * happens for all dependencies of an executable or
359                  * the first dlopen call from a C program.
360                  * In this case we add the DSO to the _loadedDSOs of this
361                  * thread with a refCnt of 1 and call the TlsCtors.
362                  */
363                 immutable ushort refCnt = 1, addCnt = 0;
364                 _loadedDSOs.insertBack(ThreadDSO(pdso, refCnt, addCnt));
365             }
366         }
367         else
368         {
369             foreach (p; _loadedDSOs)
370                 safeAssert(p !is pdso, "DSO already registered.");
371             _loadedDSOs.insertBack(pdso);
372         }
373 
374         // don't initialize modules before rt_init was called
375         if (_isRuntimeInitialized)
376         {
377             registerGCRanges(pdso);
378             // rt_loadLibrary will run tls ctors, so do this only for dlopen
379             immutable runTlsCtors = !_rtLoading;
380             runModuleConstructors(pdso, runTlsCtors);
381         }
382     }
383     // has backlink => unregister
384     else
385     {
386         DSO* pdso = cast(DSO*)*data._slot;
387         *data._slot = null;
388 
389         // don't finalizes modules after rt_term was called (see Bugzilla 11378)
390         if (_isRuntimeInitialized)
391         {
392             // rt_unloadLibrary already ran tls dtors, so do this only for dlclose
393             immutable runTlsDtors = !_rtLoading;
394             runModuleDestructors(pdso, runTlsDtors);
395             unregisterGCRanges(pdso);
396             // run finalizers after module dtors (same order as in rt_term)
397             version (Shared) runFinalizers(pdso);
398         }
399 
400         version (Shared)
401         {
402             if (!_rtLoading)
403             {
404                 /* This DSO was not unloaded by rt_unloadLibrary so we
405                  * have to remove it from _loadedDSOs here.
406                  */
407                 foreach (i, ref tdso; _loadedDSOs)
408                 {
409                     if (tdso._pdso == pdso)
410                     {
411                         _loadedDSOs.remove(i);
412                         break;
413                     }
414                 }
415             }
416 
417             unsetDSOForHandle(pdso, pdso._handle);
418         }
419         else
420         {
421             // static DSOs are unloaded in reverse order
422             safeAssert(pdso == _loadedDSOs.back, "DSO being unregistered isn't current last one.");
423             _loadedDSOs.popBack();
424         }
425 
426         freeDSO(pdso);
427 
428         // last DSO being unloaded => shutdown registry
429         if (_loadedDSOs.empty)
430         {
431             version (GNU_EMUTLS)
432                 _d_emutls_destroy();
433             version (Shared)
434             {
435                 safeAssert(_handleToDSO.empty, "_handleToDSO not in sync with _loadedDSOs.");
436                 _handleToDSO.reset();
437             }
438             finiLocks();
439         }
440     }
441 }
442 
443 ///////////////////////////////////////////////////////////////////////////////
444 // dynamic loading
445 ///////////////////////////////////////////////////////////////////////////////
446 
447 // Shared D libraries are only supported when linking against a shared druntime library.
448 
version(Shared)449 version (Shared)
450 {
451     ThreadDSO* findThreadDSO(DSO* pdso) nothrow @nogc
452     {
453         foreach (ref tdata; _loadedDSOs)
454             if (tdata._pdso == pdso) return &tdata;
455         return null;
456     }
457 
458     void incThreadRef(DSO* pdso, bool incAdd)
459     {
460         if (auto tdata = findThreadDSO(pdso)) // already initialized
461         {
462             if (incAdd && ++tdata._addCnt > 1) return;
463             ++tdata._refCnt;
464         }
465         else
466         {
467             foreach (dep; pdso._deps)
468                 incThreadRef(dep, false);
469             immutable ushort refCnt = 1, addCnt = incAdd ? 1 : 0;
470             _loadedDSOs.insertBack(ThreadDSO(pdso, refCnt, addCnt));
471             pdso._moduleGroup.runTlsCtors();
472         }
473     }
474 
475     void decThreadRef(DSO* pdso, bool decAdd)
476     {
477         auto tdata = findThreadDSO(pdso);
478         safeAssert(tdata !is null, "Failed to find thread DSO.");
479         safeAssert(!decAdd || tdata._addCnt > 0, "Mismatching rt_unloadLibrary call.");
480 
481         if (decAdd && --tdata._addCnt > 0) return;
482         if (--tdata._refCnt > 0) return;
483 
484         pdso._moduleGroup.runTlsDtors();
485         foreach (i, ref td; _loadedDSOs)
486             if (td._pdso == pdso) _loadedDSOs.remove(i);
487         foreach (dep; pdso._deps)
488             decThreadRef(dep, false);
489     }
490 }
491 
492 /***********************************
493  * These are a temporary means of providing a GC hook for DLL use.  They may be
494  * replaced with some other similar functionality later.
495  */
496 extern (C)
497 {
498     void* gc_getProxy();
499     void  gc_setProxy(void* p);
500     void  gc_clrProxy();
501 
502     alias void  function(void*) gcSetFn;
503     alias void  function()      gcClrFn;
504 }
505 
506 /*******************************************
507  * Loads a DLL written in D with the name 'name'.
508  * Returns:
509  *      opaque handle to the DLL if successfully loaded
510  *      null if failure
511  */
rt_loadLibrary(const char * name)512 extern(C) void* rt_loadLibrary(const char* name)
513 {
514     version (Shared)
515     {
516         immutable save = _rtLoading;
517         _rtLoading = true;
518         scope (exit) _rtLoading = save;
519     }
520     return initLibrary(.LoadLibraryA(name));
521 }
522 
rt_loadLibraryW(const wchar_t * name)523 extern (C) void* rt_loadLibraryW(const wchar_t* name)
524 {
525     version (Shared)
526     {
527         immutable save = _rtLoading;
528         _rtLoading = true;
529         scope (exit) _rtLoading = save;
530     }
531     return initLibrary(.LoadLibraryW(name));
532 }
533 
initLibrary(void * handle)534 void* initLibrary(void* handle)
535 {
536     if (handle is null)
537         return null;
538 
539     version (Shared)
540     {
541         // if it's a D library
542         if (auto pdso = dsoForHandle(handle))
543             incThreadRef(pdso, true);
544     }
545     gcSetFn gcSet = cast(gcSetFn) GetProcAddress(handle, "gc_setProxy");
546     if (gcSet !is null)
547     {
548         // BUG: Set proxy, but too late
549         gcSet(gc_getProxy());
550     }
551     return handle;
552 }
553 
554 /*************************************
555  * Unloads DLL that was previously loaded by rt_loadLibrary().
556  * Input:
557  *      handle  the handle returned by rt_loadLibrary()
558  * Returns:
559  *      1   succeeded
560  *      0   some failure happened
561  */
rt_unloadLibrary(void * handle)562 extern(C) int rt_unloadLibrary(void* handle)
563 {
564     if (handle is null)
565         return false;
566 
567     version (Shared)
568     {
569         immutable save = _rtLoading;
570         _rtLoading = true;
571         scope (exit) _rtLoading = save;
572 
573         // if it's a D library
574         if (auto pdso = dsoForHandle(handle))
575             decThreadRef(pdso, true);
576     }
577     gcClrFn gcClr  = cast(gcClrFn) GetProcAddress(handle, "gc_clrProxy");
578     if (gcClr !is null)
579         gcClr();
580     return .FreeLibrary(handle) != 0;
581 }
582 
583 ///////////////////////////////////////////////////////////////////////////////
584 // helper functions
585 ///////////////////////////////////////////////////////////////////////////////
586 
initLocks()587 void initLocks() nothrow @nogc
588 {
589     version (Shared)
590         InitializeCriticalSection(&_handleToDSOMutex);
591 }
592 
finiLocks()593 void finiLocks() nothrow @nogc
594 {
595     version (Shared)
596         DeleteCriticalSection(&_handleToDSOMutex);
597 }
598 
runModuleConstructors(DSO * pdso,bool runTlsCtors)599 void runModuleConstructors(DSO* pdso, bool runTlsCtors)
600 {
601     pdso._moduleGroup.sortCtors();
602     pdso._moduleGroup.runCtors();
603     if (runTlsCtors) pdso._moduleGroup.runTlsCtors();
604 }
605 
runModuleDestructors(DSO * pdso,bool runTlsDtors)606 void runModuleDestructors(DSO* pdso, bool runTlsDtors)
607 {
608     if (runTlsDtors) pdso._moduleGroup.runTlsDtors();
609     pdso._moduleGroup.runDtors();
610 }
611 
registerGCRanges(DSO * pdso)612 void registerGCRanges(DSO* pdso) nothrow @nogc
613 {
614     foreach (rng; pdso._gcRanges)
615         GC.addRange(rng.ptr, rng.length);
616 }
617 
unregisterGCRanges(DSO * pdso)618 void unregisterGCRanges(DSO* pdso) nothrow @nogc
619 {
620     foreach (rng; pdso._gcRanges)
621         GC.removeRange(rng.ptr);
622 }
623 
version(Shared)624 version (Shared) void runFinalizers(DSO* pdso)
625 {
626     foreach (seg; pdso._codeSegments)
627         GC.runFinalizers(seg);
628 }
629 
freeDSO(DSO * pdso)630 void freeDSO(DSO* pdso) nothrow @nogc
631 {
632     pdso._gcRanges.reset();
633     version (Shared)
634     {
635         pdso._codeSegments.reset();
636         pdso._deps.reset();
637         pdso._handle = null;
638     }
639     .free(pdso);
640 }
641 
version(Shared)642 version (Shared)
643 {
644 @nogc nothrow:
645     const(char)* nameForDSO(DSO* pdso, ref char[] buffer)
646     {
647         const success = GetModuleFileNameA(pdso._handle, buffer.ptr, cast(DWORD)buffer.length) != 0;
648         safeAssert(success, "Failed to get DLL name.");
649         return buffer.ptr;
650     }
651 
652     DSO* dsoForHandle(in void* handle)
653     {
654         DSO* pdso;
655         .EnterCriticalSection(&_handleToDSOMutex);
656         if (auto ppdso = handle in _handleToDSO)
657             pdso = *ppdso;
658         .LeaveCriticalSection(&_handleToDSOMutex);
659         return pdso;
660     }
661 
662     void setDSOForHandle(DSO* pdso, void* handle)
663     {
664         .EnterCriticalSection(&_handleToDSOMutex);
665         safeAssert(handle !in _handleToDSO, "DSO already registered.");
666         _handleToDSO[handle] = pdso;
667         .LeaveCriticalSection(&_handleToDSOMutex);
668     }
669 
670     void unsetDSOForHandle(DSO* pdso, void* handle)
671     {
672         .EnterCriticalSection(&_handleToDSOMutex);
673         safeAssert(_handleToDSO[handle] == pdso, "Handle doesn't match registered DSO.");
674         _handleToDSO.remove(handle);
675         .LeaveCriticalSection(&_handleToDSOMutex);
676     }
677 
678     void getDependencies(in HMODULE handle, ref Array!(DSO*) deps)
679     {
680         auto nthdr = getNTHeader(handle);
681         auto import_entry = nthdr.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
682         auto addr = import_entry.VirtualAddress;
683         auto datasize = import_entry.Size;
684 
685         if (addr == 0 && datasize == 0)
686         {
687             // Maybe the optional header isn't there, look for the section.
688             foreach (section; getSectionHeader(handle))
689             {
690                 if (!compareSectionName(section, ".idata"))
691                     continue;
692                 addr = section.VirtualAddress;
693                 datasize = section.Misc.VirtualSize;
694                 break;
695             }
696             if (datasize == 0)
697                 return;
698         }
699         else
700         {
701             bool foundSection = false;
702             foreach (section; getSectionHeader(handle))
703             {
704                 if (!compareSectionName(section, ".idata"))
705                     continue;
706                 // Section containing import table has no contents.
707                 if (section.Misc.VirtualSize == 0)
708                     return;
709                 foundSection = true;
710                 break;
711             }
712             // There is an import table, but the section containing it could not be found
713             if (!foundSection)
714                 return;
715         }
716 
717         // Get the names of each DLL
718         for (uint i = 0; i + IMAGE_IMPORT_DESCRIPTOR.sizeof <= datasize;
719              i += IMAGE_IMPORT_DESCRIPTOR.sizeof)
720         {
721             const data = cast(PIMAGE_IMPORT_DESCRIPTOR)(handle + addr + i);
722             if (data.Name == 0)
723                 break;
724 
725             // dll name of dependency
726             auto name = cast(char*)(handle + data.Name);
727             // get handle without loading the library
728             auto libhandle = handleForName(name);
729             // the runtime linker has already loaded all dependencies
730             safeAssert(handle !is null, "Failed to get library handle.");
731             // if it's a D library
732             if (auto pdso = dsoForHandle(handle))
733                 deps.insertBack(pdso); // append it to the dependencies
734         }
735     }
736 
737     void* handleForName(const char* name)
738     {
739         return GetModuleHandleA(name);
740     }
741 }
742 
743 ///////////////////////////////////////////////////////////////////////////////
744 // PE/COFF program header iteration
745 ///////////////////////////////////////////////////////////////////////////////
746 
compareSectionName(ref IMAGE_SECTION_HEADER section,string name)747 bool compareSectionName(ref IMAGE_SECTION_HEADER section, string name) nothrow @nogc
748 {
749     if (name[] != cast(char[])section.Name[0 .. name.length])
750         return false;
751     return name.length == 8 || section.Name[name.length] == 0;
752 }
753 
754 /************
755  * Scan segments in the image header and store
756  * the writeable data segments in *pdso.
757  */
758 
scanSegments(in HMODULE handle,DSO * pdso)759 void scanSegments(in HMODULE handle, DSO* pdso) nothrow @nogc
760 {
761     foreach (section; getSectionHeader(handle))
762     {
763         // the ".data" image section includes both object file sections ".data" and ".bss"
764         if (compareSectionName(section, ".data"))
765         {
766             auto data = cast(void*)handle + section.VirtualAddress;
767             pdso._gcRanges.insertBack(data[0 .. section.Misc.VirtualSize]);
768             continue;
769         }
770 
771         version (Shared)
772         {
773             if (compareSectionName(section, ".text"))
774             {
775                 auto text = cast(void*)handle + section.VirtualAddress;
776                 pdso._codeSegments.insertBack(text[0 .. section.Misc.VirtualSize]);
777                 continue;
778             }
779         }
780     }
781 }
782 
783 /**************************
784  * Input:
785  *      handle  where the output is to be written
786  * Returns:
787  *      true if found, and *handle is filled in
788  */
789 
findModuleHandleForAddr(in void * addr,out HMODULE handle)790 bool findModuleHandleForAddr(in void* addr, out HMODULE handle) nothrow @nogc
791 {
792     if (GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
793                            cast(const(wchar)*) addr, &handle))
794         return true;
795 
796     return false;
797 }
798 
799 /**
800  * Returns the image NT header for the HMODULE handle passed,
801  * or null if not found.
802  */
getNTHeader(in HMODULE handle)803 PIMAGE_NT_HEADERS getNTHeader(in HMODULE handle) nothrow @nogc
804 {
805     auto doshdr = cast(PIMAGE_DOS_HEADER)handle;
806     if (doshdr.e_magic != IMAGE_DOS_SIGNATURE)
807         return null;
808 
809     return cast(typeof(return))(cast(void*)doshdr + doshdr.e_lfanew);
810 }
811 
812 /**
813  * Returns the image section header for the HMODULE handle passed,
814  * or null if not found.
815  */
getSectionHeader(in HMODULE handle)816 IMAGE_SECTION_HEADER[] getSectionHeader(in HMODULE handle) nothrow @nogc
817 {
818     if (auto nthdr = getNTHeader(handle))
819     {
820         const void* opthdr = &nthdr.OptionalHeader;
821         const offset = nthdr.FileHeader.SizeOfOptionalHeader;
822         const length = nthdr.FileHeader.NumberOfSections;
823         return (cast(PIMAGE_SECTION_HEADER)(opthdr + offset))[0 .. length];
824     }
825     return null;
826 }
827