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