From 32703b80f66f7ca504eb79bee7e745f22cf096a8 Mon Sep 17 00:00:00 2001 From: Iain Buclaw Date: Thu, 8 Apr 2021 16:11:16 +0200 Subject: [PATCH] libphobos: Add section support code for MACHO and PE/COFF 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. 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 5137f85b5714..74a0f006036b 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 4a93b4921a48..63b2133e69c1 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 defdc9586f14..880836e3a250 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 4af26b42ffc7..000000000000 --- 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 -// . - -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 000000000000..85fdc0efd48a --- /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 +// . + +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 5b0fad9543b7..8450aecb239f 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 // . -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 000000000000..3ce58a533c37 --- /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 +// . + +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 3e3db70e4b8a..000000000000 --- 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 -// . - -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(§ions_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 fdaf039fcfa4..4c2b542df238 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 000000000000..ed0340e03118 --- /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 +// . + +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 b355dfc50687..000000000000 --- 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 -// . - -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 357940ba8219..000000000000 --- 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 -// . - -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; -}