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