libphobos: Fix executables segfault on mipsel architecture
[gcc.git] / libphobos / libdruntime / gcc / sections / elf_shared.d
1 // ELF-specific support for sections with shared libraries.
2 // Copyright (C) 2019-2021 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 }