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