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