]>
Commit | Line | Data |
---|---|---|
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 | 23 | module gcc.sections.elf; |
b4c522fa | 24 | |
81f928ec IB |
25 | version (MIPS32) version = MIPS_Any; |
26 | version (MIPS64) version = MIPS_Any; | |
d9392bfa IB |
27 | version (RISCV32) version = RISCV_Any; |
28 | version (RISCV64) version = RISCV_Any; | |
ca0ddb39 IB |
29 | version (S390) version = IBMZ_Any; |
30 | version (SystemZ) version = IBMZ_Any; | |
d9392bfa | 31 | |
b4c522fa | 32 | version (CRuntime_Glibc) enum SharedELF = true; |
8b651828 | 33 | else version (CRuntime_Musl) enum SharedELF = true; |
b4c522fa IB |
34 | else version (FreeBSD) enum SharedELF = true; |
35 | else version (NetBSD) enum SharedELF = true; | |
d86e6085 | 36 | else version (OpenBSD) enum SharedELF = true; |
8b651828 IB |
37 | else version (DragonFlyBSD) enum SharedELF = true; |
38 | else version (CRuntime_UClibc) enum SharedELF = true; | |
4d513120 | 39 | else version (Solaris) enum SharedELF = true; |
b4c522fa IB |
40 | else enum SharedELF = false; |
41 | static if (SharedELF): | |
42 | ||
b4c522fa | 43 | import core.memory; |
685ae5b8 | 44 | import core.stdc.config; |
b4c522fa IB |
45 | import core.stdc.stdio; |
46 | import core.stdc.stdlib : calloc, exit, free, malloc, EXIT_FAILURE; | |
47 | import core.stdc.string : strlen; | |
48 | version (linux) | |
49 | { | |
50 | import core.sys.linux.dlfcn; | |
51 | import core.sys.linux.elf; | |
52 | import core.sys.linux.link; | |
53 | } | |
54 | else version (FreeBSD) | |
55 | { | |
56 | import core.sys.freebsd.dlfcn; | |
57 | import core.sys.freebsd.sys.elf; | |
58 | import core.sys.freebsd.sys.link_elf; | |
59 | } | |
60 | else version (NetBSD) | |
61 | { | |
62 | import core.sys.netbsd.dlfcn; | |
63 | import core.sys.netbsd.sys.elf; | |
64 | import core.sys.netbsd.sys.link_elf; | |
65 | } | |
d86e6085 IB |
66 | else 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 |
72 | else 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 |
78 | else 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 |
85 | else |
86 | { | |
87 | static assert(0, "unimplemented"); | |
88 | } | |
89 | import core.sys.posix.pthread; | |
90 | import rt.deh; | |
91 | import rt.dmain2; | |
92 | import rt.minfo; | |
5fee5ec3 IB |
93 | import core.internal.container.array; |
94 | import core.internal.container.hashtab; | |
32703b80 IB |
95 | import gcc.builtins; |
96 | import gcc.config; | |
97 | import gcc.sections.common; | |
8b651828 | 98 | |
b4c522fa IB |
99 | alias DSO SectionGroup; |
100 | struct 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 | ||
137 | private: | |
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 | */ | |
173 | void 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 | */ | |
182 | void finiSections() nothrow @nogc | |
183 | { | |
184 | _isRuntimeInitialized = false; | |
185 | } | |
186 | ||
187 | alias ScanDG = void delegate(void* pbeg, void* pend) nothrow; | |
188 | ||
189 | version (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 | } | |
304 | else | |
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 | ||
349 | private: | |
350 | ||
b4c522fa IB |
351 | version (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 | } |
394 | else | |
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 | */ | |
419 | struct 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 | ||
426 | T[] 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 | */ | |
433 | extern(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 | ||
562 | version (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 | ||
638 | void initLocks() nothrow @nogc | |
639 | { | |
640 | version (Shared) | |
641 | !pthread_mutex_init(&_handleToDSOMutex, null) || assert(0); | |
642 | } | |
643 | ||
644 | void finiLocks() nothrow @nogc | |
645 | { | |
646 | version (Shared) | |
647 | !pthread_mutex_destroy(&_handleToDSOMutex) || assert(0); | |
648 | } | |
649 | ||
650 | void runModuleConstructors(DSO* pdso, bool runTlsCtors) | |
651 | { | |
652 | pdso._moduleGroup.sortCtors(); | |
653 | pdso._moduleGroup.runCtors(); | |
654 | if (runTlsCtors) pdso._moduleGroup.runTlsCtors(); | |
655 | } | |
656 | ||
657 | void runModuleDestructors(DSO* pdso, bool runTlsDtors) | |
658 | { | |
659 | if (runTlsDtors) pdso._moduleGroup.runTlsDtors(); | |
660 | pdso._moduleGroup.runDtors(); | |
661 | } | |
662 | ||
663 | void registerGCRanges(DSO* pdso) nothrow @nogc | |
664 | { | |
665 | foreach (rng; pdso._gcRanges) | |
666 | GC.addRange(rng.ptr, rng.length); | |
667 | } | |
668 | ||
669 | void unregisterGCRanges(DSO* pdso) nothrow @nogc | |
670 | { | |
671 | foreach (rng; pdso._gcRanges) | |
672 | GC.removeRange(rng.ptr); | |
673 | } | |
674 | ||
675 | version (Shared) void runFinalizers(DSO* pdso) | |
676 | { | |
677 | foreach (seg; pdso._codeSegments) | |
678 | GC.runFinalizers(seg); | |
679 | } | |
680 | ||
681 | void 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 | ||
693 | version (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 | */ | |
836 | void 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 | 913 | bool 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 | */ | |
960 | bool 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 | */ | |
983 | version (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 | */ | |
1002 | struct 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 | ||
1027 | extern(C) void* __tls_get_addr(tls_index* ti) nothrow @nogc; | |
88e508f9 | 1028 | extern(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 | */ |
1036 | version (X86) | |
5a5129a0 | 1037 | enum TLS_DTV_OFFSET = 0x0; |
b4c522fa | 1038 | else version (X86_64) |
5a5129a0 | 1039 | enum TLS_DTV_OFFSET = 0x0; |
b4c522fa | 1040 | else version (ARM) |
5a5129a0 | 1041 | enum TLS_DTV_OFFSET = 0x0; |
b4c522fa | 1042 | else version (AArch64) |
5a5129a0 IB |
1043 | enum TLS_DTV_OFFSET = 0x0; |
1044 | else version (RISCV32) | |
1045 | enum TLS_DTV_OFFSET = 0x800; | |
1046 | else version (RISCV64) | |
1047 | enum TLS_DTV_OFFSET = 0x800; | |
50deb970 IB |
1048 | else version (HPPA) |
1049 | enum TLS_DTV_OFFSET = 0x0; | |
b4c522fa | 1050 | else version (SPARC) |
5a5129a0 | 1051 | enum TLS_DTV_OFFSET = 0x0; |
b4c522fa | 1052 | else version (SPARC64) |
5a5129a0 | 1053 | enum TLS_DTV_OFFSET = 0x0; |
b4c522fa IB |
1054 | else version (PPC) |
1055 | enum TLS_DTV_OFFSET = 0x8000; | |
1056 | else version (PPC64) | |
1057 | enum TLS_DTV_OFFSET = 0x8000; | |
1058 | else version (MIPS32) | |
1059 | enum TLS_DTV_OFFSET = 0x8000; | |
1060 | else version (MIPS64) | |
1061 | enum TLS_DTV_OFFSET = 0x8000; | |
ca0ddb39 | 1062 | else version (IBMZ_Any) |
130cc10e | 1063 | enum TLS_DTV_OFFSET = 0x0; |
b4c522fa IB |
1064 | else |
1065 | static assert( false, "Platform not supported." ); | |
1066 | ||
1067 | void[] 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 | } |