aboutsummaryrefslogtreecommitdiff
path: root/libphobos/libdruntime/gcc
diff options
context:
space:
mode:
authorIain Buclaw <ibuclaw@gdcproject.org>2019-04-13 15:29:15 +0000
committerIain Buclaw <ibuclaw@gcc.gnu.org>2019-04-13 15:29:15 +0000
commit8b6518285b87fd282573fa5d7d6f7414d58dc96b (patch)
tree513a3346a5fe38d485ac1a2ca28fc7336fcf3e33 /libphobos/libdruntime/gcc
parent151c5c0b80a30ba4316c86fcd0a7bf232a4ff127 (diff)
downloadgcc-8b6518285b87fd282573fa5d7d6f7414d58dc96b.zip
gcc-8b6518285b87fd282573fa5d7d6f7414d58dc96b.tar.gz
gcc-8b6518285b87fd282573fa5d7d6f7414d58dc96b.tar.bz2
libphobos: Move rt.sections modules to gcc.sections
These modules depend on a mixture between how the compiler emits run-time module information, and what functions are exposed by the platform to inquire about loaded global and thread-local data sections. As the upstream implementation is written to work only with how the reference D compiler writes out data, much of what is present does not apply to the GCC D front-end. So it has been moved to a non-upstream location in the source tree, where most of it will be rewritten once each port has been completed. The only tested module sections/elf_shared.d has been cleaned up so that all deprecated declarations have been removed, as well as the brittle module collision checking, which required bss_sections.c. All other ports have been left unchanged apart from a commonizing of attributes. libphobos/ChangeLog: 2019-04-13 Iain Buclaw <ibuclaw@gdcproject.org> * libdruntime/Makefile.am (DRUNTIME_CSOURCES): Remove bss_sections.c. (DRUNTIME_DSOURCES): Rename rt/sections_* modules to gcc/sections/*. * libdruntime/Makefile.in: Regenerate. * libdruntime/gcc/sections/android.d: New file. * libdruntime/gcc/sections/elf_shared.d: New file. * libdruntime/gcc/sections/osx.d: New file. * libdruntime/gcc/sections/package.d: New file. * libdruntime/gcc/sections/solaris.d: New file. * libdruntime/gcc/sections/win32.d: New file. * libdruntime/gcc/sections/win64.d: New file. * libdruntime/rt/bss_section.c: Remove. * libdruntime/rt/sections.d: Publicly import gcc.sections. * libdruntime/rt/sections_android.d: Remove. * libdruntime/rt/sections_elf_shared.d: Remove. * libdruntime/rt/sections_osx.d: Remove. * libdruntime/rt/sections_solaris.d: Remove. * libdruntime/rt/sections_win32.d: Remove. * libdruntime/rt/sections_win64.d: Remove. From-SVN: r270341
Diffstat (limited to 'libphobos/libdruntime/gcc')
-rw-r--r--libphobos/libdruntime/gcc/sections/android.d184
-rw-r--r--libphobos/libdruntime/gcc/sections/elf_shared.d978
-rw-r--r--libphobos/libdruntime/gcc/sections/osx.d284
-rw-r--r--libphobos/libdruntime/gcc/sections/package.d48
-rw-r--r--libphobos/libdruntime/gcc/sections/solaris.d126
-rw-r--r--libphobos/libdruntime/gcc/sections/win32.d183
-rw-r--r--libphobos/libdruntime/gcc/sections/win64.d321
7 files changed, 2124 insertions, 0 deletions
diff --git a/libphobos/libdruntime/gcc/sections/android.d b/libphobos/libdruntime/gcc/sections/android.d
new file mode 100644
index 0000000..f00bb89
--- /dev/null
+++ b/libphobos/libdruntime/gcc/sections/android.d
@@ -0,0 +1,184 @@
+// Bionic-specific support for sections.
+// Copyright (C) 2019 Free Software Foundation, Inc.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+// for more details.
+
+// Under Section 7 of GPL version 3, you are granted additional
+// permissions described in the GCC Runtime Library Exception, version
+// 3.1, as published by the Free Software Foundation.
+
+// You should have received a copy of the GNU General Public License and
+// a copy of the GCC Runtime Library Exception along with this program;
+// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
+// <http://www.gnu.org/licenses/>.
+
+module gcc.sections.android;
+
+version (CRuntime_Bionic):
+
+// debug = PRINTF;
+debug(PRINTF) import core.stdc.stdio;
+import core.stdc.stdlib : malloc, free;
+import rt.deh, rt.minfo;
+import core.sys.posix.pthread;
+import core.stdc.stdlib : calloc;
+import core.stdc.string : memcpy;
+
+struct SectionGroup
+{
+ static int opApply(scope int delegate(ref SectionGroup) dg)
+ {
+ return dg(_sections);
+ }
+
+ static int opApplyReverse(scope int delegate(ref SectionGroup) dg)
+ {
+ return dg(_sections);
+ }
+
+ @property immutable(ModuleInfo*)[] modules() const nothrow @nogc
+ {
+ return _moduleGroup.modules;
+ }
+
+ @property ref inout(ModuleGroup) moduleGroup() inout nothrow @nogc
+ {
+ return _moduleGroup;
+ }
+
+ @property immutable(FuncTable)[] ehTables() const nothrow @nogc
+ {
+ auto pbeg = cast(immutable(FuncTable)*)&__start_deh;
+ auto pend = cast(immutable(FuncTable)*)&__stop_deh;
+ return pbeg[0 .. pend - pbeg];
+ }
+
+ @property inout(void[])[] gcRanges() inout nothrow @nogc
+ {
+ return _gcRanges[];
+ }
+
+private:
+ ModuleGroup _moduleGroup;
+ void[][1] _gcRanges;
+}
+
+void initSections() nothrow @nogc
+{
+ pthread_key_create(&_tlsKey, null);
+
+ auto mbeg = cast(immutable ModuleInfo**)&__start_minfo;
+ auto mend = cast(immutable ModuleInfo**)&__stop_minfo;
+ _sections.moduleGroup = ModuleGroup(mbeg[0 .. mend - mbeg]);
+
+ auto pbeg = cast(void*)&_tlsend;
+ auto pend = cast(void*)&__bss_end__;
+ // _tlsend is a 32-bit int and may not be 64-bit void*-aligned, so align pbeg.
+ version (D_LP64) pbeg = cast(void*)(cast(size_t)(pbeg + 7) & ~cast(size_t)7);
+ _sections._gcRanges[0] = pbeg[0 .. pend - pbeg];
+}
+
+void finiSections() nothrow @nogc
+{
+ pthread_key_delete(_tlsKey);
+}
+
+void[]* initTLSRanges() nothrow @nogc
+{
+ return &getTLSBlock();
+}
+
+void finiTLSRanges(void[]* rng) nothrow @nogc
+{
+ .free(rng.ptr);
+ .free(rng);
+}
+
+void scanTLSRanges(void[]* rng, scope void delegate(void* pbeg, void* pend) nothrow dg) nothrow
+{
+ dg(rng.ptr, rng.ptr + rng.length);
+}
+
+/* NOTE: The Bionic C library ignores thread-local data stored in the normal
+ * .tbss/.tdata ELF sections, which are marked with the SHF_TLS/STT_TLS
+ * flags. So instead we roll our own by keeping TLS data in the
+ * .tdata/.tbss sections but removing the SHF_TLS/STT_TLS flags, and
+ * access the TLS data using this function and the _tlsstart/_tlsend
+ * symbols as delimiters.
+ *
+ * This function is called by the code emitted by the compiler. It
+ * is expected to translate an address in the TLS static data to
+ * the corresponding address in the TLS dynamic per-thread data.
+ */
+
+extern(C) void* __tls_get_addr( void* p ) nothrow @nogc
+{
+ debug(PRINTF) printf(" __tls_get_addr input - %p\n", p);
+ immutable offset = cast(size_t)(p - cast(void*)&_tlsstart);
+ auto tls = getTLSBlockAlloc();
+ assert(offset < tls.length);
+ return tls.ptr + offset;
+}
+
+private:
+
+__gshared pthread_key_t _tlsKey;
+
+ref void[] getTLSBlock() nothrow @nogc
+{
+ auto pary = cast(void[]*)pthread_getspecific(_tlsKey);
+ if (pary is null)
+ {
+ pary = cast(void[]*).calloc(1, (void[]).sizeof);
+ if (pthread_setspecific(_tlsKey, pary) != 0)
+ {
+ import core.stdc.stdio;
+ perror("pthread_setspecific failed with");
+ assert(0);
+ }
+ }
+ return *pary;
+}
+
+ref void[] getTLSBlockAlloc() nothrow @nogc
+{
+ auto pary = &getTLSBlock();
+ if (!pary.length)
+ {
+ auto pbeg = cast(void*)&_tlsstart;
+ auto pend = cast(void*)&_tlsend;
+ auto p = .malloc(pend - pbeg);
+ memcpy(p, pbeg, pend - pbeg);
+ *pary = p[0 .. pend - pbeg];
+ }
+ return *pary;
+}
+
+__gshared SectionGroup _sections;
+
+extern(C)
+{
+ /* Symbols created by the compiler/linker and inserted into the
+ * object file that 'bracket' sections.
+ */
+ extern __gshared
+ {
+ void* __start_deh;
+ void* __stop_deh;
+ void* __start_minfo;
+ void* __stop_minfo;
+
+ size_t __bss_end__;
+
+ int _tlsstart;
+ int _tlsend;
+ }
+}
diff --git a/libphobos/libdruntime/gcc/sections/elf_shared.d b/libphobos/libdruntime/gcc/sections/elf_shared.d
new file mode 100644
index 0000000..f70c232
--- /dev/null
+++ b/libphobos/libdruntime/gcc/sections/elf_shared.d
@@ -0,0 +1,978 @@
+// ELF-specific support for sections with shared libraries.
+// Copyright (C) 2019 Free Software Foundation, Inc.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+// for more details.
+
+// Under Section 7 of GPL version 3, you are granted additional
+// permissions described in the GCC Runtime Library Exception, version
+// 3.1, as published by the Free Software Foundation.
+
+// You should have received a copy of the GNU General Public License and
+// a copy of the GCC Runtime Library Exception along with this program;
+// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
+// <http://www.gnu.org/licenses/>.
+
+module gcc.sections.elf_shared;
+
+version (CRuntime_Glibc) enum SharedELF = true;
+else version (CRuntime_Musl) enum SharedELF = true;
+else version (FreeBSD) enum SharedELF = true;
+else version (NetBSD) enum SharedELF = true;
+else version (DragonFlyBSD) enum SharedELF = true;
+else version (CRuntime_UClibc) enum SharedELF = true;
+else enum SharedELF = false;
+static if (SharedELF):
+
+// debug = PRINTF;
+import core.memory;
+import core.stdc.config;
+import core.stdc.stdio;
+import core.stdc.stdlib : calloc, exit, free, malloc, EXIT_FAILURE;
+import core.stdc.string : strlen;
+version (linux)
+{
+ import core.sys.linux.dlfcn;
+ import core.sys.linux.elf;
+ import core.sys.linux.link;
+}
+else version (FreeBSD)
+{
+ import core.sys.freebsd.dlfcn;
+ import core.sys.freebsd.sys.elf;
+ import core.sys.freebsd.sys.link_elf;
+}
+else version (NetBSD)
+{
+ import core.sys.netbsd.dlfcn;
+ import core.sys.netbsd.sys.elf;
+ import core.sys.netbsd.sys.link_elf;
+}
+else version (DragonFlyBSD)
+{
+ import core.sys.dragonflybsd.dlfcn;
+ import core.sys.dragonflybsd.sys.elf;
+ import core.sys.dragonflybsd.sys.link_elf;
+}
+else
+{
+ static assert(0, "unimplemented");
+}
+import core.sys.posix.pthread;
+import rt.deh;
+import rt.dmain2;
+import rt.minfo;
+import rt.util.container.array;
+import rt.util.container.hashtab;
+
+/****
+ * Asserts the specified condition, independent from -release, by abort()ing.
+ * Regular assertions throw an AssertError and thus require an initialized
+ * GC, which isn't the case (yet or anymore) for the startup/shutdown code in
+ * this module (called by CRT ctors/dtors etc.).
+ */
+private void safeAssert(bool condition, scope string msg, size_t line = __LINE__) @nogc nothrow @safe
+{
+ import core.internal.abort;
+ condition || abort(msg, __FILE__, line);
+}
+
+alias DSO SectionGroup;
+struct DSO
+{
+ static int opApply(scope int delegate(ref DSO) dg)
+ {
+ foreach (dso; _loadedDSOs)
+ {
+ if (auto res = dg(*dso))
+ return res;
+ }
+ return 0;
+ }
+
+ static int opApplyReverse(scope int delegate(ref DSO) dg)
+ {
+ foreach_reverse (dso; _loadedDSOs)
+ {
+ if (auto res = dg(*dso))
+ return res;
+ }
+ return 0;
+ }
+
+ @property immutable(ModuleInfo*)[] modules() const nothrow @nogc
+ {
+ return _moduleGroup.modules;
+ }
+
+ @property ref inout(ModuleGroup) moduleGroup() inout nothrow @nogc
+ {
+ return _moduleGroup;
+ }
+
+ @property immutable(FuncTable)[] ehTables() const nothrow @nogc
+ {
+ return null;
+ }
+
+ @property inout(void[])[] gcRanges() inout nothrow @nogc
+ {
+ return _gcRanges[];
+ }
+
+private:
+
+ invariant()
+ {
+ safeAssert(_moduleGroup.modules.length > 0, "No modules for DSO.");
+ safeAssert(_tlsMod || !_tlsSize, "Inconsistent TLS fields for DSO.");
+ }
+
+ ModuleGroup _moduleGroup;
+ Array!(void[]) _gcRanges;
+ size_t _tlsMod;
+ size_t _tlsSize;
+
+ version (Shared)
+ {
+ Array!(void[]) _codeSegments; // array of code segments
+ Array!(DSO*) _deps; // D libraries needed by this DSO
+ void* _handle; // corresponding handle
+ }
+
+ // get the TLS range for the executing thread
+ void[] tlsRange() const nothrow @nogc
+ {
+ return getTLSRange(_tlsMod, _tlsSize);
+ }
+}
+
+/****
+ * Boolean flag set to true while the runtime is initialized.
+ */
+__gshared bool _isRuntimeInitialized;
+
+
+version (FreeBSD) private __gshared void* dummy_ref;
+version (DragonFlyBSD) private __gshared void* dummy_ref;
+version (NetBSD) private __gshared void* dummy_ref;
+
+/****
+ * Gets called on program startup just before GC is initialized.
+ */
+void initSections() nothrow @nogc
+{
+ _isRuntimeInitialized = true;
+ // reference symbol to support weak linkage
+ version (FreeBSD) dummy_ref = &_d_dso_registry;
+ version (DragonFlyBSD) dummy_ref = &_d_dso_registry;
+ version (NetBSD) dummy_ref = &_d_dso_registry;
+}
+
+
+/***
+ * Gets called on program shutdown just after GC is terminated.
+ */
+void finiSections() nothrow @nogc
+{
+ _isRuntimeInitialized = false;
+}
+
+alias ScanDG = void delegate(void* pbeg, void* pend) nothrow;
+
+version (Shared)
+{
+ /***
+ * Called once per thread; returns array of thread local storage ranges
+ */
+ Array!(ThreadDSO)* initTLSRanges() @nogc nothrow
+ {
+ return &_loadedDSOs();
+ }
+
+ void finiTLSRanges(Array!(ThreadDSO)* tdsos) @nogc nothrow
+ {
+ // Nothing to do here. tdsos used to point to the _loadedDSOs instance
+ // in the dying thread's TLS segment and as such is not valid anymore.
+ // The memory for the array contents was already reclaimed in
+ // cleanupLoadedLibraries().
+ }
+
+ void scanTLSRanges(Array!(ThreadDSO)* tdsos, scope ScanDG dg) nothrow
+ {
+ foreach (ref tdso; *tdsos)
+ dg(tdso._tlsRange.ptr, tdso._tlsRange.ptr + tdso._tlsRange.length);
+ }
+
+ // interface for core.thread to inherit loaded libraries
+ void* pinLoadedLibraries() nothrow @nogc
+ {
+ auto res = cast(Array!(ThreadDSO)*)calloc(1, Array!(ThreadDSO).sizeof);
+ res.length = _loadedDSOs.length;
+ foreach (i, ref tdso; _loadedDSOs)
+ {
+ (*res)[i] = tdso;
+ if (tdso._addCnt)
+ {
+ // Increment the dlopen ref for explicitly loaded libraries to pin them.
+ const success = .dlopen(linkMapForHandle(tdso._pdso._handle).l_name, RTLD_LAZY) !is null;
+ safeAssert(success, "Failed to increment dlopen ref.");
+ (*res)[i]._addCnt = 1; // new array takes over the additional ref count
+ }
+ }
+ return res;
+ }
+
+ void unpinLoadedLibraries(void* p) nothrow @nogc
+ {
+ auto pary = cast(Array!(ThreadDSO)*)p;
+ // In case something failed we need to undo the pinning.
+ foreach (ref tdso; *pary)
+ {
+ if (tdso._addCnt)
+ {
+ auto handle = tdso._pdso._handle;
+ safeAssert(handle !is null, "Invalid library handle.");
+ .dlclose(handle);
+ }
+ }
+ pary.reset();
+ .free(pary);
+ }
+
+ // Called before TLS ctors are ran, copy over the loaded libraries
+ // of the parent thread.
+ void inheritLoadedLibraries(void* p) nothrow @nogc
+ {
+ safeAssert(_loadedDSOs.empty, "DSOs have already been registered for this thread.");
+ _loadedDSOs.swap(*cast(Array!(ThreadDSO)*)p);
+ .free(p);
+ foreach (ref dso; _loadedDSOs)
+ {
+ // the copied _tlsRange corresponds to parent thread
+ dso.updateTLSRange();
+ }
+ }
+
+ // Called after all TLS dtors ran, decrements all remaining dlopen refs.
+ void cleanupLoadedLibraries() nothrow @nogc
+ {
+ foreach (ref tdso; _loadedDSOs)
+ {
+ if (tdso._addCnt == 0) continue;
+
+ auto handle = tdso._pdso._handle;
+ safeAssert(handle !is null, "Invalid DSO handle.");
+ for (; tdso._addCnt > 0; --tdso._addCnt)
+ .dlclose(handle);
+ }
+
+ // Free the memory for the array contents.
+ _loadedDSOs.reset();
+ }
+}
+else
+{
+ /***
+ * Called once per thread; returns array of thread local storage ranges
+ */
+ Array!(void[])* initTLSRanges() nothrow @nogc
+ {
+ return &_tlsRanges();
+ }
+
+ void finiTLSRanges(Array!(void[])* rngs) nothrow @nogc
+ {
+ rngs.reset();
+ }
+
+ void scanTLSRanges(Array!(void[])* rngs, scope ScanDG dg) nothrow
+ {
+ foreach (rng; *rngs)
+ dg(rng.ptr, rng.ptr + rng.length);
+ }
+}
+
+private:
+
+version (Shared)
+{
+ /*
+ * Array of thread local DSO metadata for all libraries loaded and
+ * initialized in this thread.
+ *
+ * Note:
+ * A newly spawned thread will inherit these libraries.
+ * Note:
+ * We use an array here to preserve the order of
+ * initialization. If that became a performance issue, we
+ * could use a hash table and enumerate the DSOs during
+ * loading so that the hash table values could be sorted when
+ * necessary.
+ */
+ struct ThreadDSO
+ {
+ DSO* _pdso;
+ static if (_pdso.sizeof == 8) uint _refCnt, _addCnt;
+ else static if (_pdso.sizeof == 4) ushort _refCnt, _addCnt;
+ else static assert(0, "unimplemented");
+ void[] _tlsRange;
+ alias _pdso this;
+ // update the _tlsRange for the executing thread
+ void updateTLSRange() nothrow @nogc
+ {
+ _tlsRange = _pdso.tlsRange();
+ }
+ }
+ @property ref Array!(ThreadDSO) _loadedDSOs() @nogc nothrow { static Array!(ThreadDSO) x; return x; }
+
+ /*
+ * Set to true during rt_loadLibrary/rt_unloadLibrary calls.
+ */
+ bool _rtLoading;
+
+ /*
+ * Hash table to map link_map* to corresponding DSO*.
+ * The hash table is protected by a Mutex.
+ */
+ __gshared pthread_mutex_t _handleToDSOMutex;
+ @property ref HashTab!(void*, DSO*) _handleToDSO() @nogc nothrow { __gshared HashTab!(void*, DSO*) x; return x; }
+
+ /*
+ * Section in executable that contains copy relocations.
+ * Might be null when druntime is dynamically loaded by a C host.
+ */
+ __gshared const(void)[] _copyRelocSection;
+}
+else
+{
+ /*
+ * Static DSOs loaded by the runtime linker. This includes the
+ * executable. These can't be unloaded.
+ */
+ @property ref Array!(DSO*) _loadedDSOs() @nogc nothrow { __gshared Array!(DSO*) x; return x; }
+
+ /*
+ * Thread local array that contains TLS memory ranges for each
+ * library initialized in this thread.
+ */
+ @property ref Array!(void[]) _tlsRanges() @nogc nothrow { static Array!(void[]) x; return x; }
+
+ enum _rtLoading = false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Compiler to runtime interface.
+///////////////////////////////////////////////////////////////////////////////
+
+/*
+ * This data structure is generated by the compiler, and then passed to
+ * _d_dso_registry().
+ */
+struct CompilerDSOData
+{
+ size_t _version; // currently 1
+ void** _slot; // can be used to store runtime data
+ immutable(object.ModuleInfo*)* _minfo_beg, _minfo_end; // array of modules in this object file
+}
+
+T[] toRange(T)(T* beg, T* end) { return beg[0 .. end - beg]; }
+
+/* For each shared library and executable, the compiler generates code that
+ * sets up CompilerDSOData and calls _d_dso_registry().
+ * A pointer to that code is inserted into both the .ctors and .dtors
+ * segment so it gets called by the loader on startup and shutdown.
+ */
+extern(C) void _d_dso_registry(CompilerDSOData* data)
+{
+ // only one supported currently
+ safeAssert(data._version >= 1, "Incompatible compiler-generated DSO data version.");
+
+ // no backlink => register
+ if (*data._slot is null)
+ {
+ immutable firstDSO = _loadedDSOs.empty;
+ if (firstDSO) initLocks();
+
+ DSO* pdso = cast(DSO*).calloc(1, DSO.sizeof);
+ assert(typeid(DSO).initializer().ptr is null);
+ *data._slot = pdso; // store backlink in library record
+
+ pdso._moduleGroup = ModuleGroup(toRange(data._minfo_beg, data._minfo_end));
+
+ dl_phdr_info info = void;
+ const headerFound = findDSOInfoForAddr(data._slot, &info);
+ safeAssert(headerFound, "Failed to find image header.");
+
+ scanSegments(info, pdso);
+
+ version (Shared)
+ {
+ auto handle = handleForAddr(data._slot);
+
+ getDependencies(info, pdso._deps);
+ pdso._handle = handle;
+ setDSOForHandle(pdso, pdso._handle);
+
+ if (!_rtLoading)
+ {
+ /* This DSO was not loaded by rt_loadLibrary which
+ * happens for all dependencies of an executable or
+ * the first dlopen call from a C program.
+ * In this case we add the DSO to the _loadedDSOs of this
+ * thread with a refCnt of 1 and call the TlsCtors.
+ */
+ immutable ushort refCnt = 1, addCnt = 0;
+ _loadedDSOs.insertBack(ThreadDSO(pdso, refCnt, addCnt, pdso.tlsRange()));
+ }
+ }
+ else
+ {
+ foreach (p; _loadedDSOs)
+ safeAssert(p !is pdso, "DSO already registered.");
+ _loadedDSOs.insertBack(pdso);
+ _tlsRanges.insertBack(pdso.tlsRange());
+ }
+
+ // don't initialize modules before rt_init was called (see Bugzilla 11378)
+ if (_isRuntimeInitialized)
+ {
+ registerGCRanges(pdso);
+ // rt_loadLibrary will run tls ctors, so do this only for dlopen
+ immutable runTlsCtors = !_rtLoading;
+ runModuleConstructors(pdso, runTlsCtors);
+ }
+ }
+ // has backlink => unregister
+ else
+ {
+ DSO* pdso = cast(DSO*)*data._slot;
+ *data._slot = null;
+
+ // don't finalizes modules after rt_term was called (see Bugzilla 11378)
+ if (_isRuntimeInitialized)
+ {
+ // rt_unloadLibrary already ran tls dtors, so do this only for dlclose
+ immutable runTlsDtors = !_rtLoading;
+ runModuleDestructors(pdso, runTlsDtors);
+ unregisterGCRanges(pdso);
+ // run finalizers after module dtors (same order as in rt_term)
+ version (Shared) runFinalizers(pdso);
+ }
+
+ version (Shared)
+ {
+ if (!_rtLoading)
+ {
+ /* This DSO was not unloaded by rt_unloadLibrary so we
+ * have to remove it from _loadedDSOs here.
+ */
+ foreach (i, ref tdso; _loadedDSOs)
+ {
+ if (tdso._pdso == pdso)
+ {
+ _loadedDSOs.remove(i);
+ break;
+ }
+ }
+ }
+
+ unsetDSOForHandle(pdso, pdso._handle);
+ }
+ else
+ {
+ // static DSOs are unloaded in reverse order
+ safeAssert(pdso == _loadedDSOs.back, "DSO being unregistered isn't current last one.");
+ _loadedDSOs.popBack();
+ }
+
+ freeDSO(pdso);
+
+ // last DSO being unloaded => shutdown registry
+ if (_loadedDSOs.empty)
+ {
+ version (Shared)
+ {
+ safeAssert(_handleToDSO.empty, "_handleToDSO not in sync with _loadedDSOs.");
+ _handleToDSO.reset();
+ }
+ finiLocks();
+ }
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Dynamic loading
+///////////////////////////////////////////////////////////////////////////////
+
+// Shared D libraries are only supported when linking against a shared druntime library.
+
+version (Shared)
+{
+ ThreadDSO* findThreadDSO(DSO* pdso) nothrow @nogc
+ {
+ foreach (ref tdata; _loadedDSOs)
+ if (tdata._pdso == pdso) return &tdata;
+ return null;
+ }
+
+ void incThreadRef(DSO* pdso, bool incAdd)
+ {
+ if (auto tdata = findThreadDSO(pdso)) // already initialized
+ {
+ if (incAdd && ++tdata._addCnt > 1) return;
+ ++tdata._refCnt;
+ }
+ else
+ {
+ foreach (dep; pdso._deps)
+ incThreadRef(dep, false);
+ immutable ushort refCnt = 1, addCnt = incAdd ? 1 : 0;
+ _loadedDSOs.insertBack(ThreadDSO(pdso, refCnt, addCnt, pdso.tlsRange()));
+ pdso._moduleGroup.runTlsCtors();
+ }
+ }
+
+ void decThreadRef(DSO* pdso, bool decAdd)
+ {
+ auto tdata = findThreadDSO(pdso);
+ safeAssert(tdata !is null, "Failed to find thread DSO.");
+ safeAssert(!decAdd || tdata._addCnt > 0, "Mismatching rt_unloadLibrary call.");
+
+ if (decAdd && --tdata._addCnt > 0) return;
+ if (--tdata._refCnt > 0) return;
+
+ pdso._moduleGroup.runTlsDtors();
+ foreach (i, ref td; _loadedDSOs)
+ if (td._pdso == pdso) _loadedDSOs.remove(i);
+ foreach (dep; pdso._deps)
+ decThreadRef(dep, false);
+ }
+
+ extern(C) void* rt_loadLibrary(const char* name)
+ {
+ immutable save = _rtLoading;
+ _rtLoading = true;
+ scope (exit) _rtLoading = save;
+
+ auto handle = .dlopen(name, RTLD_LAZY);
+ if (handle is null) return null;
+
+ // if it's a D library
+ if (auto pdso = dsoForHandle(handle))
+ incThreadRef(pdso, true);
+ return handle;
+ }
+
+ extern(C) int rt_unloadLibrary(void* handle)
+ {
+ if (handle is null) return false;
+
+ immutable save = _rtLoading;
+ _rtLoading = true;
+ scope (exit) _rtLoading = save;
+
+ // if it's a D library
+ if (auto pdso = dsoForHandle(handle))
+ decThreadRef(pdso, true);
+ return .dlclose(handle) == 0;
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Helper functions
+///////////////////////////////////////////////////////////////////////////////
+
+void initLocks() nothrow @nogc
+{
+ version (Shared)
+ !pthread_mutex_init(&_handleToDSOMutex, null) || assert(0);
+}
+
+void finiLocks() nothrow @nogc
+{
+ version (Shared)
+ !pthread_mutex_destroy(&_handleToDSOMutex) || assert(0);
+}
+
+void runModuleConstructors(DSO* pdso, bool runTlsCtors)
+{
+ pdso._moduleGroup.sortCtors();
+ pdso._moduleGroup.runCtors();
+ if (runTlsCtors) pdso._moduleGroup.runTlsCtors();
+}
+
+void runModuleDestructors(DSO* pdso, bool runTlsDtors)
+{
+ if (runTlsDtors) pdso._moduleGroup.runTlsDtors();
+ pdso._moduleGroup.runDtors();
+}
+
+void registerGCRanges(DSO* pdso) nothrow @nogc
+{
+ foreach (rng; pdso._gcRanges)
+ GC.addRange(rng.ptr, rng.length);
+}
+
+void unregisterGCRanges(DSO* pdso) nothrow @nogc
+{
+ foreach (rng; pdso._gcRanges)
+ GC.removeRange(rng.ptr);
+}
+
+version (Shared) void runFinalizers(DSO* pdso)
+{
+ foreach (seg; pdso._codeSegments)
+ GC.runFinalizers(seg);
+}
+
+void freeDSO(DSO* pdso) nothrow @nogc
+{
+ pdso._gcRanges.reset();
+ version (Shared)
+ {
+ pdso._codeSegments.reset();
+ pdso._deps.reset();
+ pdso._handle = null;
+ }
+ .free(pdso);
+}
+
+version (Shared)
+{
+@nogc nothrow:
+ link_map* linkMapForHandle(void* handle)
+ {
+ link_map* map;
+ const success = dlinfo(handle, RTLD_DI_LINKMAP, &map) == 0;
+ safeAssert(success, "Failed to get DSO info.");
+ return map;
+ }
+
+ link_map* exeLinkMap(link_map* map)
+ {
+ safeAssert(map !is null, "Invalid link_map.");
+ while (map.l_prev !is null)
+ map = map.l_prev;
+ return map;
+ }
+
+ DSO* dsoForHandle(void* handle)
+ {
+ DSO* pdso;
+ !pthread_mutex_lock(&_handleToDSOMutex) || assert(0);
+ if (auto ppdso = handle in _handleToDSO)
+ pdso = *ppdso;
+ !pthread_mutex_unlock(&_handleToDSOMutex) || assert(0);
+ return pdso;
+ }
+
+ void setDSOForHandle(DSO* pdso, void* handle)
+ {
+ !pthread_mutex_lock(&_handleToDSOMutex) || assert(0);
+ safeAssert(handle !in _handleToDSO, "DSO already registered.");
+ _handleToDSO[handle] = pdso;
+ !pthread_mutex_unlock(&_handleToDSOMutex) || assert(0);
+ }
+
+ void unsetDSOForHandle(DSO* pdso, void* handle)
+ {
+ !pthread_mutex_lock(&_handleToDSOMutex) || assert(0);
+ safeAssert(_handleToDSO[handle] == pdso, "Handle doesn't match registered DSO.");
+ _handleToDSO.remove(handle);
+ !pthread_mutex_unlock(&_handleToDSOMutex) || assert(0);
+ }
+
+ void getDependencies(in ref dl_phdr_info info, ref Array!(DSO*) deps)
+ {
+ // get the entries of the .dynamic section
+ ElfW!"Dyn"[] dyns;
+ foreach (ref phdr; info.dlpi_phdr[0 .. info.dlpi_phnum])
+ {
+ if (phdr.p_type == PT_DYNAMIC)
+ {
+ auto p = cast(ElfW!"Dyn"*)(info.dlpi_addr + (phdr.p_vaddr & ~(size_t.sizeof - 1)));
+ dyns = p[0 .. phdr.p_memsz / ElfW!"Dyn".sizeof];
+ break;
+ }
+ }
+ // find the string table which contains the sonames
+ const(char)* strtab;
+ foreach (dyn; dyns)
+ {
+ if (dyn.d_tag == DT_STRTAB)
+ {
+ version (CRuntime_Musl)
+ strtab = cast(const(char)*)(info.dlpi_addr + dyn.d_un.d_ptr); // relocate
+ else version (linux)
+ strtab = cast(const(char)*)dyn.d_un.d_ptr;
+ else version (FreeBSD)
+ strtab = cast(const(char)*)(info.dlpi_addr + dyn.d_un.d_ptr); // relocate
+ else version (NetBSD)
+ strtab = cast(const(char)*)(info.dlpi_addr + dyn.d_un.d_ptr); // relocate
+ else version (DragonFlyBSD)
+ strtab = cast(const(char)*)(info.dlpi_addr + dyn.d_un.d_ptr); // relocate
+ else
+ static assert(0, "unimplemented");
+ break;
+ }
+ }
+ foreach (dyn; dyns)
+ {
+ immutable tag = dyn.d_tag;
+ if (!(tag == DT_NEEDED || tag == DT_AUXILIARY || tag == DT_FILTER))
+ continue;
+
+ // soname of the dependency
+ auto name = strtab + dyn.d_un.d_val;
+ // get handle without loading the library
+ auto handle = handleForName(name);
+ // the runtime linker has already loaded all dependencies
+ safeAssert(handle !is null, "Failed to get library handle.");
+ // if it's a D library
+ if (auto pdso = dsoForHandle(handle))
+ deps.insertBack(pdso); // append it to the dependencies
+ }
+ }
+
+ void* handleForName(const char* name)
+ {
+ auto handle = .dlopen(name, RTLD_NOLOAD | RTLD_LAZY);
+ if (handle !is null) .dlclose(handle); // drop reference count
+ return handle;
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Elf program header iteration
+///////////////////////////////////////////////////////////////////////////////
+
+/************
+ * Scan segments in Linux dl_phdr_info struct and store
+ * the TLS and writeable data segments in *pdso.
+ */
+void scanSegments(in ref dl_phdr_info info, DSO* pdso) nothrow @nogc
+{
+ foreach (ref phdr; info.dlpi_phdr[0 .. info.dlpi_phnum])
+ {
+ switch (phdr.p_type)
+ {
+ case PT_LOAD:
+ if (phdr.p_flags & PF_W) // writeable data segment
+ {
+ auto beg = cast(void*)(info.dlpi_addr + (phdr.p_vaddr & ~(size_t.sizeof - 1)));
+ pdso._gcRanges.insertBack(beg[0 .. phdr.p_memsz]);
+ }
+ version (Shared) if (phdr.p_flags & PF_X) // code segment
+ {
+ auto beg = cast(void*)(info.dlpi_addr + (phdr.p_vaddr & ~(size_t.sizeof - 1)));
+ pdso._codeSegments.insertBack(beg[0 .. phdr.p_memsz]);
+ }
+ break;
+
+ case PT_TLS: // TLS segment
+ safeAssert(!pdso._tlsSize, "Multiple TLS segments in image header.");
+ pdso._tlsMod = info.dlpi_tls_modid;
+ pdso._tlsSize = phdr.p_memsz;
+ break;
+
+ default:
+ break;
+ }
+ }
+}
+
+/**************************
+ * Input:
+ * result where the output is to be written; dl_phdr_info is an OS struct
+ * Returns:
+ * true if found, and *result is filled in
+ * References:
+ * http://linux.die.net/man/3/dl_iterate_phdr
+ */
+bool findDSOInfoForAddr(in void* addr, dl_phdr_info* result=null) nothrow @nogc
+{
+ version (linux) enum IterateManually = true;
+ else version (NetBSD) enum IterateManually = true;
+ else enum IterateManually = false;
+
+ static if (IterateManually)
+ {
+ static struct DG { const(void)* addr; dl_phdr_info* result; }
+
+ extern(C) int callback(dl_phdr_info* info, size_t sz, void* arg) nothrow @nogc
+ {
+ auto p = cast(DG*)arg;
+ if (findSegmentForAddr(*info, p.addr))
+ {
+ if (p.result !is null) *p.result = *info;
+ return 1; // break;
+ }
+ return 0; // continue iteration
+ }
+
+ auto dg = DG(addr, result);
+
+ /* OS function that walks through the list of an application's shared objects and
+ * calls 'callback' once for each object, until either all shared objects
+ * have been processed or 'callback' returns a nonzero value.
+ */
+ return dl_iterate_phdr(&callback, &dg) != 0;
+ }
+ else version (FreeBSD)
+ {
+ return !!_rtld_addr_phdr(addr, result);
+ }
+ else version (DragonFlyBSD)
+ {
+ return !!_rtld_addr_phdr(addr, result);
+ }
+ else
+ static assert(0, "unimplemented");
+}
+
+/*********************************
+ * Determine if 'addr' lies within shared object 'info'.
+ * If so, return true and fill in 'result' with the corresponding ELF program header.
+ */
+bool findSegmentForAddr(in ref dl_phdr_info info, in void* addr, ElfW!"Phdr"* result=null) nothrow @nogc
+{
+ if (addr < cast(void*)info.dlpi_addr) // less than base address of object means quick reject
+ return false;
+
+ foreach (ref phdr; info.dlpi_phdr[0 .. info.dlpi_phnum])
+ {
+ auto beg = cast(void*)(info.dlpi_addr + phdr.p_vaddr);
+ if (cast(size_t)(addr - beg) < phdr.p_memsz)
+ {
+ if (result !is null) *result = phdr;
+ return true;
+ }
+ }
+ return false;
+}
+
+version (linux) import core.sys.linux.errno : program_invocation_name;
+// should be in core.sys.freebsd.stdlib
+version (FreeBSD) extern(C) const(char)* getprogname() nothrow @nogc;
+version (DragonFlyBSD) extern(C) const(char)* getprogname() nothrow @nogc;
+version (NetBSD) extern(C) const(char)* getprogname() nothrow @nogc;
+
+@property const(char)* progname() nothrow @nogc
+{
+ version (linux) return program_invocation_name;
+ version (FreeBSD) return getprogname();
+ version (DragonFlyBSD) return getprogname();
+ version (NetBSD) return getprogname();
+}
+
+const(char)[] dsoName(const char* dlpi_name) nothrow @nogc
+{
+ // the main executable doesn't have a name in its dlpi_name field
+ const char* p = dlpi_name[0] != 0 ? dlpi_name : progname;
+ return p[0 .. strlen(p)];
+}
+
+/**************************
+ * Input:
+ * addr an internal address of a DSO
+ * Returns:
+ * the dlopen handle for that DSO or null if addr is not within a loaded DSO
+ */
+version (Shared) void* handleForAddr(void* addr) nothrow @nogc
+{
+ Dl_info info = void;
+ if (dladdr(addr, &info) != 0)
+ return handleForName(info.dli_fname);
+ return null;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// TLS module helper
+///////////////////////////////////////////////////////////////////////////////
+
+
+/*
+ * Returns: the TLS memory range for a given module and the calling
+ * thread or null if that module has no TLS.
+ *
+ * Note: This will cause the TLS memory to be eagerly allocated.
+ */
+struct tls_index
+{
+ version (CRuntime_Glibc)
+ {
+ // For x86_64, fields are of type uint64_t, this is important for x32
+ // where tls_index would otherwise have the wrong size.
+ // See https://sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/x86_64/dl-tls.h
+ version (X86_64)
+ {
+ ulong ti_module;
+ ulong ti_offset;
+ }
+ else
+ {
+ c_ulong ti_module;
+ c_ulong ti_offset;
+ }
+ }
+ else
+ {
+ size_t ti_module;
+ size_t ti_offset;
+ }
+}
+
+extern(C) void* __tls_get_addr(tls_index* ti) nothrow @nogc;
+
+/* The dynamic thread vector (DTV) pointers may point 0x8000 past the start of
+ * each TLS block. This is at least true for PowerPC and Mips platforms.
+ * See: https://sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/powerpc/dl-tls.h;h=f7cf6f96ebfb505abfd2f02be0ad0e833107c0cd;hb=HEAD#l34
+ * https://sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/mips/dl-tls.h;h=93a6dc050cb144b9f68b96fb3199c60f5b1fcd18;hb=HEAD#l32
+ * https://sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/riscv/dl-tls.h;h=ab2d860314de94c18812bc894ff6b3f55368f20f;hb=HEAD#l32
+ */
+version (X86)
+ enum TLS_DTV_OFFSET = 0x0;
+else version (X86_64)
+ enum TLS_DTV_OFFSET = 0x0;
+else version (ARM)
+ enum TLS_DTV_OFFSET = 0x0;
+else version (AArch64)
+ enum TLS_DTV_OFFSET = 0x0;
+else version (RISCV32)
+ enum TLS_DTV_OFFSET = 0x800;
+else version (RISCV64)
+ enum TLS_DTV_OFFSET = 0x800;
+else version (HPPA)
+ enum TLS_DTV_OFFSET = 0x0;
+else version (SPARC)
+ enum TLS_DTV_OFFSET = 0x0;
+else version (SPARC64)
+ enum TLS_DTV_OFFSET = 0x0;
+else version (PPC)
+ enum TLS_DTV_OFFSET = 0x8000;
+else version (PPC64)
+ enum TLS_DTV_OFFSET = 0x8000;
+else version (MIPS32)
+ enum TLS_DTV_OFFSET = 0x8000;
+else version (MIPS64)
+ enum TLS_DTV_OFFSET = 0x8000;
+else
+ static assert( false, "Platform not supported." );
+
+void[] getTLSRange(size_t mod, size_t sz) nothrow @nogc
+{
+ if (mod == 0)
+ return null;
+
+ // base offset
+ auto ti = tls_index(mod, 0);
+ return (__tls_get_addr(&ti)-TLS_DTV_OFFSET)[0 .. sz];
+}
diff --git a/libphobos/libdruntime/gcc/sections/osx.d b/libphobos/libdruntime/gcc/sections/osx.d
new file mode 100644
index 0000000..55caee4
--- /dev/null
+++ b/libphobos/libdruntime/gcc/sections/osx.d
@@ -0,0 +1,284 @@
+// OSX-specific support for sections.
+// Copyright (C) 2019 Free Software Foundation, Inc.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+// for more details.
+
+// Under Section 7 of GPL version 3, you are granted additional
+// permissions described in the GCC Runtime Library Exception, version
+// 3.1, as published by the Free Software Foundation.
+
+// You should have received a copy of the GNU General Public License and
+// a copy of the GCC Runtime Library Exception along with this program;
+// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
+// <http://www.gnu.org/licenses/>.
+
+module gcc.sections.osx;
+
+version (OSX):
+
+// debug = PRINTF;
+import core.stdc.stdio;
+import core.stdc.string, core.stdc.stdlib;
+import core.sys.posix.pthread;
+import core.sys.darwin.mach.dyld;
+import core.sys.darwin.mach.getsect;
+import rt.deh, rt.minfo;
+import rt.util.container.array;
+
+struct SectionGroup
+{
+ static int opApply(scope int delegate(ref SectionGroup) dg)
+ {
+ return dg(_sections);
+ }
+
+ static int opApplyReverse(scope int delegate(ref SectionGroup) dg)
+ {
+ return dg(_sections);
+ }
+
+ @property immutable(ModuleInfo*)[] modules() const nothrow @nogc
+ {
+ return _moduleGroup.modules;
+ }
+
+ @property ref inout(ModuleGroup) moduleGroup() inout nothrow @nogc
+ {
+ return _moduleGroup;
+ }
+
+ @property inout(void[])[] gcRanges() inout nothrow @nogc
+ {
+ return _gcRanges[];
+ }
+
+ @property immutable(FuncTable)[] ehTables() const nothrow @nogc
+ {
+ return _ehTables[];
+ }
+
+private:
+ immutable(FuncTable)[] _ehTables;
+ ModuleGroup _moduleGroup;
+ Array!(void[]) _gcRanges;
+ immutable(void)[][2] _tlsImage;
+}
+
+/****
+ * Boolean flag set to true while the runtime is initialized.
+ */
+__gshared bool _isRuntimeInitialized;
+
+/****
+ * Gets called on program startup just before GC is initialized.
+ */
+void initSections() nothrow @nogc
+{
+ pthread_key_create(&_tlsKey, null);
+ _dyld_register_func_for_add_image(&sections_osx_onAddImage);
+ _isRuntimeInitialized = true;
+}
+
+/***
+ * Gets called on program shutdown just after GC is terminated.
+ */
+void finiSections() nothrow @nogc
+{
+ _sections._gcRanges.reset();
+ pthread_key_delete(_tlsKey);
+ _isRuntimeInitialized = false;
+}
+
+void[]* initTLSRanges() nothrow @nogc
+{
+ return &getTLSBlock();
+}
+
+void finiTLSRanges(void[]* rng) nothrow @nogc
+{
+ .free(rng.ptr);
+ .free(rng);
+}
+
+void scanTLSRanges(void[]* rng, scope void delegate(void* pbeg, void* pend) nothrow dg) nothrow
+{
+ dg(rng.ptr, rng.ptr + rng.length);
+}
+
+// NOTE: The Mach-O object file format does not allow for thread local
+// storage declarations. So instead we roll our own by putting tls
+// into the __tls_data and the __tlscoal_nt sections.
+//
+// This function is called by the code emitted by the compiler. It
+// is expected to translate an address into the TLS static data to
+// the corresponding address in the TLS dynamic per-thread data.
+
+// NB: the compiler mangles this function as '___tls_get_addr' even though it is extern(D)
+extern(D) void* ___tls_get_addr( void* p )
+{
+ immutable off = tlsOffset(p);
+ auto tls = getTLSBlockAlloc();
+ assert(off < tls.length);
+ return tls.ptr + off;
+}
+
+private:
+
+__gshared pthread_key_t _tlsKey;
+
+size_t tlsOffset(void* p)
+in
+{
+ assert(_sections._tlsImage[0].ptr !is null ||
+ _sections._tlsImage[1].ptr !is null);
+}
+body
+{
+ // NOTE: p is an address in the TLS static data emitted by the
+ // compiler. If it isn't, something is disastrously wrong.
+ immutable off0 = cast(size_t)(p - _sections._tlsImage[0].ptr);
+ if (off0 < _sections._tlsImage[0].length)
+ {
+ return off0;
+ }
+ immutable off1 = cast(size_t)(p - _sections._tlsImage[1].ptr);
+ if (off1 < _sections._tlsImage[1].length)
+ {
+ size_t sz = (_sections._tlsImage[0].length + 15) & ~cast(size_t)15;
+ return sz + off1;
+ }
+ assert(0);
+}
+
+ref void[] getTLSBlock() nothrow @nogc
+{
+ auto pary = cast(void[]*)pthread_getspecific(_tlsKey);
+ if (pary is null)
+ {
+ pary = cast(void[]*).calloc(1, (void[]).sizeof);
+ if (pthread_setspecific(_tlsKey, pary) != 0)
+ {
+ import core.stdc.stdio;
+ perror("pthread_setspecific failed with");
+ assert(0);
+ }
+ }
+ return *pary;
+}
+
+ref void[] getTLSBlockAlloc()
+{
+ auto pary = &getTLSBlock();
+ if (!pary.length)
+ {
+ auto imgs = _sections._tlsImage;
+ immutable sz0 = (imgs[0].length + 15) & ~cast(size_t)15;
+ immutable sz2 = sz0 + imgs[1].length;
+ auto p = .malloc(sz2);
+ memcpy(p, imgs[0].ptr, imgs[0].length);
+ memcpy(p + sz0, imgs[1].ptr, imgs[1].length);
+ *pary = p[0 .. sz2];
+ }
+ return *pary;
+}
+
+__gshared SectionGroup _sections;
+
+extern (C) void sections_osx_onAddImage(in mach_header* h, intptr_t slide)
+{
+ foreach (e; dataSegs)
+ {
+ auto sect = getSection(h, slide, e.seg.ptr, e.sect.ptr);
+ if (sect != null)
+ _sections._gcRanges.insertBack((cast(void*)sect.ptr)[0 .. sect.length]);
+ }
+
+ auto minfosect = getSection(h, slide, "__DATA", "__minfodata");
+ if (minfosect != null)
+ {
+ // no support for multiple images yet
+ // take the sections from the last static image which is the executable
+ if (_isRuntimeInitialized)
+ {
+ fprintf(stderr, "Loading shared libraries isn't yet supported on OSX.\n");
+ return;
+ }
+ else if (_sections.modules.ptr !is null)
+ {
+ fprintf(stderr, "Shared libraries are not yet supported on OSX.\n");
+ }
+
+ debug(PRINTF) printf(" minfodata\n");
+ auto p = cast(immutable(ModuleInfo*)*)minfosect.ptr;
+ immutable len = minfosect.length / (*p).sizeof;
+
+ _sections._moduleGroup = ModuleGroup(p[0 .. len]);
+ }
+
+ auto ehsect = getSection(h, slide, "__DATA", "__deh_eh");
+ if (ehsect != null)
+ {
+ debug(PRINTF) printf(" deh_eh\n");
+ auto p = cast(immutable(FuncTable)*)ehsect.ptr;
+ immutable len = ehsect.length / (*p).sizeof;
+
+ _sections._ehTables = p[0 .. len];
+ }
+
+ auto tlssect = getSection(h, slide, "__DATA", "__tls_data");
+ if (tlssect != null)
+ {
+ debug(PRINTF) printf(" tls_data %p %p\n", tlssect.ptr, tlssect.ptr + tlssect.length);
+ _sections._tlsImage[0] = (cast(immutable(void)*)tlssect.ptr)[0 .. tlssect.length];
+ }
+
+ auto tlssect2 = getSection(h, slide, "__DATA", "__tlscoal_nt");
+ if (tlssect2 != null)
+ {
+ debug(PRINTF) printf(" tlscoal_nt %p %p\n", tlssect2.ptr, tlssect2.ptr + tlssect2.length);
+ _sections._tlsImage[1] = (cast(immutable(void)*)tlssect2.ptr)[0 .. tlssect2.length];
+ }
+}
+
+struct SegRef
+{
+ string seg;
+ string sect;
+}
+
+static immutable SegRef[] dataSegs = [{SEG_DATA, SECT_DATA},
+ {SEG_DATA, SECT_BSS},
+ {SEG_DATA, SECT_COMMON}];
+
+ubyte[] getSection(in mach_header* header, intptr_t slide,
+ in char* segmentName, in char* sectionName)
+{
+ version (X86)
+ {
+ assert(header.magic == MH_MAGIC);
+ auto sect = getsectbynamefromheader(header,
+ segmentName,
+ sectionName);
+ }
+ else version (X86_64)
+ {
+ assert(header.magic == MH_MAGIC_64);
+ auto sect = getsectbynamefromheader_64(cast(mach_header_64*)header,
+ segmentName,
+ sectionName);
+ }
+ else
+ static assert(0, "unimplemented");
+
+ if (sect !is null && sect.size > 0)
+ return (cast(ubyte*)sect.addr + slide)[0 .. cast(size_t)sect.size];
+ return null;
+}
diff --git a/libphobos/libdruntime/gcc/sections/package.d b/libphobos/libdruntime/gcc/sections/package.d
new file mode 100644
index 0000000..07617ea
--- /dev/null
+++ b/libphobos/libdruntime/gcc/sections/package.d
@@ -0,0 +1,48 @@
+// Run-time support for retrieving platform-specific sections.
+// Copyright (C) 2019 Free Software Foundation, Inc.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+// for more details.
+
+// Under Section 7 of GPL version 3, you are granted additional
+// permissions described in the GCC Runtime Library Exception, version
+// 3.1, as published by the Free Software Foundation.
+
+// You should have received a copy of the GNU General Public License and
+// a copy of the GCC Runtime Library Exception along with this program;
+// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
+// <http://www.gnu.org/licenses/>.
+
+module gcc.sections;
+
+version (CRuntime_Glibc)
+ public import gcc.sections.elf_shared;
+else version (CRuntime_Musl)
+ public import gcc.sections.elf_shared;
+else version (CRuntime_UClibc)
+ public import gcc.sections.elf_shared;
+else version (FreeBSD)
+ public import gcc.sections.elf_shared;
+else version (NetBSD)
+ public import gcc.sections.elf_shared;
+else version (DragonFlyBSD)
+ public import gcc.sections.elf_shared;
+else version (Solaris)
+ public import gcc.sections.solaris;
+else version (OSX)
+ public import gcc.sections.osx;
+else version (CRuntime_DigitalMars)
+ public import gcc.sections.win32;
+else version (CRuntime_Microsoft)
+ public import gcc.sections.win64;
+else version (CRuntime_Bionic)
+ public import gcc.sections.android;
+else
+ static assert(0, "unimplemented");
diff --git a/libphobos/libdruntime/gcc/sections/solaris.d b/libphobos/libdruntime/gcc/sections/solaris.d
new file mode 100644
index 0000000..e66325b
--- /dev/null
+++ b/libphobos/libdruntime/gcc/sections/solaris.d
@@ -0,0 +1,126 @@
+// Solaris-specific support for sections.
+// Copyright (C) 2019 Free Software Foundation, Inc.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+// for more details.
+
+// Under Section 7 of GPL version 3, you are granted additional
+// permissions described in the GCC Runtime Library Exception, version
+// 3.1, as published by the Free Software Foundation.
+
+// You should have received a copy of the GNU General Public License and
+// a copy of the GCC Runtime Library Exception along with this program;
+// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
+// <http://www.gnu.org/licenses/>.
+
+module gcc.sections.solaris;
+
+version (Solaris):
+
+// debug = PRINTF;
+debug(PRINTF) import core.stdc.stdio;
+import core.stdc.stdlib : malloc, free;
+import rt.deh, rt.minfo;
+
+struct SectionGroup
+{
+ static int opApply(scope int delegate(ref SectionGroup) dg)
+ {
+ return dg(_sections);
+ }
+
+ static int opApplyReverse(scope int delegate(ref SectionGroup) dg)
+ {
+ return dg(_sections);
+ }
+
+ @property immutable(ModuleInfo*)[] modules() const nothrow @nogc
+ {
+ return _moduleGroup.modules;
+ }
+
+ @property ref inout(ModuleGroup) moduleGroup() inout nothrow @nogc
+ {
+ return _moduleGroup;
+ }
+
+ @property immutable(FuncTable)[] ehTables() const nothrow @nogc
+ {
+ auto pbeg = cast(immutable(FuncTable)*)&__start_deh;
+ auto pend = cast(immutable(FuncTable)*)&__stop_deh;
+ return pbeg[0 .. pend - pbeg];
+ }
+
+ @property inout(void[])[] gcRanges() inout nothrow @nogc
+ {
+ return _gcRanges[];
+ }
+
+private:
+ ModuleGroup _moduleGroup;
+ void[][1] _gcRanges;
+}
+
+void initSections() nothrow @nogc
+{
+ auto mbeg = cast(immutable ModuleInfo**)&__start_minfo;
+ auto mend = cast(immutable ModuleInfo**)&__stop_minfo;
+ _sections.moduleGroup = ModuleGroup(mbeg[0 .. mend - mbeg]);
+
+ auto pbeg = cast(void*)&__dso_handle;
+ auto pend = cast(void*)&_end;
+ _sections._gcRanges[0] = pbeg[0 .. pend - pbeg];
+}
+
+void finiSections() nothrow @nogc
+{
+}
+
+void[] initTLSRanges() nothrow @nogc
+{
+ auto pbeg = cast(void*)&_tlsstart;
+ auto pend = cast(void*)&_tlsend;
+ return pbeg[0 .. pend - pbeg];
+}
+
+void finiTLSRanges(void[] rng) nothrow @nogc
+{
+}
+
+void scanTLSRanges(void[] rng, scope void delegate(void* pbeg, void* pend) nothrow dg) nothrow
+{
+ dg(rng.ptr, rng.ptr + rng.length);
+}
+
+private:
+
+__gshared SectionGroup _sections;
+
+extern(C)
+{
+ /* Symbols created by the compiler/linker and inserted into the
+ * object file that 'bracket' sections.
+ */
+ extern __gshared
+ {
+ void* __start_deh;
+ void* __stop_deh;
+ void* __start_minfo;
+ void* __stop_minfo;
+ int __dso_handle;
+ int _end;
+ }
+
+ extern
+ {
+ void* _tlsstart;
+ void* _tlsend;
+ }
+}
diff --git a/libphobos/libdruntime/gcc/sections/win32.d b/libphobos/libdruntime/gcc/sections/win32.d
new file mode 100644
index 0000000..4b76ca8
--- /dev/null
+++ b/libphobos/libdruntime/gcc/sections/win32.d
@@ -0,0 +1,183 @@
+// Win32-specific support for sections.
+// Copyright (C) 2019 Free Software Foundation, Inc.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+// for more details.
+
+// Under Section 7 of GPL version 3, you are granted additional
+// permissions described in the GCC Runtime Library Exception, version
+// 3.1, as published by the Free Software Foundation.
+
+// You should have received a copy of the GNU General Public License and
+// a copy of the GCC Runtime Library Exception along with this program;
+// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
+// <http://www.gnu.org/licenses/>.
+
+module gcc.sections.win32;
+
+version (CRuntime_DigitalMars):
+
+// debug = PRINTF;
+debug(PRINTF) import core.stdc.stdio;
+import rt.minfo;
+import core.stdc.stdlib : malloc, free;
+
+struct SectionGroup
+{
+ static int opApply(scope int delegate(ref SectionGroup) dg)
+ {
+ return dg(_sections);
+ }
+
+ static int opApplyReverse(scope int delegate(ref SectionGroup) dg)
+ {
+ return dg(_sections);
+ }
+
+ @property immutable(ModuleInfo*)[] modules() const nothrow @nogc
+ {
+ return _moduleGroup.modules;
+ }
+
+ @property ref inout(ModuleGroup) moduleGroup() inout nothrow @nogc
+ {
+ return _moduleGroup;
+ }
+
+ @property inout(void[])[] gcRanges() inout nothrow @nogc
+ {
+ return _gcRanges[];
+ }
+
+private:
+ ModuleGroup _moduleGroup;
+ void[][] _gcRanges;
+}
+
+shared(bool) conservative;
+
+void initSections() nothrow @nogc
+{
+ _sections._moduleGroup = ModuleGroup(getModuleInfos());
+
+ import rt.sections;
+ conservative = !scanDataSegPrecisely();
+
+ if (conservative)
+ {
+ _sections._gcRanges = (cast(void[]*) malloc(2 * (void[]).sizeof))[0..2];
+
+ auto databeg = cast(void*)&_xi_a;
+ auto dataend = cast(void*)_moduleinfo_array.ptr;
+ _sections._gcRanges[0] = databeg[0 .. dataend - databeg];
+
+ // skip module info and CONST segment
+ auto bssbeg = cast(void*)&_edata;
+ auto bssend = cast(void*)&_end;
+ _sections._gcRanges[1] = bssbeg[0 .. bssend - bssbeg];
+ }
+ else
+ {
+ size_t count = &_DPend - &_DPbegin;
+ auto ranges = cast(void[]*) malloc(count * (void[]).sizeof);
+ size_t r = 0;
+ void* prev = null;
+ for (size_t i = 0; i < count; i++)
+ {
+ void* addr = (&_DPbegin)[i];
+ if (prev + (void*).sizeof == addr)
+ ranges[r-1] = ranges[r-1].ptr[0 .. ranges[r-1].length + (void*).sizeof];
+ else
+ ranges[r++] = (cast(void**)addr)[0..1];
+ prev = addr;
+ }
+ _sections._gcRanges = ranges[0..r];
+ }
+}
+
+void finiSections() nothrow @nogc
+{
+ free(_sections._gcRanges.ptr);
+}
+
+void[] initTLSRanges() nothrow @nogc
+{
+ auto pbeg = cast(void*)&_tlsstart;
+ auto pend = cast(void*)&_tlsend;
+ return pbeg[0 .. pend - pbeg];
+}
+
+void finiTLSRanges(void[] rng) nothrow @nogc
+{
+}
+
+void scanTLSRanges(void[] rng, scope void delegate(void* pbeg, void* pend) nothrow dg) nothrow
+{
+ if (conservative)
+ {
+ dg(rng.ptr, rng.ptr + rng.length);
+ }
+ else
+ {
+ for (auto p = &_TPbegin; p < &_TPend; )
+ {
+ uint beg = *p++;
+ uint end = beg + cast(uint)((void*).sizeof);
+ while (p < &_TPend && *p == end)
+ {
+ end += (void*).sizeof;
+ p++;
+ }
+ dg(rng.ptr + beg, rng.ptr + end);
+ }
+ }
+}
+
+private:
+
+__gshared SectionGroup _sections;
+
+// Windows: this gets initialized by minit.asm
+extern(C) __gshared immutable(ModuleInfo*)[] _moduleinfo_array;
+extern(C) void _minit() nothrow @nogc;
+
+immutable(ModuleInfo*)[] getModuleInfos() nothrow @nogc
+out (result)
+{
+ foreach (m; result)
+ assert(m !is null);
+}
+body
+{
+ // _minit directly alters the global _moduleinfo_array
+ _minit();
+ return _moduleinfo_array;
+}
+
+extern(C)
+{
+ extern __gshared
+ {
+ int _xi_a; // &_xi_a just happens to be start of data segment
+ int _edata; // &_edata is start of BSS segment
+ int _end; // &_end is past end of BSS
+
+ void* _DPbegin; // first entry in the array of pointers addresses
+ void* _DPend; // &_DPend points after last entry of array
+ uint _TPbegin; // first entry in the array of TLS offsets of pointers
+ uint _TPend; // &_DPend points after last entry of array
+ }
+
+ extern
+ {
+ int _tlsstart;
+ int _tlsend;
+ }
+}
diff --git a/libphobos/libdruntime/gcc/sections/win64.d b/libphobos/libdruntime/gcc/sections/win64.d
new file mode 100644
index 0000000..b98c352
--- /dev/null
+++ b/libphobos/libdruntime/gcc/sections/win64.d
@@ -0,0 +1,321 @@
+// Win64-specific support for sections.
+// Copyright (C) 2019 Free Software Foundation, Inc.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+// for more details.
+
+// Under Section 7 of GPL version 3, you are granted additional
+// permissions described in the GCC Runtime Library Exception, version
+// 3.1, as published by the Free Software Foundation.
+
+// You should have received a copy of the GNU General Public License and
+// a copy of the GCC Runtime Library Exception along with this program;
+// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
+// <http://www.gnu.org/licenses/>.
+
+module gcc.sections.win64;
+
+version (CRuntime_Microsoft):
+
+// debug = PRINTF;
+debug(PRINTF) import core.stdc.stdio;
+import core.stdc.stdlib : malloc, free;
+import rt.deh, rt.minfo;
+
+struct SectionGroup
+{
+ static int opApply(scope int delegate(ref SectionGroup) dg)
+ {
+ return dg(_sections);
+ }
+
+ static int opApplyReverse(scope int delegate(ref SectionGroup) dg)
+ {
+ return dg(_sections);
+ }
+
+ @property immutable(ModuleInfo*)[] modules() const nothrow @nogc
+ {
+ return _moduleGroup.modules;
+ }
+
+ @property ref inout(ModuleGroup) moduleGroup() inout nothrow @nogc
+ {
+ return _moduleGroup;
+ }
+
+ version (Win64)
+ @property immutable(FuncTable)[] ehTables() const nothrow @nogc
+ {
+ auto pbeg = cast(immutable(FuncTable)*)&_deh_beg;
+ auto pend = cast(immutable(FuncTable)*)&_deh_end;
+ return pbeg[0 .. pend - pbeg];
+ }
+
+ @property inout(void[])[] gcRanges() inout nothrow @nogc
+ {
+ return _gcRanges[];
+ }
+
+private:
+ ModuleGroup _moduleGroup;
+ void[][] _gcRanges;
+}
+
+shared(bool) conservative;
+
+void initSections() nothrow @nogc
+{
+ _sections._moduleGroup = ModuleGroup(getModuleInfos());
+
+ // the ".data" image section includes both object file sections ".data" and ".bss"
+ void[] dataSection = findImageSection(".data");
+ debug(PRINTF) printf("found .data section: [%p,+%llx]\n", dataSection.ptr,
+ cast(ulong)dataSection.length);
+
+ import rt.sections;
+ conservative = !scanDataSegPrecisely();
+
+ if (conservative)
+ {
+ _sections._gcRanges = (cast(void[]*) malloc((void[]).sizeof))[0..1];
+ _sections._gcRanges[0] = dataSection;
+ }
+ else
+ {
+ size_t count = &_DP_end - &_DP_beg;
+ auto ranges = cast(void[]*) malloc(count * (void[]).sizeof);
+ size_t r = 0;
+ void* prev = null;
+ for (size_t i = 0; i < count; i++)
+ {
+ auto off = (&_DP_beg)[i];
+ if (off == 0) // skip zero entries added by incremental linking
+ continue; // assumes there is no D-pointer at the very beginning of .data
+ void* addr = dataSection.ptr + off;
+ debug(PRINTF) printf(" scan %p\n", addr);
+ // combine consecutive pointers into single range
+ if (prev + (void*).sizeof == addr)
+ ranges[r-1] = ranges[r-1].ptr[0 .. ranges[r-1].length + (void*).sizeof];
+ else
+ ranges[r++] = (cast(void**)addr)[0..1];
+ prev = addr;
+ }
+ _sections._gcRanges = ranges[0..r];
+ }
+}
+
+void finiSections() nothrow @nogc
+{
+ .free(cast(void*)_sections.modules.ptr);
+ .free(_sections._gcRanges.ptr);
+}
+
+void[] initTLSRanges() nothrow @nogc
+{
+ void* pbeg;
+ void* pend;
+ // with VS2017 15.3.1, the linker no longer puts TLS segments into a
+ // separate image section. That way _tls_start and _tls_end no
+ // longer generate offsets into .tls, but DATA.
+ // Use the TEB entry to find the start of TLS instead and read the
+ // length from the TLS directory
+ version (D_InlineAsm_X86)
+ {
+ asm @nogc nothrow
+ {
+ mov EAX, _tls_index;
+ mov ECX, FS:[0x2C]; // _tls_array
+ mov EAX, [ECX+4*EAX];
+ mov pbeg, EAX;
+ add EAX, [_tls_used+4]; // end
+ sub EAX, [_tls_used+0]; // start
+ mov pend, EAX;
+ }
+ }
+ else version (D_InlineAsm_X86_64)
+ {
+ asm @nogc nothrow
+ {
+ xor RAX, RAX;
+ mov EAX, _tls_index;
+ mov RCX, 0x58;
+ mov RCX, GS:[RCX]; // _tls_array (immediate value causes fixup)
+ mov RAX, [RCX+8*RAX];
+ mov pbeg, RAX;
+ add RAX, [_tls_used+8]; // end
+ sub RAX, [_tls_used+0]; // start
+ mov pend, RAX;
+ }
+ }
+ else
+ static assert(false, "Architecture not supported.");
+
+ return pbeg[0 .. pend - pbeg];
+}
+
+void finiTLSRanges(void[] rng) nothrow @nogc
+{
+}
+
+void scanTLSRanges(void[] rng, scope void delegate(void* pbeg, void* pend) nothrow dg) nothrow
+{
+ if (conservative)
+ {
+ dg(rng.ptr, rng.ptr + rng.length);
+ }
+ else
+ {
+ for (auto p = &_TP_beg; p < &_TP_end; )
+ {
+ uint beg = *p++;
+ uint end = beg + cast(uint)((void*).sizeof);
+ while (p < &_TP_end && *p == end)
+ {
+ end += (void*).sizeof;
+ p++;
+ }
+ dg(rng.ptr + beg, rng.ptr + end);
+ }
+ }
+}
+
+private:
+__gshared SectionGroup _sections;
+
+extern(C)
+{
+ extern __gshared void* _minfo_beg;
+ extern __gshared void* _minfo_end;
+}
+
+immutable(ModuleInfo*)[] getModuleInfos() nothrow @nogc
+out (result)
+{
+ foreach (m; result)
+ assert(m !is null);
+}
+body
+{
+ auto m = (cast(immutable(ModuleInfo*)*)&_minfo_beg)[1 .. &_minfo_end - &_minfo_beg];
+ /* Because of alignment inserted by the linker, various null pointers
+ * are there. We need to filter them out.
+ */
+ auto p = m.ptr;
+ auto pend = m.ptr + m.length;
+
+ // count non-null pointers
+ size_t cnt;
+ for (; p < pend; ++p)
+ {
+ if (*p !is null) ++cnt;
+ }
+
+ auto result = (cast(immutable(ModuleInfo)**).malloc(cnt * size_t.sizeof))[0 .. cnt];
+
+ p = m.ptr;
+ cnt = 0;
+ for (; p < pend; ++p)
+ if (*p !is null) result[cnt++] = *p;
+
+ return cast(immutable)result;
+}
+
+extern(C)
+{
+ /* Symbols created by the compiler/linker and inserted into the
+ * object file that 'bracket' sections.
+ */
+ extern __gshared
+ {
+ void* __ImageBase;
+
+ void* _deh_beg;
+ void* _deh_end;
+
+ uint _DP_beg;
+ uint _DP_end;
+ uint _TP_beg;
+ uint _TP_end;
+
+ void*[2] _tls_used; // start, end
+ int _tls_index;
+ }
+}
+
+/////////////////////////////////////////////////////////////////////
+
+enum IMAGE_DOS_SIGNATURE = 0x5A4D; // MZ
+
+struct IMAGE_DOS_HEADER // DOS .EXE header
+{
+ ushort e_magic; // Magic number
+ ushort[29] e_res2; // Reserved ushorts
+ int e_lfanew; // File address of new exe header
+}
+
+struct IMAGE_FILE_HEADER
+{
+ ushort Machine;
+ ushort NumberOfSections;
+ uint TimeDateStamp;
+ uint PointerToSymbolTable;
+ uint NumberOfSymbols;
+ ushort SizeOfOptionalHeader;
+ ushort Characteristics;
+}
+
+struct IMAGE_NT_HEADERS
+{
+ uint Signature;
+ IMAGE_FILE_HEADER FileHeader;
+ // optional header follows
+}
+
+struct IMAGE_SECTION_HEADER
+{
+ char[8] Name = 0;
+ union {
+ uint PhysicalAddress;
+ uint VirtualSize;
+ }
+ uint VirtualAddress;
+ uint SizeOfRawData;
+ uint PointerToRawData;
+ uint PointerToRelocations;
+ uint PointerToLinenumbers;
+ ushort NumberOfRelocations;
+ ushort NumberOfLinenumbers;
+ uint Characteristics;
+}
+
+bool compareSectionName(ref IMAGE_SECTION_HEADER section, string name) nothrow @nogc
+{
+ if (name[] != section.Name[0 .. name.length])
+ return false;
+ return name.length == 8 || section.Name[name.length] == 0;
+}
+
+void[] findImageSection(string name) nothrow @nogc
+{
+ if (name.length > 8) // section name from string table not supported
+ return null;
+ IMAGE_DOS_HEADER* doshdr = cast(IMAGE_DOS_HEADER*) &__ImageBase;
+ if (doshdr.e_magic != IMAGE_DOS_SIGNATURE)
+ return null;
+
+ auto nthdr = cast(IMAGE_NT_HEADERS*)(cast(void*)doshdr + doshdr.e_lfanew);
+ auto sections = cast(IMAGE_SECTION_HEADER*)(cast(void*)nthdr + IMAGE_NT_HEADERS.sizeof + nthdr.FileHeader.SizeOfOptionalHeader);
+ for (ushort i = 0; i < nthdr.FileHeader.NumberOfSections; i++)
+ if (compareSectionName (sections[i], name))
+ return (cast(void*)&__ImageBase + sections[i].VirtualAddress)[0 .. sections[i].VirtualSize];
+
+ return null;
+}