]> git.ipfire.org Git - thirdparty/gcc.git/blame - libphobos/libdruntime/gcc/sections/macho.d
d: Import dmd b8384668f, druntime e6caaab9, phobos 5ab9ad256 (v2.098.0-beta.1)
[thirdparty/gcc.git] / libphobos / libdruntime / gcc / sections / macho.d
CommitLineData
32703b80
IB
1// MACHO-specific support for sections.
2// Copyright (C) 2021 Free Software Foundation, Inc.
3
4// GCC is free software; you can redistribute it and/or modify it under
5// the terms of the GNU General Public License as published by the Free
6// Software Foundation; either version 3, or (at your option) any later
7// version.
8
9// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
10// WARRANTY; without even the implied warranty of MERCHANTABILITY or
11// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12// for more details.
13
14// Under Section 7 of GPL version 3, you are granted additional
15// permissions described in the GCC Runtime Library Exception, version
16// 3.1, as published by the Free Software Foundation.
17
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.macho;
24
25version (OSX):
26
27import core.memory;
28import core.stdc.stdlib;
29import core.sys.darwin.dlfcn;
30import core.sys.darwin.mach.dyld;
31import core.sys.darwin.mach.getsect;
32import core.sys.posix.pthread;
33import rt.minfo;
5fee5ec3
IB
34import core.internal.container.array;
35import core.internal.container.hashtab;
32703b80
IB
36import gcc.sections.common;
37
38version (GNU_EMUTLS)
39 import gcc.emutls;
40
41alias DSO SectionGroup;
42struct DSO
43{
44 static int opApply(scope int delegate(ref DSO) dg)
45 {
46 foreach (dso; _loadedDSOs)
47 {
48 if (auto res = dg(*dso))
49 return res;
50 }
51 return 0;
52 }
53
54 static int opApplyReverse(scope int delegate(ref DSO) dg)
55 {
56 foreach_reverse (dso; _loadedDSOs)
57 {
58 if (auto res = dg(*dso))
59 return res;
60 }
61 return 0;
62 }
63
64 @property immutable(ModuleInfo*)[] modules() const nothrow @nogc
65 {
66 return _moduleGroup.modules;
67 }
68
5fee5ec3 69 @property ref inout(ModuleGroup) moduleGroup() inout return nothrow @nogc
32703b80
IB
70 {
71 return _moduleGroup;
72 }
73
74 @property inout(void[])[] gcRanges() inout nothrow @nogc
75 {
76 return _gcRanges[];
77 }
78
79private:
80
81 invariant()
82 {
83 safeAssert(_moduleGroup.modules.length > 0, "No modules for DSO.");
84 }
85
86 void** _slot;
87 ModuleGroup _moduleGroup;
88 Array!(void[]) _gcRanges;
89
90 version (Shared)
91 {
92 Array!(void[]) _codeSegments; // array of code segments
93 Array!(DSO*) _deps; // D libraries needed by this DSO
94 void* _handle; // corresponding handle
95 }
96}
97
98/****
99 * Boolean flag set to true while the runtime is initialized.
100 */
101__gshared bool _isRuntimeInitialized;
102
103/****
104 * Gets called on program startup just before GC is initialized.
105 */
106void initSections() nothrow @nogc
107{
108 _isRuntimeInitialized = true;
109}
110
111/***
112 * Gets called on program shutdown just after GC is terminated.
113 */
114void finiSections() nothrow @nogc
115{
116 _isRuntimeInitialized = false;
117}
118
119alias ScanDG = void delegate(void* pbeg, void* pend) nothrow;
120
121version (Shared)
122{
123 import gcc.sections : pinLoadedLibraries, unpinLoadedLibraries,
124 inheritLoadedLibraries, cleanupLoadedLibraries;
125
126 /***
127 * Called once per thread; returns array of thread local storage ranges
128 */
129 Array!(ThreadDSO)* initTLSRanges() @nogc nothrow
130 {
131 return &_loadedDSOs();
132 }
133
134 void finiTLSRanges(Array!(ThreadDSO)* tdsos) @nogc nothrow
135 {
136 // Nothing to do here. tdsos used to point to the _loadedDSOs instance
137 // in the dying thread's TLS segment and as such is not valid anymore.
138 // The memory for the array contents was already reclaimed in
139 // cleanupLoadedLibraries().
140 }
141
142 void scanTLSRanges(Array!(ThreadDSO)* tdsos, scope ScanDG dg) nothrow
143 {
144 version (GNU_EMUTLS)
145 _d_emutls_scan(dg);
146 else
147 static assert(0, "Native TLS unimplemented");
148 }
149
150 // interface for core.thread to inherit loaded libraries
151 pragma(mangle, gcc.sections.pinLoadedLibraries.mangleof)
152 void* pinLoadedLibraries() nothrow @nogc
153 {
154 auto res = cast(Array!(ThreadDSO)*)calloc(1, Array!(ThreadDSO).sizeof);
155 res.length = _loadedDSOs.length;
156 foreach (i, ref tdso; _loadedDSOs)
157 {
158 (*res)[i] = tdso;
159 if (tdso._addCnt)
160 {
161 // Increment the dlopen ref for explicitly loaded libraries to pin them.
162 const success = .dlopen(nameForDSO(tdso._pdso), RTLD_LAZY) !is null;
163 safeAssert(success, "Failed to increment dlopen ref.");
164 (*res)[i]._addCnt = 1; // new array takes over the additional ref count
165 }
166 }
167 return res;
168 }
169
170 pragma(mangle, gcc.sections.unpinLoadedLibraries.mangleof)
171 void unpinLoadedLibraries(void* p) nothrow @nogc
172 {
173 auto pary = cast(Array!(ThreadDSO)*)p;
174 // In case something failed we need to undo the pinning.
175 foreach (ref tdso; *pary)
176 {
177 if (tdso._addCnt)
178 {
179 auto handle = tdso._pdso._handle;
180 safeAssert(handle !is null, "Invalid library handle.");
181 .dlclose(handle);
182 }
183 }
184 pary.reset();
185 .free(pary);
186 }
187
188 // Called before TLS ctors are ran, copy over the loaded libraries
189 // of the parent thread.
190 pragma(mangle, gcc.sections.inheritLoadedLibraries.mangleof)
191 void inheritLoadedLibraries(void* p) nothrow @nogc
192 {
193 safeAssert(_loadedDSOs.empty, "DSOs have already been registered for this thread.");
194 _loadedDSOs.swap(*cast(Array!(ThreadDSO)*)p);
195 .free(p);
196 }
197
198 // Called after all TLS dtors ran, decrements all remaining dlopen refs.
199 pragma(mangle, gcc.sections.cleanupLoadedLibraries.mangleof)
200 void cleanupLoadedLibraries() nothrow @nogc
201 {
202 foreach (ref tdso; _loadedDSOs)
203 {
204 if (tdso._addCnt == 0) continue;
205
206 auto handle = tdso._pdso._handle;
207 safeAssert(handle !is null, "Invalid DSO handle.");
208 for (; tdso._addCnt > 0; --tdso._addCnt)
209 .dlclose(handle);
210 }
211
212 // Free the memory for the array contents.
213 _loadedDSOs.reset();
214 }
215}
216else
217{
218 /***
219 * Called once per thread; returns array of thread local storage ranges
220 */
221 Array!(void[])* initTLSRanges() nothrow @nogc
222 {
223 return null;
224 }
225
226 void finiTLSRanges(Array!(void[])* rngs) nothrow @nogc
227 {
228 }
229
230 void scanTLSRanges(Array!(void[])* rngs, scope ScanDG dg) nothrow
231 {
232 version (GNU_EMUTLS)
233 _d_emutls_scan(dg);
234 else
235 static assert(0, "Native TLS unimplemented");
236 }
237}
238
239private:
240
241version (Shared)
242{
243 /*
244 * Array of thread local DSO metadata for all libraries loaded and
245 * initialized in this thread.
246 *
247 * Note:
248 * A newly spawned thread will inherit these libraries.
249 * Note:
250 * We use an array here to preserve the order of
251 * initialization. If that became a performance issue, we
252 * could use a hash table and enumerate the DSOs during
253 * loading so that the hash table values could be sorted when
254 * necessary.
255 */
256 struct ThreadDSO
257 {
258 DSO* _pdso;
259 static if (_pdso.sizeof == 8) uint _refCnt, _addCnt;
260 else static if (_pdso.sizeof == 4) ushort _refCnt, _addCnt;
261 else static assert(0, "unimplemented");
262 alias _pdso this;
263 }
264
265 @property ref Array!(ThreadDSO) _loadedDSOs() @nogc nothrow
266 {
267 static Array!(ThreadDSO) x;
268 return x;
269 }
270
271 /*
272 * Set to true during rt_loadLibrary/rt_unloadLibrary calls.
273 */
274 bool _rtLoading;
275
276 /*
277 * Hash table to map the native handle (as returned by dlopen)
278 * to the corresponding DSO*, protected by a mutex.
279 */
280 __gshared pthread_mutex_t _handleToDSOMutex;
281 @property ref HashTab!(void*, DSO*) _handleToDSO() @nogc nothrow
282 {
283 __gshared HashTab!(void*, DSO*) x;
284 return x;
285 }
286}
287else
288{
289 /*
290 * Static DSOs loaded by the runtime linker. This includes the
291 * executable. These can't be unloaded.
292 */
293 @property ref Array!(DSO*) _loadedDSOs() @nogc nothrow
294 {
295 __gshared Array!(DSO*) x;
296 return x;
297 }
298
299 enum _rtLoading = false;
300}
301
302///////////////////////////////////////////////////////////////////////////////
303// Compiler to runtime interface.
304///////////////////////////////////////////////////////////////////////////////
305
306struct MachHeader
307{
308 const(mach_header)* header; // the mach header of the image
309 intptr_t slide; // virtural memory address slide amount
310}
311
312/****
313 * This data structure is generated by the compiler, and then passed to
314 * _d_dso_registry().
315 */
316struct CompilerDSOData
317{
318 size_t _version; // currently 1
319 void** _slot; // can be used to store runtime data
320 immutable(object.ModuleInfo*)* _minfo_beg, _minfo_end; // array of modules in this object file
321}
322
323T[] toRange(T)(T* beg, T* end) { return beg[0 .. end - beg]; }
324
325/* For each shared library and executable, the compiler generates code that
326 * sets up CompilerDSOData and calls _d_dso_registry().
327 * A pointer to that code is inserted into both the .ctors and .dtors
328 * segment so it gets called by the loader on startup and shutdown.
329 */
330extern(C) void _d_dso_registry(CompilerDSOData* data)
331{
332 // only one supported currently
333 safeAssert(data._version >= 1, "Incompatible compiler-generated DSO data version.");
334
335 // no backlink => register
336 if (*data._slot is null)
337 {
338 immutable firstDSO = _loadedDSOs.empty;
339 if (firstDSO) initLocks();
340
341 DSO* pdso = cast(DSO*).calloc(1, DSO.sizeof);
342 assert(typeid(DSO).initializer().ptr is null);
343 pdso._slot = data._slot;
344 *data._slot = pdso; // store backlink in library record
345
346 pdso._moduleGroup = ModuleGroup(toRange(data._minfo_beg, data._minfo_end));
347
348 MachHeader header = void;
349 const headerFound = findImageHeaderForAddr(data._slot, header);
350 safeAssert(headerFound, "Failed to find image header.");
351
352 scanSegments(header, pdso);
353
354 version (Shared)
355 {
356 auto handle = handleForAddr(data._slot);
357
358 getDependencies(header, pdso._deps);
359 pdso._handle = handle;
360 setDSOForHandle(pdso, pdso._handle);
361
362 if (!_rtLoading)
363 {
364 /* This DSO was not loaded by rt_loadLibrary which
365 * happens for all dependencies of an executable or
366 * the first dlopen call from a C program.
367 * In this case we add the DSO to the _loadedDSOs of this
368 * thread with a refCnt of 1 and call the TlsCtors.
369 */
370 immutable ushort refCnt = 1, addCnt = 0;
371 _loadedDSOs.insertBack(ThreadDSO(pdso, refCnt, addCnt));
372 }
373 }
374 else
375 {
376 foreach (p; _loadedDSOs)
377 safeAssert(p !is pdso, "DSO already registered.");
378 _loadedDSOs.insertBack(pdso);
379 }
380
381 // don't initialize modules before rt_init was called
382 if (_isRuntimeInitialized)
383 {
384 registerGCRanges(pdso);
385 // rt_loadLibrary will run tls ctors, so do this only for dlopen
386 immutable runTlsCtors = !_rtLoading;
387 runModuleConstructors(pdso, runTlsCtors);
388 }
389 }
390 // has backlink => unregister
391 else
392 {
393 DSO* pdso = cast(DSO*)*data._slot;
394 *data._slot = null;
395
396 // don't finalizes modules after rt_term was called (see Bugzilla 11378)
397 if (_isRuntimeInitialized)
398 {
399 // rt_unloadLibrary already ran tls dtors, so do this only for dlclose
400 immutable runTlsDtors = !_rtLoading;
401 runModuleDestructors(pdso, runTlsDtors);
402 unregisterGCRanges(pdso);
403 // run finalizers after module dtors (same order as in rt_term)
404 version (Shared) runFinalizers(pdso);
405 }
406
407 version (Shared)
408 {
409 if (!_rtLoading)
410 {
411 /* This DSO was not unloaded by rt_unloadLibrary so we
412 * have to remove it from _loadedDSOs here.
413 */
414 foreach (i, ref tdso; _loadedDSOs)
415 {
416 if (tdso._pdso == pdso)
417 {
418 _loadedDSOs.remove(i);
419 break;
420 }
421 }
422 }
423
424 unsetDSOForHandle(pdso, pdso._handle);
425 }
426 else
427 {
428 // static DSOs are unloaded in reverse order
429 safeAssert(pdso == _loadedDSOs.back, "DSO being unregistered isn't current last one.");
430 _loadedDSOs.popBack();
431 }
432
433 freeDSO(pdso);
434
435 // last DSO being unloaded => shutdown registry
436 if (_loadedDSOs.empty)
437 {
438 version (GNU_EMUTLS)
439 _d_emutls_destroy();
440 version (Shared)
441 {
442 safeAssert(_handleToDSO.empty, "_handleToDSO not in sync with _loadedDSOs.");
443 _handleToDSO.reset();
444 }
445 finiLocks();
446 }
447 }
448}
449
450///////////////////////////////////////////////////////////////////////////////
451// dynamic loading
452///////////////////////////////////////////////////////////////////////////////
453
454// Shared D libraries are only supported when linking against a shared druntime library.
455
456version (Shared)
457{
458 ThreadDSO* findThreadDSO(DSO* pdso) nothrow @nogc
459 {
460 foreach (ref tdata; _loadedDSOs)
461 if (tdata._pdso == pdso) return &tdata;
462 return null;
463 }
464
465 void incThreadRef(DSO* pdso, bool incAdd)
466 {
467 if (auto tdata = findThreadDSO(pdso)) // already initialized
468 {
469 if (incAdd && ++tdata._addCnt > 1) return;
470 ++tdata._refCnt;
471 }
472 else
473 {
474 foreach (dep; pdso._deps)
475 incThreadRef(dep, false);
476 immutable ushort refCnt = 1, addCnt = incAdd ? 1 : 0;
477 _loadedDSOs.insertBack(ThreadDSO(pdso, refCnt, addCnt));
478 pdso._moduleGroup.runTlsCtors();
479 }
480 }
481
482 void decThreadRef(DSO* pdso, bool decAdd)
483 {
484 auto tdata = findThreadDSO(pdso);
485 safeAssert(tdata !is null, "Failed to find thread DSO.");
486 safeAssert(!decAdd || tdata._addCnt > 0, "Mismatching rt_unloadLibrary call.");
487
488 if (decAdd && --tdata._addCnt > 0) return;
489 if (--tdata._refCnt > 0) return;
490
491 pdso._moduleGroup.runTlsDtors();
492 foreach (i, ref td; _loadedDSOs)
493 if (td._pdso == pdso) _loadedDSOs.remove(i);
494 foreach (dep; pdso._deps)
495 decThreadRef(dep, false);
496 }
497
498 extern(C) void* rt_loadLibrary(const char* name)
499 {
500 immutable save = _rtLoading;
501 _rtLoading = true;
502 scope (exit) _rtLoading = save;
503
504 auto handle = .dlopen(name, RTLD_LAZY);
505 if (handle is null) return null;
506
507 // if it's a D library
508 if (auto pdso = dsoForHandle(handle))
509 incThreadRef(pdso, true);
510 return handle;
511 }
512
513 extern(C) int rt_unloadLibrary(void* handle)
514 {
515 if (handle is null) return false;
516
517 immutable save = _rtLoading;
518 _rtLoading = true;
519 scope (exit) _rtLoading = save;
520
521 // if it's a D library
522 if (auto pdso = dsoForHandle(handle))
523 decThreadRef(pdso, true);
524 return .dlclose(handle) == 0;
525 }
526}
527
528///////////////////////////////////////////////////////////////////////////////
529// helper functions
530///////////////////////////////////////////////////////////////////////////////
531
532void initLocks() nothrow @nogc
533{
534 version (Shared)
535 !pthread_mutex_init(&_handleToDSOMutex, null) || assert(0);
536}
537
538void finiLocks() nothrow @nogc
539{
540 version (Shared)
541 !pthread_mutex_destroy(&_handleToDSOMutex) || assert(0);
542}
543
544void runModuleConstructors(DSO* pdso, bool runTlsCtors)
545{
546 pdso._moduleGroup.sortCtors();
547 pdso._moduleGroup.runCtors();
548 if (runTlsCtors) pdso._moduleGroup.runTlsCtors();
549}
550
551void runModuleDestructors(DSO* pdso, bool runTlsDtors)
552{
553 if (runTlsDtors) pdso._moduleGroup.runTlsDtors();
554 pdso._moduleGroup.runDtors();
555}
556
557void registerGCRanges(DSO* pdso) nothrow @nogc
558{
559 foreach (rng; pdso._gcRanges)
560 GC.addRange(rng.ptr, rng.length);
561}
562
563void unregisterGCRanges(DSO* pdso) nothrow @nogc
564{
565 foreach (rng; pdso._gcRanges)
566 GC.removeRange(rng.ptr);
567}
568
569version (Shared) void runFinalizers(DSO* pdso)
570{
571 foreach (seg; pdso._codeSegments)
572 GC.runFinalizers(seg);
573}
574
575void freeDSO(DSO* pdso) nothrow @nogc
576{
577 pdso._gcRanges.reset();
578 version (Shared)
579 {
580 pdso._codeSegments.reset();
581 pdso._deps.reset();
582 pdso._handle = null;
583 }
584 .free(pdso);
585}
586
587version (Shared)
588{
589@nogc nothrow:
590 const(char)* nameForDSO(in DSO* pdso)
591 {
592 Dl_info info = void;
593 const success = dladdr(pdso._slot, &info) != 0;
594 safeAssert(success, "Failed to get DSO info.");
595 return info.dli_fname;
596 }
597
598 DSO* dsoForHandle(void* handle)
599 {
600 DSO* pdso;
601 !pthread_mutex_lock(&_handleToDSOMutex) || assert(0);
602 if (auto ppdso = handle in _handleToDSO)
603 pdso = *ppdso;
604 !pthread_mutex_unlock(&_handleToDSOMutex) || assert(0);
605 return pdso;
606 }
607
608 void setDSOForHandle(DSO* pdso, void* handle)
609 {
610 !pthread_mutex_lock(&_handleToDSOMutex) || assert(0);
611 safeAssert(handle !in _handleToDSO, "DSO already registered.");
612 _handleToDSO[handle] = pdso;
613 !pthread_mutex_unlock(&_handleToDSOMutex) || assert(0);
614 }
615
616 void unsetDSOForHandle(DSO* pdso, void* handle)
617 {
618 !pthread_mutex_lock(&_handleToDSOMutex) || assert(0);
619 safeAssert(_handleToDSO[handle] == pdso, "Handle doesn't match registered DSO.");
620 _handleToDSO.remove(handle);
621 !pthread_mutex_unlock(&_handleToDSOMutex) || assert(0);
622 }
623
624 void getDependencies(in MachHeader info, ref Array!(DSO*) deps)
625 {
626 // FIXME: Not implemented yet.
627 }
628
629 void* handleForName(const char* name)
630 {
631 auto handle = .dlopen(name, RTLD_NOLOAD | RTLD_LAZY);
632 if (handle !is null) .dlclose(handle); // drop reference count
633 return handle;
634 }
635}
636
637///////////////////////////////////////////////////////////////////////////////
638// Mach-O program header iteration
639///////////////////////////////////////////////////////////////////////////////
640
641/************
642 * Scan segments in the image header and store
643 * the writeable data segments in *pdso.
644 */
645
646void scanSegments(in MachHeader info, DSO* pdso)
647{
648 foreach (e; dataSegs)
649 {
650 auto sect = getSection(info.header, info.slide, e.seg.ptr, e.sect.ptr);
651 if (sect != null)
652 pdso._gcRanges.insertBack((cast(void*)sect.ptr)[0 .. sect.length]);
653 }
654
655 version (Shared)
656 {
657 void[] text = getSection(info.header, info.slide, "__TEXT", "__text");
658 if (!text)
659 assert(0, "Failed to get text section.");
660 pdso._codeSegments.insertBack(text);
661 }
662}
663
664/**************************
665 * Input:
666 * result where the output is to be written
667 * Returns:
668 * true if found, and *result is filled in
669 */
670
671bool findImageHeaderForAddr(in void* addr, out MachHeader result)
672{
673 Dl_info info;
674 if (dladdr(addr, &info) == 0)
675 return false;
676
677 foreach (i; 0 .. _dyld_image_count())
678 {
679 if (info.dli_fbase == _dyld_get_image_header(i))
680 {
681 result.header = cast(const(mach_header)*)info.dli_fbase;
682 result.slide = _dyld_get_image_vmaddr_slide(i);
683 return true;
684 }
685 }
686 return false;
687}
688
689/**************************
690 * Input:
691 * addr an internal address of a DSO
692 * Returns:
693 * the dlopen handle for that DSO or null if addr is not within a loaded DSO
694 */
695version (Shared) void* handleForAddr(void* addr) nothrow @nogc
696{
697 Dl_info info = void;
698 if (dladdr(addr, &info) != 0)
699 return handleForName(info.dli_fname);
700 return null;
701}
702
703struct SegRef
704{
705 string seg;
706 string sect;
707}
708
709static immutable SegRef[] dataSegs = [{SEG_DATA, SECT_DATA},
710 {SEG_DATA, SECT_BSS},
711 {SEG_DATA, SECT_COMMON}];
712
713/**
714 * Returns the section for the named section in the named segment
715 * for the mach_header pointer passed, or null if not found.
716 */
717ubyte[] getSection(in mach_header* header, intptr_t slide,
718 in char* segmentName, in char* sectionName)
719{
720 version (D_LP64)
721 {
722 assert(header.magic == MH_MAGIC_64);
723 auto sect = getsectbynamefromheader_64(cast(mach_header_64*)header,
724 segmentName,
725 sectionName);
726 }
727 else
728 {
729 assert(header.magic == MH_MAGIC);
730 auto sect = getsectbynamefromheader(header,
731 segmentName,
732 sectionName);
733 }
734
735 if (sect !is null && sect.size > 0)
736 return (cast(ubyte*)sect.addr + slide)[0 .. cast(size_t)sect.size];
737 return null;
738}