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