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