public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [committed] libphobos: Add section support code for MACHO and PE/COFF
@ 2021-04-10 13:10 Iain Buclaw
  0 siblings, 0 replies; only message in thread
From: Iain Buclaw @ 2021-04-10 13:10 UTC (permalink / raw)
  To: gcc-patches

Hi,

This replaces the original and untested support for Windows and OSX, and
is the 90% of the work needed to support libphobos on those targets.

The core.thread interface has been updated to accomodate for the same
function might be implemented by any of the platform-dependent modules.

Bootstrapped and regression tested on x86_64-linux-gnu/-m32/-mx32, as
well as been tested on preliminary ports of *-*-darwin and
x86_64-w64-mingw64.  Committed to mainline.

Regards
Iain

---
libphobos/ChangeLog:

	* libdruntime/Makefile.am (DRUNTIME_DSOURCES): Removed
	gcc/sections/android.d, elf_shared.d, osx.d, win32.d, and win64.d.
	Added gcc/sections/common.d, elf.d macho.d, and pecoff.d.
	* libdruntime/Makefile.in: Regenerate.
	* libdruntime/core/thread/osthread.d: Update externDFunc FQDN names to
	use platform independant section function names.
	* libdruntime/gcc/sections/elf_shared.d: Renamed to...
	* libdruntime/gcc/sections/elf.d: ...this.  Mangle functions for
	core.thread interface as if they come from the gcc.sections module.
	* libdruntime/gcc/sections/package.d: Update public imports, declare
	functions for core.thread interface.
	* libdruntime/gcc/sections/android.d: Removed.
	* libdruntime/gcc/sections/osx.d: Removed.
	* libdruntime/gcc/sections/win32.d: Removed.
	* libdruntime/gcc/sections/win64.d: Removed.
	* libdruntime/gcc/sections/common.d: New file.
	* libdruntime/gcc/sections/macho.d: New file.
	* libdruntime/gcc/sections/pecoff.d: New file.
---
 libphobos/libdruntime/Makefile.am             |  23 +-
 libphobos/libdruntime/Makefile.in             |  39 +-
 libphobos/libdruntime/core/thread/osthread.d  |  10 +-
 libphobos/libdruntime/gcc/sections/android.d  | 184 ----
 libphobos/libdruntime/gcc/sections/common.d   |  39 +
 .../gcc/sections/{elf_shared.d => elf.d}      |  71 +-
 libphobos/libdruntime/gcc/sections/macho.d    | 738 ++++++++++++++++
 libphobos/libdruntime/gcc/sections/osx.d      | 284 ------
 libphobos/libdruntime/gcc/sections/package.d  |  47 +-
 libphobos/libdruntime/gcc/sections/pecoff.d   | 826 ++++++++++++++++++
 libphobos/libdruntime/gcc/sections/win32.d    | 183 ----
 libphobos/libdruntime/gcc/sections/win64.d    | 321 -------
 12 files changed, 1673 insertions(+), 1092 deletions(-)
 delete mode 100644 libphobos/libdruntime/gcc/sections/android.d
 create mode 100644 libphobos/libdruntime/gcc/sections/common.d
 rename libphobos/libdruntime/gcc/sections/{elf_shared.d => elf.d} (93%)
 create mode 100644 libphobos/libdruntime/gcc/sections/macho.d
 delete mode 100644 libphobos/libdruntime/gcc/sections/osx.d
 create mode 100644 libphobos/libdruntime/gcc/sections/pecoff.d
 delete mode 100644 libphobos/libdruntime/gcc/sections/win32.d
 delete mode 100644 libphobos/libdruntime/gcc/sections/win64.d

diff --git a/libphobos/libdruntime/Makefile.am b/libphobos/libdruntime/Makefile.am
index 5137f85b571..74a0f006036 100644
--- a/libphobos/libdruntime/Makefile.am
+++ b/libphobos/libdruntime/Makefile.am
@@ -186,18 +186,17 @@ DRUNTIME_DSOURCES = core/atomic.d core/attribute.d core/bitop.d \
 	gc/gcinterface.d gc/impl/conservative/gc.d gc/impl/manual/gc.d gc/os.d \
 	gc/pooltable.d gc/proxy.d gcc/attribute.d gcc/attributes.d \
 	gcc/backtrace.d gcc/builtins.d gcc/deh.d gcc/emutls.d gcc/gthread.d \
-	gcc/sections/android.d gcc/sections/elf_shared.d gcc/sections/osx.d \
-	gcc/sections/package.d gcc/sections/win32.d gcc/sections/win64.d \
-	gcc/unwind/arm.d gcc/unwind/arm_common.d gcc/unwind/c6x.d \
-	gcc/unwind/generic.d gcc/unwind/package.d gcc/unwind/pe.d object.d \
-	rt/aApply.d rt/aApplyR.d rt/aaA.d rt/adi.d rt/arrayassign.d \
-	rt/arraycast.d rt/arraycat.d rt/cast_.d rt/config.d rt/critical_.d \
-	rt/deh.d rt/dmain2.d rt/invariant.d rt/lifetime.d rt/memory.d \
-	rt/minfo.d rt/monitor_.d rt/obj.d rt/qsort.d rt/sections.d \
-	rt/switch_.d rt/tlsgc.d rt/util/array.d rt/util/container/array.d \
-	rt/util/container/common.d rt/util/container/hashtab.d \
-	rt/util/container/treap.d rt/util/random.d rt/util/typeinfo.d \
-	rt/util/utf.d
+	gcc/sections/common.d gcc/sections/elf.d gcc/sections/macho.d \
+	gcc/sections/package.d gcc/sections/pecoff.d gcc/unwind/arm.d \
+	gcc/unwind/arm_common.d gcc/unwind/c6x.d gcc/unwind/generic.d \
+	gcc/unwind/package.d gcc/unwind/pe.d object.d rt/aApply.d rt/aApplyR.d \
+	rt/aaA.d rt/adi.d rt/arrayassign.d rt/arraycast.d rt/arraycat.d \
+	rt/cast_.d rt/config.d rt/critical_.d rt/deh.d rt/dmain2.d \
+	rt/invariant.d rt/lifetime.d rt/memory.d rt/minfo.d rt/monitor_.d \
+	rt/obj.d rt/qsort.d rt/sections.d rt/switch_.d rt/tlsgc.d \
+	rt/util/array.d rt/util/container/array.d rt/util/container/common.d \
+	rt/util/container/hashtab.d rt/util/container/treap.d rt/util/random.d \
+	rt/util/typeinfo.d rt/util/utf.d
 
 DRUNTIME_DSOURCES_STDCXX = core/stdcpp/exception.d \
 	core/stdcpp/typeinfo.d
diff --git a/libphobos/libdruntime/Makefile.in b/libphobos/libdruntime/Makefile.in
index 4a93b4921a4..63b2133e69c 100644
--- a/libphobos/libdruntime/Makefile.in
+++ b/libphobos/libdruntime/Makefile.in
@@ -211,10 +211,9 @@ am__objects_1 = core/atomic.lo core/attribute.lo core/bitop.lo \
 	gc/impl/conservative/gc.lo gc/impl/manual/gc.lo gc/os.lo \
 	gc/pooltable.lo gc/proxy.lo gcc/attribute.lo gcc/attributes.lo \
 	gcc/backtrace.lo gcc/builtins.lo gcc/deh.lo gcc/emutls.lo \
-	gcc/gthread.lo gcc/sections/android.lo \
-	gcc/sections/elf_shared.lo gcc/sections/osx.lo \
-	gcc/sections/package.lo gcc/sections/win32.lo \
-	gcc/sections/win64.lo gcc/unwind/arm.lo \
+	gcc/gthread.lo gcc/sections/common.lo gcc/sections/elf.lo \
+	gcc/sections/macho.lo gcc/sections/package.lo \
+	gcc/sections/pecoff.lo gcc/unwind/arm.lo \
 	gcc/unwind/arm_common.lo gcc/unwind/c6x.lo \
 	gcc/unwind/generic.lo gcc/unwind/package.lo gcc/unwind/pe.lo \
 	object.lo rt/aApply.lo rt/aApplyR.lo rt/aaA.lo rt/adi.lo \
@@ -819,18 +818,17 @@ DRUNTIME_DSOURCES = core/atomic.d core/attribute.d core/bitop.d \
 	gc/gcinterface.d gc/impl/conservative/gc.d gc/impl/manual/gc.d gc/os.d \
 	gc/pooltable.d gc/proxy.d gcc/attribute.d gcc/attributes.d \
 	gcc/backtrace.d gcc/builtins.d gcc/deh.d gcc/emutls.d gcc/gthread.d \
-	gcc/sections/android.d gcc/sections/elf_shared.d gcc/sections/osx.d \
-	gcc/sections/package.d gcc/sections/win32.d gcc/sections/win64.d \
-	gcc/unwind/arm.d gcc/unwind/arm_common.d gcc/unwind/c6x.d \
-	gcc/unwind/generic.d gcc/unwind/package.d gcc/unwind/pe.d object.d \
-	rt/aApply.d rt/aApplyR.d rt/aaA.d rt/adi.d rt/arrayassign.d \
-	rt/arraycast.d rt/arraycat.d rt/cast_.d rt/config.d rt/critical_.d \
-	rt/deh.d rt/dmain2.d rt/invariant.d rt/lifetime.d rt/memory.d \
-	rt/minfo.d rt/monitor_.d rt/obj.d rt/qsort.d rt/sections.d \
-	rt/switch_.d rt/tlsgc.d rt/util/array.d rt/util/container/array.d \
-	rt/util/container/common.d rt/util/container/hashtab.d \
-	rt/util/container/treap.d rt/util/random.d rt/util/typeinfo.d \
-	rt/util/utf.d
+	gcc/sections/common.d gcc/sections/elf.d gcc/sections/macho.d \
+	gcc/sections/package.d gcc/sections/pecoff.d gcc/unwind/arm.d \
+	gcc/unwind/arm_common.d gcc/unwind/c6x.d gcc/unwind/generic.d \
+	gcc/unwind/package.d gcc/unwind/pe.d object.d rt/aApply.d rt/aApplyR.d \
+	rt/aaA.d rt/adi.d rt/arrayassign.d rt/arraycast.d rt/arraycat.d \
+	rt/cast_.d rt/config.d rt/critical_.d rt/deh.d rt/dmain2.d \
+	rt/invariant.d rt/lifetime.d rt/memory.d rt/minfo.d rt/monitor_.d \
+	rt/obj.d rt/qsort.d rt/sections.d rt/switch_.d rt/tlsgc.d \
+	rt/util/array.d rt/util/container/array.d rt/util/container/common.d \
+	rt/util/container/hashtab.d rt/util/container/treap.d rt/util/random.d \
+	rt/util/typeinfo.d rt/util/utf.d
 
 DRUNTIME_DSOURCES_STDCXX = core/stdcpp/exception.d \
 	core/stdcpp/typeinfo.d
@@ -1219,12 +1217,11 @@ gcc/gthread.lo: gcc/$(am__dirstamp)
 gcc/sections/$(am__dirstamp):
 	@$(MKDIR_P) gcc/sections
 	@: > gcc/sections/$(am__dirstamp)
-gcc/sections/android.lo: gcc/sections/$(am__dirstamp)
-gcc/sections/elf_shared.lo: gcc/sections/$(am__dirstamp)
-gcc/sections/osx.lo: gcc/sections/$(am__dirstamp)
+gcc/sections/common.lo: gcc/sections/$(am__dirstamp)
+gcc/sections/elf.lo: gcc/sections/$(am__dirstamp)
+gcc/sections/macho.lo: gcc/sections/$(am__dirstamp)
 gcc/sections/package.lo: gcc/sections/$(am__dirstamp)
-gcc/sections/win32.lo: gcc/sections/$(am__dirstamp)
-gcc/sections/win64.lo: gcc/sections/$(am__dirstamp)
+gcc/sections/pecoff.lo: gcc/sections/$(am__dirstamp)
 gcc/unwind/$(am__dirstamp):
 	@$(MKDIR_P) gcc/unwind
 	@: > gcc/unwind/$(am__dirstamp)
diff --git a/libphobos/libdruntime/core/thread/osthread.d b/libphobos/libdruntime/core/thread/osthread.d
index defdc9586f1..880836e3a25 100644
--- a/libphobos/libdruntime/core/thread/osthread.d
+++ b/libphobos/libdruntime/core/thread/osthread.d
@@ -510,7 +510,7 @@ class Thread : ThreadBase
                 {
                     version (GNU)
                     {
-                        auto libs = externDFunc!("gcc.sections.elf_shared.pinLoadedLibraries",
+                        auto libs = externDFunc!("gcc.sections.pinLoadedLibraries",
                                                  void* function() @nogc nothrow)();
                     }
                     else
@@ -527,7 +527,7 @@ class Thread : ThreadBase
                     {
                         version (GNU)
                         {
-                            externDFunc!("gcc.sections.elf_shared.unpinLoadedLibraries",
+                            externDFunc!("gcc.sections.unpinLoadedLibraries",
                                          void function(void*) @nogc nothrow)(libs);
                         }
                         else
@@ -2196,7 +2196,7 @@ else version (Posix)
             // before initilizing GC for TLS (rt_tlsgc_init)
             version (GNUShared)
             {
-                externDFunc!("gcc.sections.elf_shared.inheritLoadedLibraries",
+                externDFunc!("gcc.sections.inheritLoadedLibraries",
                              void function(void*) @nogc nothrow)(loadedLibraries);
             }
             else version (Shared)
@@ -2287,7 +2287,7 @@ else version (Posix)
                 rt_moduleTlsDtor();
                 version (GNUShared)
                 {
-                    externDFunc!("gcc.sections.elf_shared.cleanupLoadedLibraries",
+                    externDFunc!("gcc.sections.cleanupLoadedLibraries",
                                  void function() @nogc nothrow)();
                 }
                 else version (Shared)
@@ -2786,7 +2786,7 @@ private size_t adjustStackSize(size_t sz) nothrow @nogc
         // On glibc, TLS uses the top of the stack, so add its size to the requested size
         version (GNU)
         {
-            sz += externDFunc!("gcc.sections.elf_shared.sizeOfTLS",
+            sz += externDFunc!("gcc.sections.elf.sizeOfTLS",
                                size_t function() @nogc nothrow)();
         }
         else
diff --git a/libphobos/libdruntime/gcc/sections/android.d b/libphobos/libdruntime/gcc/sections/android.d
deleted file mode 100644
index 4af26b42ffc..00000000000
--- a/libphobos/libdruntime/gcc/sections/android.d
+++ /dev/null
@@ -1,184 +0,0 @@
-// Bionic-specific support for sections.
-// Copyright (C) 2019-2021 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/common.d b/libphobos/libdruntime/gcc/sections/common.d
new file mode 100644
index 00000000000..85fdc0efd48
--- /dev/null
+++ b/libphobos/libdruntime/gcc/sections/common.d
@@ -0,0 +1,39 @@
+// Contains various utility functions used by the runtime implementation.
+// Copyright (C) 2019-2021 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.common;
+
+/**
+ * Asserts that the given condition is `true`.
+ *
+ * The assertion is independent from -release, by abort()ing. Regular assertions
+ * throw an AssertError and thus require an initialized GC, which might not be
+ * the case (yet or anymore) for the startup/shutdown code in this package
+ * (called by CRT ctors/dtors etc.).
+ */
+package(gcc) void safeAssert(
+    bool condition, scope string msg, scope string file = __FILE__, size_t line = __LINE__
+) nothrow @nogc @safe
+{
+    import core.internal.abort;
+    condition || abort(msg, file, line);
+}
diff --git a/libphobos/libdruntime/gcc/sections/elf_shared.d b/libphobos/libdruntime/gcc/sections/elf.d
similarity index 93%
rename from libphobos/libdruntime/gcc/sections/elf_shared.d
rename to libphobos/libdruntime/gcc/sections/elf.d
index 5b0fad9543b..8450aecb239 100644
--- a/libphobos/libdruntime/gcc/sections/elf_shared.d
+++ b/libphobos/libdruntime/gcc/sections/elf.d
@@ -20,7 +20,7 @@
 // see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
 // <http://www.gnu.org/licenses/>.
 
-module gcc.sections.elf_shared;
+module gcc.sections.elf;
 
 version (MIPS32)  version = MIPS_Any;
 version (MIPS64)  version = MIPS_Any;
@@ -39,7 +39,6 @@ else version (Solaris) enum SharedELF = true;
 else enum SharedELF = false;
 static if (SharedELF):
 
-// debug = PRINTF;
 import core.memory;
 import core.stdc.config;
 import core.stdc.stdio;
@@ -81,25 +80,14 @@ else
     static assert(0, "unimplemented");
 }
 import core.sys.posix.pthread;
-import gcc.builtins;
-import gcc.config;
 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);
-}
+import gcc.builtins;
+import gcc.config;
+import gcc.sections.common;
 
 alias DSO SectionGroup;
 struct DSO
@@ -134,11 +122,6 @@ struct DSO
         return _moduleGroup;
     }
 
-    @property immutable(FuncTable)[] ehTables() const nothrow @nogc
-    {
-        return null;
-    }
-
     @property inout(void[])[] gcRanges() inout nothrow @nogc
     {
         return _gcRanges[];
@@ -177,22 +160,12 @@ private:
 __gshared bool _isRuntimeInitialized;
 
 
-version (FreeBSD) private __gshared void* dummy_ref;
-version (DragonFlyBSD) private __gshared void* dummy_ref;
-version (NetBSD) private __gshared void* dummy_ref;
-version (Solaris) 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;
-    version (Solaris) dummy_ref = &_d_dso_registry;
 }
 
 
@@ -208,6 +181,9 @@ alias ScanDG = void delegate(void* pbeg, void* pend) nothrow;
 
 version (Shared)
 {
+    import gcc.sections : pinLoadedLibraries, unpinLoadedLibraries,
+           inheritLoadedLibraries, cleanupLoadedLibraries;
+
     /***
      * Called once per thread; returns array of thread local storage ranges
      */
@@ -248,6 +224,7 @@ version (Shared)
     }
 
     // interface for core.thread to inherit loaded libraries
+    pragma(mangle, gcc.sections.pinLoadedLibraries.mangleof)
     void* pinLoadedLibraries() nothrow @nogc
     {
         auto res = cast(Array!(ThreadDSO)*)calloc(1, Array!(ThreadDSO).sizeof);
@@ -266,6 +243,7 @@ version (Shared)
         return res;
     }
 
+    pragma(mangle, gcc.sections.unpinLoadedLibraries.mangleof)
     void unpinLoadedLibraries(void* p) nothrow @nogc
     {
         auto pary = cast(Array!(ThreadDSO)*)p;
@@ -285,6 +263,7 @@ version (Shared)
 
     // Called before TLS ctors are ran, copy over the loaded libraries
     // of the parent thread.
+    pragma(mangle, gcc.sections.inheritLoadedLibraries.mangleof)
     void inheritLoadedLibraries(void* p) nothrow @nogc
     {
         safeAssert(_loadedDSOs.empty, "DSOs have already been registered for this thread.");
@@ -298,6 +277,7 @@ version (Shared)
     }
 
     // Called after all TLS dtors ran, decrements all remaining dlopen refs.
+    pragma(mangle, gcc.sections.cleanupLoadedLibraries.mangleof)
     void cleanupLoadedLibraries() nothrow @nogc
     {
         foreach (ref tdso; _loadedDSOs)
@@ -403,12 +383,6 @@ version (Shared)
      */
     __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
 {
@@ -976,29 +950,6 @@ bool findSegmentForAddr(in ref dl_phdr_info info, in void* addr, ElfW!"Phdr"* re
     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;
-version (Solaris) 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();
-    version (Solaris) 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
diff --git a/libphobos/libdruntime/gcc/sections/macho.d b/libphobos/libdruntime/gcc/sections/macho.d
new file mode 100644
index 00000000000..3ce58a533c3
--- /dev/null
+++ b/libphobos/libdruntime/gcc/sections/macho.d
@@ -0,0 +1,738 @@
+// MACHO-specific support for sections.
+// Copyright (C) 2021 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.macho;
+
+version (OSX):
+
+import core.memory;
+import core.stdc.stdlib;
+import core.sys.darwin.dlfcn;
+import core.sys.darwin.mach.dyld;
+import core.sys.darwin.mach.getsect;
+import core.sys.posix.pthread;
+import rt.minfo;
+import rt.util.container.array;
+import rt.util.container.hashtab;
+import gcc.sections.common;
+
+version (GNU_EMUTLS)
+    import gcc.emutls;
+
+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 inout(void[])[] gcRanges() inout nothrow @nogc
+    {
+        return _gcRanges[];
+    }
+
+private:
+
+    invariant()
+    {
+        safeAssert(_moduleGroup.modules.length > 0, "No modules for DSO.");
+    }
+
+    void** _slot;
+    ModuleGroup _moduleGroup;
+    Array!(void[]) _gcRanges;
+
+    version (Shared)
+    {
+        Array!(void[]) _codeSegments; // array of code segments
+        Array!(DSO*) _deps; // D libraries needed by this DSO
+        void* _handle; // corresponding handle
+    }
+}
+
+/****
+ * 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
+{
+    _isRuntimeInitialized = true;
+}
+
+/***
+ * 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)
+{
+    import gcc.sections : pinLoadedLibraries, unpinLoadedLibraries,
+           inheritLoadedLibraries, cleanupLoadedLibraries;
+
+    /***
+     * 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
+    {
+        version (GNU_EMUTLS)
+            _d_emutls_scan(dg);
+        else
+            static assert(0, "Native TLS unimplemented");
+    }
+
+    // interface for core.thread to inherit loaded libraries
+    pragma(mangle, gcc.sections.pinLoadedLibraries.mangleof)
+    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(nameForDSO(tdso._pdso), 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;
+    }
+
+    pragma(mangle, gcc.sections.unpinLoadedLibraries.mangleof)
+    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.
+    pragma(mangle, gcc.sections.inheritLoadedLibraries.mangleof)
+    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);
+    }
+
+    // Called after all TLS dtors ran, decrements all remaining dlopen refs.
+    pragma(mangle, gcc.sections.cleanupLoadedLibraries.mangleof)
+    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 null;
+    }
+
+    void finiTLSRanges(Array!(void[])* rngs) nothrow @nogc
+    {
+    }
+
+    void scanTLSRanges(Array!(void[])* rngs, scope ScanDG dg) nothrow
+    {
+        version (GNU_EMUTLS)
+            _d_emutls_scan(dg);
+        else
+            static assert(0, "Native TLS unimplemented");
+    }
+}
+
+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");
+        alias _pdso this;
+    }
+
+    @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 the native handle (as returned by dlopen)
+     * to the corresponding DSO*, protected by a mutex.
+     */
+    __gshared pthread_mutex_t _handleToDSOMutex;
+    @property ref HashTab!(void*, DSO*) _handleToDSO() @nogc nothrow
+    {
+        __gshared HashTab!(void*, DSO*) x;
+        return x;
+    }
+}
+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;
+    }
+
+    enum _rtLoading = false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Compiler to runtime interface.
+///////////////////////////////////////////////////////////////////////////////
+
+struct MachHeader
+{
+    const(mach_header)* header; // the mach header of the image
+    intptr_t slide;             // virtural memory address slide amount
+}
+
+/****
+ * 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);
+        pdso._slot = data._slot;
+        *data._slot = pdso; // store backlink in library record
+
+        pdso._moduleGroup = ModuleGroup(toRange(data._minfo_beg, data._minfo_end));
+
+        MachHeader header = void;
+        const headerFound = findImageHeaderForAddr(data._slot, header);
+        safeAssert(headerFound, "Failed to find image header.");
+
+        scanSegments(header, pdso);
+
+        version (Shared)
+        {
+            auto handle = handleForAddr(data._slot);
+
+            getDependencies(header, 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));
+            }
+        }
+        else
+        {
+            foreach (p; _loadedDSOs)
+                safeAssert(p !is pdso, "DSO already registered.");
+            _loadedDSOs.insertBack(pdso);
+        }
+
+        // don't initialize modules before rt_init was called
+        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 (GNU_EMUTLS)
+                _d_emutls_destroy();
+            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._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:
+    const(char)* nameForDSO(in DSO* pdso)
+    {
+        Dl_info info = void;
+        const success = dladdr(pdso._slot, &info) != 0;
+        safeAssert(success, "Failed to get DSO info.");
+        return info.dli_fname;
+    }
+
+    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 MachHeader info, ref Array!(DSO*) deps)
+    {
+        // FIXME: Not implemented yet.
+    }
+
+    void* handleForName(const char* name)
+    {
+        auto handle = .dlopen(name, RTLD_NOLOAD | RTLD_LAZY);
+        if (handle !is null) .dlclose(handle); // drop reference count
+        return handle;
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Mach-O program header iteration
+///////////////////////////////////////////////////////////////////////////////
+
+/************
+ * Scan segments in the image header and store
+ * the writeable data segments in *pdso.
+ */
+
+void scanSegments(in MachHeader info, DSO* pdso)
+{
+    foreach (e; dataSegs)
+    {
+        auto sect = getSection(info.header, info.slide, e.seg.ptr, e.sect.ptr);
+        if (sect != null)
+            pdso._gcRanges.insertBack((cast(void*)sect.ptr)[0 .. sect.length]);
+    }
+
+    version (Shared)
+    {
+        void[] text = getSection(info.header, info.slide, "__TEXT", "__text");
+        if (!text)
+            assert(0, "Failed to get text section.");
+        pdso._codeSegments.insertBack(text);
+    }
+}
+
+/**************************
+ * Input:
+ *      result where the output is to be written
+ * Returns:
+ *      true if found, and *result is filled in
+ */
+
+bool findImageHeaderForAddr(in void* addr, out MachHeader result)
+{
+    Dl_info info;
+    if (dladdr(addr, &info) == 0)
+        return false;
+
+    foreach (i; 0 .. _dyld_image_count())
+    {
+        if (info.dli_fbase == _dyld_get_image_header(i))
+        {
+            result.header = cast(const(mach_header)*)info.dli_fbase;
+            result.slide = _dyld_get_image_vmaddr_slide(i);
+            return true;
+        }
+    }
+    return false;
+}
+
+/**************************
+ * 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;
+}
+
+struct SegRef
+{
+    string seg;
+    string sect;
+}
+
+static immutable SegRef[] dataSegs = [{SEG_DATA, SECT_DATA},
+                                      {SEG_DATA, SECT_BSS},
+                                      {SEG_DATA, SECT_COMMON}];
+
+/**
+ * Returns the section for the named section in the named segment
+ * for the mach_header pointer passed, or null if not found.
+ */
+ubyte[] getSection(in mach_header* header, intptr_t slide,
+                   in char* segmentName, in char* sectionName)
+{
+    version (D_LP64)
+    {
+        assert(header.magic == MH_MAGIC_64);
+        auto sect = getsectbynamefromheader_64(cast(mach_header_64*)header,
+                                            segmentName,
+                                            sectionName);
+    }
+    else
+    {
+        assert(header.magic == MH_MAGIC);
+        auto sect = getsectbynamefromheader(header,
+                                            segmentName,
+                                            sectionName);
+    }
+
+    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/osx.d b/libphobos/libdruntime/gcc/sections/osx.d
deleted file mode 100644
index 3e3db70e4b8..00000000000
--- a/libphobos/libdruntime/gcc/sections/osx.d
+++ /dev/null
@@ -1,284 +0,0 @@
-// OSX-specific support for sections.
-// Copyright (C) 2019-2021 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
index fdaf039fcfa..4c2b542df23 100644
--- a/libphobos/libdruntime/gcc/sections/package.d
+++ b/libphobos/libdruntime/gcc/sections/package.d
@@ -22,27 +22,30 @@
 
 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.elf_shared;
-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;
+version (CRuntime_Glibc)  version = SectionsElf;
+version (CRuntime_Musl)   version = SectionsElf;
+version (CRuntime_UClibc) version = SectionsElf;
+version (FreeBSD)         version = SectionsElf;
+version (NetBSD)          version = SectionsElf;
+version (DragonFlyBSD)    version = SectionsElf;
+version (Solaris)         version = SectionsElf;
+version (OSX)             version = SectionsMacho;
+version (Windows)         version = SectionsPeCoff;
+
+version (SectionsElf)
+    public import gcc.sections.elf;
+else version (SectionsMacho)
+    public import gcc.sections.macho;
+else version (SectionsPeCoff)
+    public import gcc.sections.pecoff;
 else
     static assert(0, "unimplemented");
+
+version (Shared)
+{
+    // interface for core.thread to inherit loaded libraries
+    void* pinLoadedLibraries() nothrow @nogc;
+    void unpinLoadedLibraries(void* p) nothrow @nogc;
+    void inheritLoadedLibraries(void* p) nothrow @nogc;
+    void cleanupLoadedLibraries() nothrow @nogc;
+}
diff --git a/libphobos/libdruntime/gcc/sections/pecoff.d b/libphobos/libdruntime/gcc/sections/pecoff.d
new file mode 100644
index 00000000000..ed0340e0311
--- /dev/null
+++ b/libphobos/libdruntime/gcc/sections/pecoff.d
@@ -0,0 +1,826 @@
+// PE/COFF-specific support for sections.
+// Copyright (C) 2021 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.pecoff;
+
+version (Windows):
+
+import core.memory;
+import core.stdc.stdlib;
+import core.sys.windows.winbase;
+import core.sys.windows.windef;
+import core.sys.windows.winnt;
+import rt.minfo;
+import rt.util.container.array;
+import rt.util.container.hashtab;
+import gcc.sections.common;
+
+version (GNU_EMUTLS)
+    import gcc.emutls;
+
+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 inout(void[])[] gcRanges() inout nothrow @nogc
+    {
+        return _gcRanges[];
+    }
+
+private:
+
+    invariant()
+    {
+        safeAssert(_moduleGroup.modules.length > 0, "No modules for DSO.");
+    }
+
+    void** _slot;
+    ModuleGroup _moduleGroup;
+    Array!(void[]) _gcRanges;
+
+    version (Shared)
+    {
+        Array!(void[]) _codeSegments; // array of code segments
+        Array!(DSO*) _deps; // D libraries needed by this DSO
+        void* _handle; // corresponding handle
+    }
+}
+
+/****
+ * 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
+{
+    _isRuntimeInitialized = true;
+}
+
+/***
+ * 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)
+{
+    import gcc.sections : pinLoadedLibraries, unpinLoadedLibraries,
+           inheritLoadedLibraries, cleanupLoadedLibraries;
+
+    /***
+     * 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
+    {
+        version (GNU_EMUTLS)
+            _d_emutls_scan(dg);
+        else
+            static assert(0, "Native TLS unimplemented");
+    }
+
+    // interface for core.thread to inherit loaded libraries
+    pragma(mangle, gcc.sections.pinLoadedLibraries.mangleof)
+    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 DLL ref for explicitly loaded libraries to pin them.
+                char[MAX_PATH] buf;
+                char[] buffer = buf[];
+                const success = .LoadLibraryA(nameForDSO(tdso._pdso, buffer)) !is null;
+                safeAssert(success, "Failed to increment DLL ref.");
+                (*res)[i]._addCnt = 1; // new array takes over the additional ref count
+            }
+        }
+        return res;
+    }
+
+    pragma(mangle, gcc.sections.unpinLoadedLibraries.mangleof)
+    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.");
+                .FreeLibrary(handle);
+            }
+        }
+        pary.reset();
+        .free(pary);
+    }
+
+    // Called before TLS ctors are ran, copy over the loaded libraries
+    // of the parent thread.
+    pragma(mangle, gcc.sections.inheritLoadedLibraries.mangleof)
+    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);
+    }
+
+    // Called after all TLS dtors ran, decrements all remaining DLL refs.
+    pragma(mangle, gcc.sections.cleanupLoadedLibraries.mangleof)
+    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)
+                .FreeLibrary(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 null;
+    }
+
+    void finiTLSRanges(Array!(void[])* rngs) nothrow @nogc
+    {
+    }
+
+    void scanTLSRanges(Array!(void[])* rngs, scope ScanDG dg) nothrow
+    {
+        version (GNU_EMUTLS)
+            _d_emutls_scan(dg);
+        else
+            static assert(0, "Native TLS unimplemented");
+    }
+}
+
+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");
+        alias _pdso this;
+    }
+
+    @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 the native handle (as returned by dlopen)
+     * to the corresponding DSO*, protected by a mutex.
+     */
+    __gshared CRITICAL_SECTION _handleToDSOMutex;
+    @property ref HashTab!(void*, DSO*) _handleToDSO() @nogc nothrow
+    {
+        __gshared HashTab!(void*, DSO*) x;
+        return x;
+    }
+}
+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;
+    }
+
+    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);
+        pdso._slot = data._slot;
+        *data._slot = pdso; // store backlink in library record
+
+        pdso._moduleGroup = ModuleGroup(toRange(data._minfo_beg, data._minfo_end));
+
+        HMODULE handle = void;
+        const moduleFound = findModuleHandleForAddr(data._slot, handle);
+        safeAssert(moduleFound, "Failed to find image header.");
+
+        scanSegments(handle, pdso);
+
+        version (Shared)
+        {
+            getDependencies(handle, 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));
+            }
+        }
+        else
+        {
+            foreach (p; _loadedDSOs)
+                safeAssert(p !is pdso, "DSO already registered.");
+            _loadedDSOs.insertBack(pdso);
+        }
+
+        // don't initialize modules before rt_init was called
+        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 (GNU_EMUTLS)
+                _d_emutls_destroy();
+            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._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);
+    }
+}
+
+/***********************************
+ * These are a temporary means of providing a GC hook for DLL use.  They may be
+ * replaced with some other similar functionality later.
+ */
+extern (C)
+{
+    void* gc_getProxy();
+    void  gc_setProxy(void* p);
+    void  gc_clrProxy();
+
+    alias void  function(void*) gcSetFn;
+    alias void  function()      gcClrFn;
+}
+
+/*******************************************
+ * Loads a DLL written in D with the name 'name'.
+ * Returns:
+ *      opaque handle to the DLL if successfully loaded
+ *      null if failure
+ */
+extern(C) void* rt_loadLibrary(const char* name)
+{
+    version (Shared)
+    {
+        immutable save = _rtLoading;
+        _rtLoading = true;
+        scope (exit) _rtLoading = save;
+    }
+    return initLibrary(.LoadLibraryA(name));
+}
+
+extern (C) void* rt_loadLibraryW(const wchar_t* name)
+{
+    version (Shared)
+    {
+        immutable save = _rtLoading;
+        _rtLoading = true;
+        scope (exit) _rtLoading = save;
+    }
+    return initLibrary(.LoadLibraryW(name));
+}
+
+void* initLibrary(void* handle)
+{
+    if (handle is null)
+        return null;
+
+    version (Shared)
+    {
+        // if it's a D library
+        if (auto pdso = dsoForHandle(handle))
+            incThreadRef(pdso, true);
+    }
+    gcSetFn gcSet = cast(gcSetFn) GetProcAddress(handle, "gc_setProxy");
+    if (gcSet !is null)
+    {
+        // BUG: Set proxy, but too late
+        gcSet(gc_getProxy());
+    }
+    return handle;
+}
+
+/*************************************
+ * Unloads DLL that was previously loaded by rt_loadLibrary().
+ * Input:
+ *      handle  the handle returned by rt_loadLibrary()
+ * Returns:
+ *      1   succeeded
+ *      0   some failure happened
+ */
+extern(C) int rt_unloadLibrary(void* handle)
+{
+    if (handle is null)
+        return false;
+
+    version (Shared)
+    {
+        immutable save = _rtLoading;
+        _rtLoading = true;
+        scope (exit) _rtLoading = save;
+
+        // if it's a D library
+        if (auto pdso = dsoForHandle(handle))
+            decThreadRef(pdso, true);
+    }
+    gcClrFn gcClr  = cast(gcClrFn) GetProcAddress(handle, "gc_clrProxy");
+    if (gcClr !is null)
+        gcClr();
+    return .FreeLibrary(handle) != 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// helper functions
+///////////////////////////////////////////////////////////////////////////////
+
+void initLocks() nothrow @nogc
+{
+    version (Shared)
+        InitializeCriticalSection(&_handleToDSOMutex);
+}
+
+void finiLocks() nothrow @nogc
+{
+    version (Shared)
+        DeleteCriticalSection(&_handleToDSOMutex);
+}
+
+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:
+    const(char)* nameForDSO(DSO* pdso, ref char[] buffer)
+    {
+        const success = GetModuleFileNameA(pdso._handle, buffer.ptr, cast(DWORD)buffer.length) != 0;
+        safeAssert(success, "Failed to get DLL name.");
+        return buffer.ptr;
+    }
+
+    DSO* dsoForHandle(in void* handle)
+    {
+        DSO* pdso;
+        .EnterCriticalSection(&_handleToDSOMutex);
+        if (auto ppdso = handle in _handleToDSO)
+            pdso = *ppdso;
+        .LeaveCriticalSection(&_handleToDSOMutex);
+        return pdso;
+    }
+
+    void setDSOForHandle(DSO* pdso, void* handle)
+    {
+        .EnterCriticalSection(&_handleToDSOMutex);
+        safeAssert(handle !in _handleToDSO, "DSO already registered.");
+        _handleToDSO[handle] = pdso;
+        .LeaveCriticalSection(&_handleToDSOMutex);
+    }
+
+    void unsetDSOForHandle(DSO* pdso, void* handle)
+    {
+        .EnterCriticalSection(&_handleToDSOMutex);
+        safeAssert(_handleToDSO[handle] == pdso, "Handle doesn't match registered DSO.");
+        _handleToDSO.remove(handle);
+        .LeaveCriticalSection(&_handleToDSOMutex);
+    }
+
+    void getDependencies(in HMODULE handle, ref Array!(DSO*) deps)
+    {
+        auto nthdr = getNTHeader(handle);
+        auto import_entry = nthdr.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
+        auto addr = import_entry.VirtualAddress;
+        auto datasize = import_entry.Size;
+
+        if (addr == 0 && datasize == 0)
+        {
+            // Maybe the optional header isn't there, look for the section.
+            foreach (section; getSectionHeader(handle))
+            {
+                if (!compareSectionName(section, ".idata"))
+                    continue;
+                addr = section.VirtualAddress;
+                datasize = section.Misc.VirtualSize;
+                break;
+            }
+            if (datasize == 0)
+                return;
+        }
+        else
+        {
+            bool foundSection = false;
+            foreach (section; getSectionHeader(handle))
+            {
+                if (!compareSectionName(section, ".idata"))
+                    continue;
+                // Section containing import table has no contents.
+                if (section.Misc.VirtualSize == 0)
+                    return;
+                foundSection = true;
+                break;
+            }
+            // There is an import table, but the section containing it could not be found
+            if (!foundSection)
+                return;
+        }
+
+        // Get the names of each DLL
+        for (uint i = 0; i + IMAGE_IMPORT_DESCRIPTOR.sizeof <= datasize;
+             i += IMAGE_IMPORT_DESCRIPTOR.sizeof)
+        {
+            const data = cast(PIMAGE_IMPORT_DESCRIPTOR)(handle + addr + i);
+            if (data.Name == 0)
+                break;
+
+            // dll name of dependency
+            auto name = cast(char*)(handle + data.Name);
+            // get handle without loading the library
+            auto libhandle = 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)
+    {
+        return GetModuleHandleA(name);
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// PE/COFF program header iteration
+///////////////////////////////////////////////////////////////////////////////
+
+bool compareSectionName(ref IMAGE_SECTION_HEADER section, string name) nothrow @nogc
+{
+    if (name[] != cast(char[])section.Name[0 .. name.length])
+        return false;
+    return name.length == 8 || section.Name[name.length] == 0;
+}
+
+/************
+ * Scan segments in the image header and store
+ * the writeable data segments in *pdso.
+ */
+
+void scanSegments(in HMODULE handle, DSO* pdso) nothrow @nogc
+{
+    foreach (section; getSectionHeader(handle))
+    {
+        // the ".data" image section includes both object file sections ".data" and ".bss"
+        if (compareSectionName(section, ".data"))
+        {
+            auto data = cast(void*)handle + section.VirtualAddress;
+            pdso._gcRanges.insertBack(data[0 .. section.Misc.VirtualSize]);
+            continue;
+        }
+
+        version (Shared)
+        {
+            if (compareSectionName(section, ".text"))
+            {
+                auto text = cast(void*)handle + section.VirtualAddress;
+                pdso._codeSegments.insertBack(text[0 .. section.Misc.VirtualSize]);
+                continue;
+            }
+        }
+    }
+}
+
+/**************************
+ * Input:
+ *      handle  where the output is to be written
+ * Returns:
+ *      true if found, and *handle is filled in
+ */
+
+bool findModuleHandleForAddr(in void* addr, out HMODULE handle) nothrow @nogc
+{
+    if (GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
+                           cast(const(wchar)*) addr, &handle))
+        return true;
+
+    return false;
+}
+
+/**
+ * Returns the image NT header for the HMODULE handle passed,
+ * or null if not found.
+ */
+PIMAGE_NT_HEADERS getNTHeader(in HMODULE handle) nothrow @nogc
+{
+    auto doshdr = cast(PIMAGE_DOS_HEADER)handle;
+    if (doshdr.e_magic != IMAGE_DOS_SIGNATURE)
+        return null;
+
+    return cast(typeof(return))(cast(void*)doshdr + doshdr.e_lfanew);
+}
+
+/**
+ * Returns the image section header for the HMODULE handle passed,
+ * or null if not found.
+ */
+IMAGE_SECTION_HEADER[] getSectionHeader(in HMODULE handle) nothrow @nogc
+{
+    if (auto nthdr = getNTHeader(handle))
+    {
+        const void* opthdr = &nthdr.OptionalHeader;
+        const offset = nthdr.FileHeader.SizeOfOptionalHeader;
+        const length = nthdr.FileHeader.NumberOfSections;
+        return (cast(PIMAGE_SECTION_HEADER)(opthdr + offset))[0 .. length];
+    }
+    return null;
+}
diff --git a/libphobos/libdruntime/gcc/sections/win32.d b/libphobos/libdruntime/gcc/sections/win32.d
deleted file mode 100644
index b355dfc5068..00000000000
--- a/libphobos/libdruntime/gcc/sections/win32.d
+++ /dev/null
@@ -1,183 +0,0 @@
-// Win32-specific support for sections.
-// Copyright (C) 2019-2021 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
deleted file mode 100644
index 357940ba821..00000000000
--- a/libphobos/libdruntime/gcc/sections/win64.d
+++ /dev/null
@@ -1,321 +0,0 @@
-// Win64-specific support for sections.
-// Copyright (C) 2019-2021 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;
-}
-- 
2.27.0



^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2021-04-10 13:10 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-04-10 13:10 [committed] libphobos: Add section support code for MACHO and PE/COFF Iain Buclaw

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).