xref: /netbsd-src/external/gpl3/gcc/dist/libphobos/libdruntime/gcc/sections/elf.d (revision b1e838363e3c6fc78a55519254d99869742dd33c)
1 // ELF-specific support for sections with shared libraries.
2 // Copyright (C) 2019-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.elf;
24 
25 version (MIPS32)  version = MIPS_Any;
26 version (MIPS64)  version = MIPS_Any;
27 version (RISCV32) version = RISCV_Any;
28 version (RISCV64) version = RISCV_Any;
29 version (S390)    version = IBMZ_Any;
30 version (SystemZ) version = IBMZ_Any;
31 
32 version (CRuntime_Glibc) enum SharedELF = true;
33 else version (CRuntime_Musl) enum SharedELF = true;
34 else version (FreeBSD) enum SharedELF = true;
35 else version (NetBSD) enum SharedELF = true;
36 else version (OpenBSD) enum SharedELF = true;
37 else version (DragonFlyBSD) enum SharedELF = true;
38 else version (CRuntime_UClibc) enum SharedELF = true;
39 else version (Solaris) enum SharedELF = true;
40 else enum SharedELF = false;
41 static if (SharedELF):
42 
43 import core.memory;
44 import core.stdc.config;
45 import core.stdc.stdio;
46 import core.stdc.stdlib : calloc, exit, free, malloc, EXIT_FAILURE;
47 import core.stdc.string : strlen;
version(linux)48 version (linux)
49 {
50     import core.sys.linux.dlfcn;
51     import core.sys.linux.elf;
52     import core.sys.linux.link;
53 }
version(FreeBSD)54 else version (FreeBSD)
55 {
56     import core.sys.freebsd.dlfcn;
57     import core.sys.freebsd.sys.elf;
58     import core.sys.freebsd.sys.link_elf;
59 }
version(NetBSD)60 else version (NetBSD)
61 {
62     import core.sys.netbsd.dlfcn;
63     import core.sys.netbsd.sys.elf;
64     import core.sys.netbsd.sys.link_elf;
65 }
version(OpenBSD)66 else version (OpenBSD)
67 {
68     import core.sys.openbsd.dlfcn;
69     import core.sys.openbsd.sys.elf;
70     import core.sys.openbsd.sys.link_elf;
71 }
version(DragonFlyBSD)72 else version (DragonFlyBSD)
73 {
74     import core.sys.dragonflybsd.dlfcn;
75     import core.sys.dragonflybsd.sys.elf;
76     import core.sys.dragonflybsd.sys.link_elf;
77 }
version(Solaris)78 else version (Solaris)
79 {
80     import core.sys.solaris.dlfcn;
81     import core.sys.solaris.link;
82     import core.sys.solaris.sys.elf;
83     import core.sys.solaris.sys.link;
84 }
85 else
86 {
87     static assert(0, "unimplemented");
88 }
89 import core.sys.posix.pthread;
90 import rt.deh;
91 import rt.dmain2;
92 import rt.minfo;
93 import core.internal.container.array;
94 import core.internal.container.hashtab;
95 import gcc.builtins;
96 import gcc.config;
97 import gcc.sections.common;
98 
99 alias DSO SectionGroup;
100 struct DSO
101 {
opApplyDSO102     static int opApply(scope int delegate(ref DSO) dg)
103     {
104         foreach (dso; _loadedDSOs)
105         {
106             if (auto res = dg(*dso))
107                 return res;
108         }
109         return 0;
110     }
111 
opApplyReverseDSO112     static int opApplyReverse(scope int delegate(ref DSO) dg)
113     {
114         foreach_reverse (dso; _loadedDSOs)
115         {
116             if (auto res = dg(*dso))
117                 return res;
118         }
119         return 0;
120     }
121 
immutableDSO122     @property immutable(ModuleInfo*)[] modules() const nothrow @nogc
123     {
124         return _moduleGroup.modules;
125     }
126 
127     @property ref inout(ModuleGroup) moduleGroup() inout return nothrow @nogc
128     {
129         return _moduleGroup;
130     }
131 
132     @property inout(void[])[] gcRanges() inout nothrow @nogc
133     {
134         return _gcRanges[];
135     }
136 
137 private:
138 
139     invariant()
140     {
141         safeAssert(_moduleGroup.modules.length > 0, "No modules for DSO.");
142         safeAssert(_tlsMod || !_tlsSize, "Inconsistent TLS fields for DSO.");
143     }
144 
145     ModuleGroup _moduleGroup;
146     Array!(void[]) _gcRanges;
147     size_t _tlsMod;
148     size_t _tlsSize;
149 
versionDSO150     version (Shared)
151     {
152         Array!(void[]) _codeSegments; // array of code segments
153         Array!(DSO*) _deps; // D libraries needed by this DSO
154         void* _handle; // corresponding handle
155     }
156 
157     // get the TLS range for the executing thread
tlsRangeDSO158     void[] tlsRange() const nothrow @nogc
159     {
160         return getTLSRange(_tlsMod, _tlsSize);
161     }
162 }
163 
164 /****
165  * Boolean flag set to true while the runtime is initialized.
166  */
167 __gshared bool _isRuntimeInitialized;
168 
169 
170 /****
171  * Gets called on program startup just before GC is initialized.
172  */
initSections()173 void initSections() nothrow @nogc
174 {
175     _isRuntimeInitialized = true;
176 }
177 
178 
179 /***
180  * Gets called on program shutdown just after GC is terminated.
181  */
finiSections()182 void finiSections() nothrow @nogc
183 {
184     _isRuntimeInitialized = false;
185 }
186 
187 alias ScanDG = void delegate(void* pbeg, void* pend) nothrow;
188 
version(Shared)189 version (Shared)
190 {
191     import gcc.sections : pinLoadedLibraries, unpinLoadedLibraries,
192            inheritLoadedLibraries, cleanupLoadedLibraries;
193 
194     /***
195      * Called once per thread; returns array of thread local storage ranges
196      */
197     Array!(ThreadDSO)* initTLSRanges() @nogc nothrow
198     {
199         return &_loadedDSOs();
200     }
201 
202     void finiTLSRanges(Array!(ThreadDSO)* tdsos) @nogc nothrow
203     {
204         // Nothing to do here. tdsos used to point to the _loadedDSOs instance
205         // in the dying thread's TLS segment and as such is not valid anymore.
206         // The memory for the array contents was already reclaimed in
207         // cleanupLoadedLibraries().
208     }
209 
210     void scanTLSRanges(Array!(ThreadDSO)* tdsos, scope ScanDG dg) nothrow
211     {
212         version (GNU_EMUTLS)
213         {
214             import gcc.emutls;
215             _d_emutls_scan(dg);
216         }
217         else
218         {
219             foreach (ref tdso; *tdsos)
220                 dg(tdso._tlsRange.ptr, tdso._tlsRange.ptr + tdso._tlsRange.length);
221         }
222     }
223 
224     size_t sizeOfTLS() nothrow @nogc
225     {
226         auto tdsos = initTLSRanges();
227         size_t sum;
228         foreach (ref tdso; *tdsos)
229             sum += tdso._tlsRange.length;
230         return sum;
231     }
232 
233     // interface for core.thread to inherit loaded libraries
234     pragma(mangle, gcc.sections.pinLoadedLibraries.mangleof)
235     void* pinLoadedLibraries() nothrow @nogc
236     {
237         auto res = cast(Array!(ThreadDSO)*)calloc(1, Array!(ThreadDSO).sizeof);
238         res.length = _loadedDSOs.length;
239         foreach (i, ref tdso; _loadedDSOs)
240         {
241             (*res)[i] = tdso;
242             if (tdso._addCnt)
243             {
244                 // Increment the dlopen ref for explicitly loaded libraries to pin them.
245                 const success = .dlopen(linkMapForHandle(tdso._pdso._handle).l_name, RTLD_LAZY) !is null;
246                 safeAssert(success, "Failed to increment dlopen ref.");
247                 (*res)[i]._addCnt = 1; // new array takes over the additional ref count
248             }
249         }
250         return res;
251     }
252 
253     pragma(mangle, gcc.sections.unpinLoadedLibraries.mangleof)
254     void unpinLoadedLibraries(void* p) nothrow @nogc
255     {
256         auto pary = cast(Array!(ThreadDSO)*)p;
257         // In case something failed we need to undo the pinning.
258         foreach (ref tdso; *pary)
259         {
260             if (tdso._addCnt)
261             {
262                 auto handle = tdso._pdso._handle;
263                 safeAssert(handle !is null, "Invalid library handle.");
264                 .dlclose(handle);
265             }
266         }
267         pary.reset();
268         .free(pary);
269     }
270 
271     // Called before TLS ctors are ran, copy over the loaded libraries
272     // of the parent thread.
273     pragma(mangle, gcc.sections.inheritLoadedLibraries.mangleof)
274     void inheritLoadedLibraries(void* p) nothrow @nogc
275     {
276         safeAssert(_loadedDSOs.empty, "DSOs have already been registered for this thread.");
277         _loadedDSOs.swap(*cast(Array!(ThreadDSO)*)p);
278         .free(p);
279         foreach (ref dso; _loadedDSOs)
280         {
281             // the copied _tlsRange corresponds to parent thread
282             dso.updateTLSRange();
283         }
284     }
285 
286     // Called after all TLS dtors ran, decrements all remaining dlopen refs.
287     pragma(mangle, gcc.sections.cleanupLoadedLibraries.mangleof)
288     void cleanupLoadedLibraries() nothrow @nogc
289     {
290         foreach (ref tdso; _loadedDSOs)
291         {
292             if (tdso._addCnt == 0) continue;
293 
294             auto handle = tdso._pdso._handle;
295             safeAssert(handle !is null, "Invalid DSO handle.");
296             for (; tdso._addCnt > 0; --tdso._addCnt)
297                 .dlclose(handle);
298         }
299 
300         // Free the memory for the array contents.
301         _loadedDSOs.reset();
302     }
303 }
304 else
305 {
306     /***
307      * Called once per thread; returns array of thread local storage ranges
308      */
309     Array!(void[])* initTLSRanges() nothrow @nogc
310     {
311         auto rngs = &_tlsRanges();
312         if (rngs.empty)
313         {
314             foreach (ref pdso; _loadedDSOs)
315                 rngs.insertBack(pdso.tlsRange());
316         }
317         return rngs;
318     }
319 
320     void finiTLSRanges(Array!(void[])* rngs) nothrow @nogc
321     {
322         rngs.reset();
323     }
324 
325     void scanTLSRanges(Array!(void[])* rngs, scope ScanDG dg) nothrow
326     {
version(GNU_EMUTLS)327         version (GNU_EMUTLS)
328         {
329             import gcc.emutls;
330             _d_emutls_scan(dg);
331         }
332         else
333         {
334             foreach (rng; *rngs)
335                 dg(rng.ptr, rng.ptr + rng.length);
336         }
337     }
338 
sizeOfTLS()339     size_t sizeOfTLS() nothrow @nogc
340     {
341         auto rngs = initTLSRanges();
342         size_t sum;
343         foreach (rng; *rngs)
344             sum += rng.length;
345         return sum;
346     }
347 }
348 
349 private:
350 
version(Shared)351 version (Shared)
352 {
353     /*
354      * Array of thread local DSO metadata for all libraries loaded and
355      * initialized in this thread.
356      *
357      * Note:
358      *     A newly spawned thread will inherit these libraries.
359      * Note:
360      *     We use an array here to preserve the order of
361      *     initialization.  If that became a performance issue, we
362      *     could use a hash table and enumerate the DSOs during
363      *     loading so that the hash table values could be sorted when
364      *     necessary.
365      */
366     struct ThreadDSO
367     {
368         DSO* _pdso;
369         static if (_pdso.sizeof == 8) uint _refCnt, _addCnt;
370         else static if (_pdso.sizeof == 4) ushort _refCnt, _addCnt;
371         else static assert(0, "unimplemented");
372         void[] _tlsRange;
373         alias _pdso this;
374         // update the _tlsRange for the executing thread
375         void updateTLSRange() nothrow @nogc
376         {
377             _tlsRange = _pdso.tlsRange();
378         }
379     }
380     @property ref Array!(ThreadDSO) _loadedDSOs() @nogc nothrow { static Array!(ThreadDSO) x; return x; }
381 
382     /*
383      * Set to true during rt_loadLibrary/rt_unloadLibrary calls.
384      */
385     bool _rtLoading;
386 
387     /*
388      * Hash table to map link_map* to corresponding DSO*.
389      * The hash table is protected by a Mutex.
390      */
391     __gshared pthread_mutex_t _handleToDSOMutex;
392     @property ref HashTab!(void*, DSO*) _handleToDSO() @nogc nothrow { __gshared HashTab!(void*, DSO*) x; return x; }
393 }
394 else
395 {
396     /*
397      * Static DSOs loaded by the runtime linker. This includes the
398      * executable. These can't be unloaded.
399      */
400     @property ref Array!(DSO*) _loadedDSOs() @nogc nothrow { __gshared Array!(DSO*) x; return x; }
401 
402     /*
403      * Thread local array that contains TLS memory ranges for each
404      * library initialized in this thread.
405      */
406     @property ref Array!(void[]) _tlsRanges() @nogc nothrow { static Array!(void[]) x; return x; }
407 
408     enum _rtLoading = false;
409 }
410 
411 ///////////////////////////////////////////////////////////////////////////////
412 // Compiler to runtime interface.
413 ///////////////////////////////////////////////////////////////////////////////
414 
415 /*
416  * This data structure is generated by the compiler, and then passed to
417  * _d_dso_registry().
418  */
419 struct CompilerDSOData
420 {
421     size_t _version;                                       // currently 1
422     void** _slot;                                          // can be used to store runtime data
423     immutable(object.ModuleInfo*)* _minfo_beg, _minfo_end; // array of modules in this object file
424 }
425 
toRange(T)426 T[] toRange(T)(T* beg, T* end) { return beg[0 .. end - beg]; }
427 
428 /* For each shared library and executable, the compiler generates code that
429  * sets up CompilerDSOData and calls _d_dso_registry().
430  * A pointer to that code is inserted into both the .ctors and .dtors
431  * segment so it gets called by the loader on startup and shutdown.
432  */
_d_dso_registry(CompilerDSOData * data)433 extern(C) void _d_dso_registry(CompilerDSOData* data)
434 {
435     // only one supported currently
436     safeAssert(data._version >= 1, "Incompatible compiler-generated DSO data version.");
437 
438     // no backlink => register
439     if (*data._slot is null)
440     {
441         immutable firstDSO = _loadedDSOs.empty;
442         if (firstDSO) initLocks();
443 
444         DSO* pdso = cast(DSO*).calloc(1, DSO.sizeof);
445         assert(typeid(DSO).initializer().ptr is null);
446         *data._slot = pdso; // store backlink in library record
447 
448         pdso._moduleGroup = ModuleGroup(toRange(data._minfo_beg, data._minfo_end));
449 
450         dl_phdr_info info = void;
451         const headerFound = findDSOInfoForAddr(data._slot, &info);
452         safeAssert(headerFound, "Failed to find image header.");
453 
454         scanSegments(info, pdso);
455 
456         version (Shared)
457         {
458             auto handle = handleForAddr(data._slot);
459 
460             getDependencies(info, pdso._deps);
461             pdso._handle = handle;
462             setDSOForHandle(pdso, pdso._handle);
463 
464             if (!_rtLoading)
465             {
466                 /* This DSO was not loaded by rt_loadLibrary which
467                  * happens for all dependencies of an executable or
468                  * the first dlopen call from a C program.
469                  * In this case we add the DSO to the _loadedDSOs of this
470                  * thread with a refCnt of 1 and call the TlsCtors.
471                  */
472                 immutable ushort refCnt = 1, addCnt = 0;
473                 _loadedDSOs.insertBack(ThreadDSO(pdso, refCnt, addCnt, pdso.tlsRange()));
474             }
475         }
476         else
477         {
478             foreach (p; _loadedDSOs)
479                 safeAssert(p !is pdso, "DSO already registered.");
480             _loadedDSOs.insertBack(pdso);
481             _tlsRanges.insertBack(pdso.tlsRange());
482         }
483 
484         // don't initialize modules before rt_init was called (see Bugzilla 11378)
485         if (_isRuntimeInitialized)
486         {
487             registerGCRanges(pdso);
488             // rt_loadLibrary will run tls ctors, so do this only for dlopen
489             immutable runTlsCtors = !_rtLoading;
490             runModuleConstructors(pdso, runTlsCtors);
491         }
492     }
493     // has backlink => unregister
494     else
495     {
496         DSO* pdso = cast(DSO*)*data._slot;
497         *data._slot = null;
498 
499         // don't finalizes modules after rt_term was called (see Bugzilla 11378)
500         if (_isRuntimeInitialized)
501         {
502             // rt_unloadLibrary already ran tls dtors, so do this only for dlclose
503             immutable runTlsDtors = !_rtLoading;
504             runModuleDestructors(pdso, runTlsDtors);
505             unregisterGCRanges(pdso);
506             // run finalizers after module dtors (same order as in rt_term)
507             version (Shared) runFinalizers(pdso);
508         }
509 
510         version (Shared)
511         {
512             if (!_rtLoading)
513             {
514                 /* This DSO was not unloaded by rt_unloadLibrary so we
515                  * have to remove it from _loadedDSOs here.
516                  */
517                 foreach (i, ref tdso; _loadedDSOs)
518                 {
519                     if (tdso._pdso == pdso)
520                     {
521                         _loadedDSOs.remove(i);
522                         break;
523                     }
524                 }
525             }
526 
527             unsetDSOForHandle(pdso, pdso._handle);
528         }
529         else
530         {
531             // static DSOs are unloaded in reverse order
532             safeAssert(pdso == _loadedDSOs.back, "DSO being unregistered isn't current last one.");
533             _loadedDSOs.popBack();
534         }
535 
536         freeDSO(pdso);
537 
538         // last DSO being unloaded => shutdown registry
539         if (_loadedDSOs.empty)
540         {
541             version (Shared)
542             {
543                 safeAssert(_handleToDSO.empty, "_handleToDSO not in sync with _loadedDSOs.");
544                 _handleToDSO.reset();
545             }
546             finiLocks();
547             version (GNU_EMUTLS)
548             {
549                 import gcc.emutls;
550                 _d_emutls_destroy();
551             }
552         }
553     }
554 }
555 
556 ///////////////////////////////////////////////////////////////////////////////
557 // Dynamic loading
558 ///////////////////////////////////////////////////////////////////////////////
559 
560 // Shared D libraries are only supported when linking against a shared druntime library.
561 
version(Shared)562 version (Shared)
563 {
564     ThreadDSO* findThreadDSO(DSO* pdso) nothrow @nogc
565     {
566         foreach (ref tdata; _loadedDSOs)
567             if (tdata._pdso == pdso) return &tdata;
568         return null;
569     }
570 
571     void incThreadRef(DSO* pdso, bool incAdd)
572     {
573         if (auto tdata = findThreadDSO(pdso)) // already initialized
574         {
575             if (incAdd && ++tdata._addCnt > 1) return;
576             ++tdata._refCnt;
577         }
578         else
579         {
580             foreach (dep; pdso._deps)
581                 incThreadRef(dep, false);
582             immutable ushort refCnt = 1, addCnt = incAdd ? 1 : 0;
583             _loadedDSOs.insertBack(ThreadDSO(pdso, refCnt, addCnt, pdso.tlsRange()));
584             pdso._moduleGroup.runTlsCtors();
585         }
586     }
587 
588     void decThreadRef(DSO* pdso, bool decAdd)
589     {
590         auto tdata = findThreadDSO(pdso);
591         safeAssert(tdata !is null, "Failed to find thread DSO.");
592         safeAssert(!decAdd || tdata._addCnt > 0, "Mismatching rt_unloadLibrary call.");
593 
594         if (decAdd && --tdata._addCnt > 0) return;
595         if (--tdata._refCnt > 0) return;
596 
597         pdso._moduleGroup.runTlsDtors();
598         foreach (i, ref td; _loadedDSOs)
599             if (td._pdso == pdso) _loadedDSOs.remove(i);
600         foreach (dep; pdso._deps)
601             decThreadRef(dep, false);
602     }
603 
604     extern(C) void* rt_loadLibrary(const char* name)
605     {
606         immutable save = _rtLoading;
607         _rtLoading = true;
608         scope (exit) _rtLoading = save;
609 
610         auto handle = .dlopen(name, RTLD_LAZY);
611         if (handle is null) return null;
612 
613         // if it's a D library
614         if (auto pdso = dsoForHandle(handle))
615             incThreadRef(pdso, true);
616         return handle;
617     }
618 
619     extern(C) int rt_unloadLibrary(void* handle)
620     {
621         if (handle is null) return false;
622 
623         immutable save = _rtLoading;
624         _rtLoading = true;
625         scope (exit) _rtLoading = save;
626 
627         // if it's a D library
628         if (auto pdso = dsoForHandle(handle))
629             decThreadRef(pdso, true);
630         return .dlclose(handle) == 0;
631     }
632 }
633 
634 ///////////////////////////////////////////////////////////////////////////////
635 // Helper functions
636 ///////////////////////////////////////////////////////////////////////////////
637 
initLocks()638 void initLocks() nothrow @nogc
639 {
640     version (Shared)
641         !pthread_mutex_init(&_handleToDSOMutex, null) || assert(0);
642 }
643 
finiLocks()644 void finiLocks() nothrow @nogc
645 {
646     version (Shared)
647         !pthread_mutex_destroy(&_handleToDSOMutex) || assert(0);
648 }
649 
runModuleConstructors(DSO * pdso,bool runTlsCtors)650 void runModuleConstructors(DSO* pdso, bool runTlsCtors)
651 {
652     pdso._moduleGroup.sortCtors();
653     pdso._moduleGroup.runCtors();
654     if (runTlsCtors) pdso._moduleGroup.runTlsCtors();
655 }
656 
runModuleDestructors(DSO * pdso,bool runTlsDtors)657 void runModuleDestructors(DSO* pdso, bool runTlsDtors)
658 {
659     if (runTlsDtors) pdso._moduleGroup.runTlsDtors();
660     pdso._moduleGroup.runDtors();
661 }
662 
registerGCRanges(DSO * pdso)663 void registerGCRanges(DSO* pdso) nothrow @nogc
664 {
665     foreach (rng; pdso._gcRanges)
666         GC.addRange(rng.ptr, rng.length);
667 }
668 
unregisterGCRanges(DSO * pdso)669 void unregisterGCRanges(DSO* pdso) nothrow @nogc
670 {
671     foreach (rng; pdso._gcRanges)
672         GC.removeRange(rng.ptr);
673 }
674 
version(Shared)675 version (Shared) void runFinalizers(DSO* pdso)
676 {
677     foreach (seg; pdso._codeSegments)
678         GC.runFinalizers(seg);
679 }
680 
freeDSO(DSO * pdso)681 void freeDSO(DSO* pdso) nothrow @nogc
682 {
683     pdso._gcRanges.reset();
684     version (Shared)
685     {
686         pdso._codeSegments.reset();
687         pdso._deps.reset();
688         pdso._handle = null;
689     }
690     .free(pdso);
691 }
692 
version(Shared)693 version (Shared)
694 {
695 @nogc nothrow:
696     link_map* linkMapForHandle(void* handle)
697     {
698         static if (__traits(compiles, RTLD_DI_LINKMAP))
699         {
700             link_map* map;
701             const success = dlinfo(handle, RTLD_DI_LINKMAP, &map) == 0;
702             safeAssert(success, "Failed to get DSO info.");
703             return map;
704         }
705         else version (OpenBSD)
706         {
707             safeAssert(handle !is null, "Failed to get DSO info.");
708             return cast(link_map*)handle;
709         }
710         else
711             static assert(0, "unimplemented");
712     }
713 
714     DSO* dsoForHandle(void* handle)
715     {
716         DSO* pdso;
717         !pthread_mutex_lock(&_handleToDSOMutex) || assert(0);
718         if (auto ppdso = handle in _handleToDSO)
719             pdso = *ppdso;
720         !pthread_mutex_unlock(&_handleToDSOMutex) || assert(0);
721         return pdso;
722     }
723 
724     void setDSOForHandle(DSO* pdso, void* handle)
725     {
726         !pthread_mutex_lock(&_handleToDSOMutex) || assert(0);
727         safeAssert(handle !in _handleToDSO, "DSO already registered.");
728         _handleToDSO[handle] = pdso;
729         !pthread_mutex_unlock(&_handleToDSOMutex) || assert(0);
730     }
731 
732     void unsetDSOForHandle(DSO* pdso, void* handle)
733     {
734         !pthread_mutex_lock(&_handleToDSOMutex) || assert(0);
735         safeAssert(_handleToDSO[handle] == pdso, "Handle doesn't match registered DSO.");
736         _handleToDSO.remove(handle);
737         !pthread_mutex_unlock(&_handleToDSOMutex) || assert(0);
738     }
739 
740     void getDependencies(in ref dl_phdr_info info, ref Array!(DSO*) deps)
741     {
742         // get the entries of the .dynamic section
743         ElfW!"Dyn"[] dyns;
744         foreach (ref phdr; info.dlpi_phdr[0 .. info.dlpi_phnum])
745         {
746             if (phdr.p_type == PT_DYNAMIC)
747             {
748                 auto p = cast(ElfW!"Dyn"*)(info.dlpi_addr + (phdr.p_vaddr & ~(size_t.sizeof - 1)));
749                 dyns = p[0 .. phdr.p_memsz / ElfW!"Dyn".sizeof];
750                 break;
751             }
752         }
753         // find the string table which contains the sonames
754         const(char)* strtab;
755         foreach (dyn; dyns)
756         {
757             if (dyn.d_tag == DT_STRTAB)
758             {
759                 version (CRuntime_Musl)
760                     strtab = cast(const(char)*)(info.dlpi_addr + dyn.d_un.d_ptr); // relocate
761                 else version (linux)
762                 {
763                     // This might change in future glibc releases (after 2.29) as dynamic sections
764                     // are not required to be read-only on RISC-V. This was copy & pasted from MIPS
765                     // while upstreaming RISC-V support. Otherwise MIPS is the only arch which sets
766                     // in glibc: #define DL_RO_DYN_SECTION 1
767                     version (RISCV_Any)
768                         strtab = cast(const(char)*)(info.dlpi_addr + dyn.d_un.d_ptr); // relocate
769                     else version (MIPS_Any)
770                         strtab = cast(const(char)*)(info.dlpi_addr + dyn.d_un.d_ptr); // relocate
771                     else
772                         strtab = cast(const(char)*)dyn.d_un.d_ptr;
773                 }
774                 else version (FreeBSD)
775                     strtab = cast(const(char)*)(info.dlpi_addr + dyn.d_un.d_ptr); // relocate
776                 else version (NetBSD)
777                     strtab = cast(const(char)*)(info.dlpi_addr + dyn.d_un.d_ptr); // relocate
778                 else version (OpenBSD)
779                     strtab = cast(const(char)*)(info.dlpi_addr + dyn.d_un.d_ptr); // relocate
780                 else version (DragonFlyBSD)
781                     strtab = cast(const(char)*)(info.dlpi_addr + dyn.d_un.d_ptr); // relocate
782                 else version (Solaris)
783                     strtab = cast(const(char)*)(info.dlpi_addr + dyn.d_un.d_ptr); // relocate
784                 else
785                     static assert(0, "unimplemented");
786                 break;
787             }
788         }
789         foreach (dyn; dyns)
790         {
791             immutable tag = dyn.d_tag;
792             if (!(tag == DT_NEEDED || tag == DT_AUXILIARY || tag == DT_FILTER))
793                 continue;
794 
795             // soname of the dependency
796             auto name = strtab + dyn.d_un.d_val;
797             // get handle without loading the library
798             auto handle = handleForName(name);
799             // the runtime linker has already loaded all dependencies
800             safeAssert(handle !is null, "Failed to get library handle.");
801             // if it's a D library
802             if (auto pdso = dsoForHandle(handle))
803                 deps.insertBack(pdso); // append it to the dependencies
804         }
805     }
806 
807     void* handleForName(const char* name)
808     {
809         version (Solaris) enum refCounted = false;
810         else version (OpenBSD) enum refCounted = false;
811         else enum refCounted = true;
812 
813         static if (__traits(compiles, RTLD_NOLOAD))
814             enum flags = (RTLD_NOLOAD | RTLD_LAZY);
815         else
816             enum flags = RTLD_LAZY;
817 
818         auto handle = .dlopen(name, flags);
819         static if (refCounted)
820         {
821             if (handle !is null)
822                 .dlclose(handle); // drop reference count
823         }
824         return handle;
825     }
826 }
827 
828 ///////////////////////////////////////////////////////////////////////////////
829 // Elf program header iteration
830 ///////////////////////////////////////////////////////////////////////////////
831 
832 /************
833  * Scan segments in Linux dl_phdr_info struct and store
834  * the TLS and writeable data segments in *pdso.
835  */
scanSegments(in ref dl_phdr_info info,DSO * pdso)836 void scanSegments(in ref dl_phdr_info info, DSO* pdso) nothrow @nogc
837 {
838     foreach (ref phdr; info.dlpi_phdr[0 .. info.dlpi_phnum])
839     {
840         switch (phdr.p_type)
841         {
842         case PT_LOAD:
843             if (phdr.p_flags & PF_W) // writeable data segment
844             {
845                 auto beg = cast(void*)(info.dlpi_addr + (phdr.p_vaddr & ~(size_t.sizeof - 1)));
846                 pdso._gcRanges.insertBack(beg[0 .. phdr.p_memsz]);
847             }
848             version (Shared) if (phdr.p_flags & PF_X) // code segment
849             {
850                 auto beg = cast(void*)(info.dlpi_addr + (phdr.p_vaddr & ~(size_t.sizeof - 1)));
851                 pdso._codeSegments.insertBack(beg[0 .. phdr.p_memsz]);
852             }
853             break;
854 
855         case PT_TLS: // TLS segment
856             version (GNU_EMUTLS)
857             {
858             }
859             else
860             {
861                 safeAssert(!pdso._tlsSize, "Multiple TLS segments in image header.");
862                 static if (OS_Have_Dlpi_Tls_Modid)
863                 {
864                     pdso._tlsMod = info.dlpi_tls_modid;
865                     pdso._tlsSize = phdr.p_memsz;
866                 }
867                 else version (Solaris)
868                 {
869                     struct Rt_map
870                     {
871                         Link_map rt_public;
872                         const char* rt_pathname;
873                         c_ulong rt_padstart;
874                         c_ulong rt_padimlen;
875                         c_ulong rt_msize;
876                         uint rt_flags;
877                         uint rt_flags1;
878                         c_ulong rt_tlsmodid;
879                     }
880 
881                     Rt_map* map;
882                     version (Shared)
883                         dlinfo(handleForName(info.dlpi_name), RTLD_DI_LINKMAP, &map);
884                     else
885                         dlinfo(RTLD_SELF, RTLD_DI_LINKMAP, &map);
886                     // Until Solaris 11.4, tlsmodid for the executable is 0.
887                     // Let it start at 1 as the rest of the code expects.
888                     pdso._tlsMod = map.rt_tlsmodid + 1;
889                     pdso._tlsSize = phdr.p_memsz;
890                 }
891                 else
892                 {
893                     pdso._tlsMod = 0;
894                     pdso._tlsSize = 0;
895                 }
896             }
897             break;
898 
899         default:
900             break;
901         }
902     }
903 }
904 
905 /**************************
906  * Input:
907  *      result  where the output is to be written; dl_phdr_info is an OS struct
908  * Returns:
909  *      true if found, and *result is filled in
910  * References:
911  *      http://linux.die.net/man/3/dl_iterate_phdr
912  */
913 bool findDSOInfoForAddr(in void* addr, dl_phdr_info* result=null) nothrow @nogc
914 {
915     version (linux)        enum IterateManually = true;
916     else version (NetBSD)  enum IterateManually = true;
917     else version (OpenBSD) enum IterateManually = true;
918     else version (Solaris) enum IterateManually = true;
919     else                   enum IterateManually = false;
920 
921     static if (IterateManually)
922     {
923         static struct DG { const(void)* addr; dl_phdr_info* result; }
924 
callback(dl_phdr_info * info,size_t sz,void * arg)925         extern(C) int callback(dl_phdr_info* info, size_t sz, void* arg) nothrow @nogc
926         {
927             auto p = cast(DG*)arg;
928             if (findSegmentForAddr(*info, p.addr))
929             {
930                 if (p.result !is null) *p.result = *info;
931                 return 1; // break;
932             }
933             return 0; // continue iteration
934         }
935 
936         auto dg = DG(addr, result);
937 
938         /* OS function that walks through the list of an application's shared objects and
939          * calls 'callback' once for each object, until either all shared objects
940          * have been processed or 'callback' returns a nonzero value.
941          */
942         return dl_iterate_phdr(&callback, &dg) != 0;
943     }
version(FreeBSD)944     else version (FreeBSD)
945     {
946         return !!_rtld_addr_phdr(addr, result);
947     }
version(DragonFlyBSD)948     else version (DragonFlyBSD)
949     {
950         return !!_rtld_addr_phdr(addr, result);
951     }
952     else
953         static assert(0, "unimplemented");
954 }
955 
956 /*********************************
957  * Determine if 'addr' lies within shared object 'info'.
958  * If so, return true and fill in 'result' with the corresponding ELF program header.
959  */
960 bool findSegmentForAddr(in ref dl_phdr_info info, in void* addr, ElfW!"Phdr"* result=null) nothrow @nogc
961 {
962     if (addr < cast(void*)info.dlpi_addr) // less than base address of object means quick reject
963         return false;
964 
965     foreach (ref phdr; info.dlpi_phdr[0 .. info.dlpi_phnum])
966     {
967         auto beg = cast(void*)(info.dlpi_addr + phdr.p_vaddr);
968         if (cast(size_t)(addr - beg) < phdr.p_memsz)
969         {
970             if (result !is null) *result = phdr;
971             return true;
972         }
973     }
974     return false;
975 }
976 
977 /**************************
978  * Input:
979  *      addr  an internal address of a DSO
980  * Returns:
981  *      the dlopen handle for that DSO or null if addr is not within a loaded DSO
982  */
version(Shared)983 version (Shared) void* handleForAddr(void* addr) nothrow @nogc
984 {
985     Dl_info info = void;
986     if (dladdr(addr, &info) != 0)
987         return handleForName(info.dli_fname);
988     return null;
989 }
990 
991 ///////////////////////////////////////////////////////////////////////////////
992 // TLS module helper
993 ///////////////////////////////////////////////////////////////////////////////
994 
995 
996 /*
997  * Returns: the TLS memory range for a given module and the calling
998  * thread or null if that module has no TLS.
999  *
1000  * Note: This will cause the TLS memory to be eagerly allocated.
1001  */
1002 struct tls_index
1003 {
versiontls_index1004     version (CRuntime_Glibc)
1005     {
1006         // For x86_64, fields are of type uint64_t, this is important for x32
1007         // where tls_index would otherwise have the wrong size.
1008         // See https://sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/x86_64/dl-tls.h
1009         version (X86_64)
1010         {
1011             ulong ti_module;
1012             ulong ti_offset;
1013         }
1014         else
1015         {
1016             c_ulong ti_module;
1017             c_ulong ti_offset;
1018         }
1019     }
1020     else
1021     {
1022         size_t ti_module;
1023         size_t ti_offset;
1024     }
1025 }
1026 
1027 extern(C) void* __tls_get_addr(tls_index* ti) nothrow @nogc;
1028 extern(C) void* __ibmz_get_tls_offset(tls_index *ti) nothrow @nogc;
1029 
1030 /* The dynamic thread vector (DTV) pointers may point 0x8000 past the start of
1031  * each TLS block. This is at least true for PowerPC and Mips platforms.
1032  * See: https://sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/powerpc/dl-tls.h;h=f7cf6f96ebfb505abfd2f02be0ad0e833107c0cd;hb=HEAD#l34
1033  *      https://sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/mips/dl-tls.h;h=93a6dc050cb144b9f68b96fb3199c60f5b1fcd18;hb=HEAD#l32
1034  *      https://sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/riscv/dl-tls.h;h=ab2d860314de94c18812bc894ff6b3f55368f20f;hb=HEAD#l32
1035  */
1036 version (X86)
1037     enum TLS_DTV_OFFSET = 0x0;
1038 else version (X86_64)
1039     enum TLS_DTV_OFFSET = 0x0;
1040 else version (ARM)
1041     enum TLS_DTV_OFFSET = 0x0;
1042 else version (AArch64)
1043     enum TLS_DTV_OFFSET = 0x0;
1044 else version (RISCV32)
1045     enum TLS_DTV_OFFSET = 0x800;
1046 else version (RISCV64)
1047     enum TLS_DTV_OFFSET = 0x800;
1048 else version (HPPA)
1049     enum TLS_DTV_OFFSET = 0x0;
1050 else version (SPARC)
1051     enum TLS_DTV_OFFSET = 0x0;
1052 else version (SPARC64)
1053     enum TLS_DTV_OFFSET = 0x0;
1054 else version (PPC)
1055     enum TLS_DTV_OFFSET = 0x8000;
1056 else version (PPC64)
1057     enum TLS_DTV_OFFSET = 0x8000;
1058 else version (MIPS32)
1059     enum TLS_DTV_OFFSET = 0x8000;
1060 else version (MIPS64)
1061     enum TLS_DTV_OFFSET = 0x8000;
1062 else version (IBMZ_Any)
1063     enum TLS_DTV_OFFSET = 0x0;
1064 else
1065     static assert( false, "Platform not supported." );
1066 
getTLSRange(size_t mod,size_t sz)1067 void[] getTLSRange(size_t mod, size_t sz) nothrow @nogc
1068 {
1069     if (mod == 0)
1070         return null;
1071 
1072     version (GNU_EMUTLS)
1073         return null;    // Handled in scanTLSRanges().
1074     else
1075     {
1076         version (Solaris)
1077         {
1078             static if (!OS_Have_Dlpi_Tls_Modid)
1079                 mod -= 1;
1080         }
1081 
1082         // base offset
1083         auto ti = tls_index(mod, 0);
1084         version (CRuntime_Musl)
1085             return (__tls_get_addr(&ti)-TLS_DTV_OFFSET)[0 .. sz];
1086         else version (IBMZ_Any)
1087         {
1088             // IBM Z only provides __tls_get_offset instead of __tls_get_addr
1089             // which returns an offset relative to the thread pointer.
1090             auto addr = __ibmz_get_tls_offset(&ti);
1091             addr = addr + cast(c_ulong)__builtin_thread_pointer();
1092             return addr[0 .. sz];
1093         }
1094         else
1095             return (__tls_get_addr(&ti)-TLS_DTV_OFFSET)[0 .. sz];
1096     }
1097 }
1098