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