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