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; 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 (DragonFlyBSD) 67 { 68 import core.sys.dragonflybsd.dlfcn; 69 import core.sys.dragonflybsd.sys.elf; 70 import core.sys.dragonflybsd.sys.link_elf; 71 } 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 { 107 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 117 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 127 @property immutable(ModuleInfo*)[] modules() const nothrow @nogc 128 { 129 return _moduleGroup.modules; 130 } 131 132 @property ref inout(ModuleGroup) moduleGroup() inout nothrow @nogc 133 { 134 return _moduleGroup; 135 } 136 137 @property immutable(FuncTable)[] ehTables() const nothrow @nogc 138 { 139 return null; 140 } 141 142 @property inout(void[])[] gcRanges() inout nothrow @nogc 143 { 144 return _gcRanges[]; 145 } 146 147 private: 148 149 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 160 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 168 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 */ 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 */ 202 void finiSections() nothrow @nogc 203 { 204 _isRuntimeInitialized = false; 205 } 206 207 alias ScanDG = void delegate(void* pbeg, void* pend) nothrow; 208 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 { 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 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 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 */ 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 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 639 void initLocks() nothrow @nogc 640 { 641 version (Shared) 642 !pthread_mutex_init(&_handleToDSOMutex, null) || assert(0); 643 } 644 645 void finiLocks() nothrow @nogc 646 { 647 version (Shared) 648 !pthread_mutex_destroy(&_handleToDSOMutex) || assert(0); 649 } 650 651 void runModuleConstructors(DSO* pdso, bool runTlsCtors) 652 { 653 pdso._moduleGroup.sortCtors(); 654 pdso._moduleGroup.runCtors(); 655 if (runTlsCtors) pdso._moduleGroup.runTlsCtors(); 656 } 657 658 void runModuleDestructors(DSO* pdso, bool runTlsDtors) 659 { 660 if (runTlsDtors) pdso._moduleGroup.runTlsDtors(); 661 pdso._moduleGroup.runDtors(); 662 } 663 664 void registerGCRanges(DSO* pdso) nothrow @nogc 665 { 666 foreach (rng; pdso._gcRanges) 667 GC.addRange(rng.ptr, rng.length); 668 } 669 670 void unregisterGCRanges(DSO* pdso) nothrow @nogc 671 { 672 foreach (rng; pdso._gcRanges) 673 GC.removeRange(rng.ptr); 674 } 675 676 version (Shared) void runFinalizers(DSO* pdso) 677 { 678 foreach (seg; pdso._codeSegments) 679 GC.runFinalizers(seg); 680 } 681 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 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 */ 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 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 } 928 else version (FreeBSD) 929 { 930 return !!_rtld_addr_phdr(addr, result); 931 } 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 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 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 */ 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 { 1011 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 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