Home | History | Annotate | Line # | Download | only in sections
      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;
     48 version (linux)
     49 {
     50     import core.sys.linux.dlfcn;
     51     import core.sys.linux.elf;
     52     import core.sys.linux.link;
     53 }
     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 }
     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 }
     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 }
     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 }
     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 {
    102     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 
    112     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 
    122     @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 
    150     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
    158     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  */
    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  */
    182 void finiSections() nothrow @nogc
    183 {
    184     _isRuntimeInitialized = false;
    185 }
    186 
    187 alias ScanDG = void delegate(void* pbeg, void* pend) nothrow;
    188 
    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     {
    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 
    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 
    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 
    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  */
    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 
    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 
    638 void initLocks() nothrow @nogc
    639 {
    640     version (Shared)
    641         !pthread_mutex_init(&_handleToDSOMutex, null) || assert(0);
    642 }
    643 
    644 void finiLocks() nothrow @nogc
    645 {
    646     version (Shared)
    647         !pthread_mutex_destroy(&_handleToDSOMutex) || assert(0);
    648 }
    649 
    650 void runModuleConstructors(DSO* pdso, bool runTlsCtors)
    651 {
    652     pdso._moduleGroup.sortCtors();
    653     pdso._moduleGroup.runCtors();
    654     if (runTlsCtors) pdso._moduleGroup.runTlsCtors();
    655 }
    656 
    657 void runModuleDestructors(DSO* pdso, bool runTlsDtors)
    658 {
    659     if (runTlsDtors) pdso._moduleGroup.runTlsDtors();
    660     pdso._moduleGroup.runDtors();
    661 }
    662 
    663 void registerGCRanges(DSO* pdso) nothrow @nogc
    664 {
    665     foreach (rng; pdso._gcRanges)
    666         GC.addRange(rng.ptr, rng.length);
    667 }
    668 
    669 void unregisterGCRanges(DSO* pdso) nothrow @nogc
    670 {
    671     foreach (rng; pdso._gcRanges)
    672         GC.removeRange(rng.ptr);
    673 }
    674 
    675 version (Shared) void runFinalizers(DSO* pdso)
    676 {
    677     foreach (seg; pdso._codeSegments)
    678         GC.runFinalizers(seg);
    679 }
    680 
    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 
    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  */
    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 
    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     }
    944     else version (FreeBSD)
    945     {
    946         return !!_rtld_addr_phdr(addr, result);
    947     }
    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  */
    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 {
   1004     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 
   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