From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: by sourceware.org (Postfix, from userid 1698) id 6F49038438A4; Sun, 13 Dec 2020 17:43:28 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 6F49038438A4 Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: Iain D Sandoe To: gcc-cvs@gcc.gnu.org Subject: [gcc(refs/users/iains/heads/d-for-darwin)] libphobos: Align interface of gcc.sections.osx with gcc.sections.elf_shared X-Act-Checkin: gcc X-Git-Author: Iain Buclaw X-Git-Refname: refs/users/iains/heads/d-for-darwin X-Git-Oldrev: 3b8c2098450ad985c03f3111200c725d31dc47e4 X-Git-Newrev: b07a001e2676d2aac15061c86e24453e21f4cdff Message-Id: <20201213174328.6F49038438A4@sourceware.org> Date: Sun, 13 Dec 2020 17:43:28 +0000 (GMT) X-BeenThere: gcc-cvs@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gcc-cvs mailing list List-Unsubscribe: , List-Archive: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 13 Dec 2020 17:43:28 -0000 https://gcc.gnu.org/g:b07a001e2676d2aac15061c86e24453e21f4cdff commit b07a001e2676d2aac15061c86e24453e21f4cdff Author: Iain Buclaw Date: Mon Dec 7 01:25:17 2020 +0100 libphobos: Align interface of gcc.sections.osx with gcc.sections.elf_shared Diff: --- libphobos/libdruntime/Makefile.am | 30 +- libphobos/libdruntime/Makefile.in | 64 +-- libphobos/libdruntime/core/sys/darwin/execinfo.d | 11 +- libphobos/libdruntime/gcc/sections/common.d | 48 -- libphobos/libdruntime/gcc/sections/osx.d | 569 ++++++++++++++++++++--- 5 files changed, 565 insertions(+), 157 deletions(-) diff --git a/libphobos/libdruntime/Makefile.am b/libphobos/libdruntime/Makefile.am index 124d5b049d0..722f1edfe09 100644 --- a/libphobos/libdruntime/Makefile.am +++ b/libphobos/libdruntime/Makefile.am @@ -183,21 +183,21 @@ DRUNTIME_DSOURCES = core/atomic.d core/attribute.d core/bitop.d \ gc/impl/conservative/gc.d gc/impl/manual/gc.d gc/os.d gc/pooltable.d \ gc/proxy.d gcc/attribute.d gcc/backtrace.d gcc/builtins.d gcc/deh.d \ gcc/emutls.d gcc/gthread.d gcc/sections/android.d \ - gcc/sections/common.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/typeinfo/ti_Acdouble.d \ - rt/typeinfo/ti_Acfloat.d rt/typeinfo/ti_Acreal.d \ - rt/typeinfo/ti_Adouble.d rt/typeinfo/ti_Afloat.d rt/typeinfo/ti_Ag.d \ - rt/typeinfo/ti_Aint.d rt/typeinfo/ti_Along.d rt/typeinfo/ti_Areal.d \ - rt/typeinfo/ti_Ashort.d rt/typeinfo/ti_C.d rt/typeinfo/ti_byte.d \ - rt/typeinfo/ti_cdouble.d rt/typeinfo/ti_cent.d rt/typeinfo/ti_cfloat.d \ - rt/typeinfo/ti_char.d rt/typeinfo/ti_creal.d rt/typeinfo/ti_dchar.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/typeinfo/ti_Acdouble.d rt/typeinfo/ti_Acfloat.d \ + rt/typeinfo/ti_Acreal.d rt/typeinfo/ti_Adouble.d \ + rt/typeinfo/ti_Afloat.d rt/typeinfo/ti_Ag.d rt/typeinfo/ti_Aint.d \ + rt/typeinfo/ti_Along.d rt/typeinfo/ti_Areal.d rt/typeinfo/ti_Ashort.d \ + rt/typeinfo/ti_C.d rt/typeinfo/ti_byte.d rt/typeinfo/ti_cdouble.d \ + rt/typeinfo/ti_cent.d rt/typeinfo/ti_cfloat.d rt/typeinfo/ti_char.d \ + rt/typeinfo/ti_creal.d rt/typeinfo/ti_dchar.d \ rt/typeinfo/ti_delegate.d rt/typeinfo/ti_double.d \ rt/typeinfo/ti_float.d rt/typeinfo/ti_idouble.d \ rt/typeinfo/ti_ifloat.d rt/typeinfo/ti_int.d rt/typeinfo/ti_ireal.d \ diff --git a/libphobos/libdruntime/Makefile.in b/libphobos/libdruntime/Makefile.in index 3bf08f52b31..d99998d8934 100644 --- a/libphobos/libdruntime/Makefile.in +++ b/libphobos/libdruntime/Makefile.in @@ -207,10 +207,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/backtrace.lo \ gcc/builtins.lo gcc/deh.lo gcc/emutls.lo gcc/gthread.lo \ - gcc/sections/android.lo gcc/sections/common.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/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/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 \ @@ -268,9 +267,10 @@ am__objects_3 = core/sys/posix/aio.lo core/sys/posix/arpa/inet.lo \ core/sys/posix/time.lo core/sys/posix/ucontext.lo \ core/sys/posix/unistd.lo core/sys/posix/utime.lo @DRUNTIME_OS_POSIX_TRUE@am__objects_4 = $(am__objects_3) -am__objects_5 = core/sys/darwin/crt_externs.lo \ - core/sys/darwin/dlfcn.lo core/sys/darwin/execinfo.lo \ - core/sys/darwin/mach/dyld.lo core/sys/darwin/mach/getsect.lo \ +am__objects_5 = core/sys/darwin/config.lo \ + core/sys/darwin/crt_externs.lo core/sys/darwin/dlfcn.lo \ + core/sys/darwin/execinfo.lo core/sys/darwin/mach/dyld.lo \ + core/sys/darwin/mach/getsect.lo \ core/sys/darwin/mach/kern_return.lo \ core/sys/darwin/mach/loader.lo core/sys/darwin/mach/port.lo \ core/sys/darwin/mach/semaphore.lo \ @@ -809,21 +809,21 @@ DRUNTIME_DSOURCES = core/atomic.d core/attribute.d core/bitop.d \ gc/impl/conservative/gc.d gc/impl/manual/gc.d gc/os.d gc/pooltable.d \ gc/proxy.d gcc/attribute.d gcc/backtrace.d gcc/builtins.d gcc/deh.d \ gcc/emutls.d gcc/gthread.d gcc/sections/android.d \ - gcc/sections/common.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/typeinfo/ti_Acdouble.d \ - rt/typeinfo/ti_Acfloat.d rt/typeinfo/ti_Acreal.d \ - rt/typeinfo/ti_Adouble.d rt/typeinfo/ti_Afloat.d rt/typeinfo/ti_Ag.d \ - rt/typeinfo/ti_Aint.d rt/typeinfo/ti_Along.d rt/typeinfo/ti_Areal.d \ - rt/typeinfo/ti_Ashort.d rt/typeinfo/ti_C.d rt/typeinfo/ti_byte.d \ - rt/typeinfo/ti_cdouble.d rt/typeinfo/ti_cent.d rt/typeinfo/ti_cfloat.d \ - rt/typeinfo/ti_char.d rt/typeinfo/ti_creal.d rt/typeinfo/ti_dchar.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/typeinfo/ti_Acdouble.d rt/typeinfo/ti_Acfloat.d \ + rt/typeinfo/ti_Acreal.d rt/typeinfo/ti_Adouble.d \ + rt/typeinfo/ti_Afloat.d rt/typeinfo/ti_Ag.d rt/typeinfo/ti_Aint.d \ + rt/typeinfo/ti_Along.d rt/typeinfo/ti_Areal.d rt/typeinfo/ti_Ashort.d \ + rt/typeinfo/ti_C.d rt/typeinfo/ti_byte.d rt/typeinfo/ti_cdouble.d \ + rt/typeinfo/ti_cent.d rt/typeinfo/ti_cfloat.d rt/typeinfo/ti_char.d \ + rt/typeinfo/ti_creal.d rt/typeinfo/ti_dchar.d \ rt/typeinfo/ti_delegate.d rt/typeinfo/ti_double.d \ rt/typeinfo/ti_float.d rt/typeinfo/ti_idouble.d \ rt/typeinfo/ti_ifloat.d rt/typeinfo/ti_int.d rt/typeinfo/ti_ireal.d \ @@ -841,15 +841,15 @@ DRUNTIME_DSOURCES_STDCXX = core/stdcpp/exception.d \ DRUNTIME_DSOURCES_BIONIC = core/sys/bionic/fcntl.d \ core/sys/bionic/string.d core/sys/bionic/unistd.d -DRUNTIME_DSOURCES_DARWIN = core/sys/darwin/crt_externs.d \ - core/sys/darwin/dlfcn.d core/sys/darwin/execinfo.d \ - core/sys/darwin/mach/dyld.d core/sys/darwin/mach/getsect.d \ - core/sys/darwin/mach/kern_return.d core/sys/darwin/mach/loader.d \ - core/sys/darwin/mach/port.d core/sys/darwin/mach/semaphore.d \ - core/sys/darwin/mach/thread_act.d core/sys/darwin/netinet/in_.d \ - core/sys/darwin/pthread.d core/sys/darwin/string.d \ - core/sys/darwin/sys/cdefs.d core/sys/darwin/sys/event.d \ - core/sys/darwin/sys/mman.d +DRUNTIME_DSOURCES_DARWIN = core/sys/darwin/config.d \ + core/sys/darwin/crt_externs.d core/sys/darwin/dlfcn.d \ + core/sys/darwin/execinfo.d core/sys/darwin/mach/dyld.d \ + core/sys/darwin/mach/getsect.d core/sys/darwin/mach/kern_return.d \ + core/sys/darwin/mach/loader.d core/sys/darwin/mach/port.d \ + core/sys/darwin/mach/semaphore.d core/sys/darwin/mach/thread_act.d \ + core/sys/darwin/netinet/in_.d core/sys/darwin/pthread.d \ + core/sys/darwin/string.d core/sys/darwin/sys/cdefs.d \ + core/sys/darwin/sys/event.d core/sys/darwin/sys/mman.d DRUNTIME_DSOURCES_DRAGONFLYBSD = core/sys/dragonflybsd/dlfcn.d \ core/sys/dragonflybsd/execinfo.d core/sys/dragonflybsd/netinet/in_.d \ @@ -1196,7 +1196,6 @@ gcc/sections/$(am__dirstamp): @$(MKDIR_P) gcc/sections @: > gcc/sections/$(am__dirstamp) gcc/sections/android.lo: gcc/sections/$(am__dirstamp) -gcc/sections/common.lo: gcc/sections/$(am__dirstamp) gcc/sections/elf_shared.lo: gcc/sections/$(am__dirstamp) gcc/sections/osx.lo: gcc/sections/$(am__dirstamp) gcc/sections/package.lo: gcc/sections/$(am__dirstamp) @@ -1359,6 +1358,7 @@ core/sys/posix/utime.lo: core/sys/posix/$(am__dirstamp) core/sys/darwin/$(am__dirstamp): @$(MKDIR_P) core/sys/darwin @: > core/sys/darwin/$(am__dirstamp) +core/sys/darwin/config.lo: core/sys/darwin/$(am__dirstamp) core/sys/darwin/crt_externs.lo: core/sys/darwin/$(am__dirstamp) core/sys/darwin/dlfcn.lo: core/sys/darwin/$(am__dirstamp) core/sys/darwin/execinfo.lo: core/sys/darwin/$(am__dirstamp) diff --git a/libphobos/libdruntime/core/sys/darwin/execinfo.d b/libphobos/libdruntime/core/sys/darwin/execinfo.d index c929cc2f88d..d9607f392eb 100644 --- a/libphobos/libdruntime/core/sys/darwin/execinfo.d +++ b/libphobos/libdruntime/core/sys/darwin/execinfo.d @@ -21,6 +21,11 @@ extern (C): nothrow: @nogc: -int backtrace(void** buffer, int size); -char** backtrace_symbols(const(void*)* buffer, int size); -void backtrace_symbols_fd(const(void*)* buffer, int size, int fd); +import core.sys.darwin.config; + +static if (__traits(getTargetInfo, "osxVersionMin") >= __MAC_10_5) +{ + int backtrace(void** buffer, int size); + char** backtrace_symbols(const(void*)* buffer, int size); + void backtrace_symbols_fd(const(void*)* buffer, int size, int fd); +} diff --git a/libphobos/libdruntime/gcc/sections/common.d b/libphobos/libdruntime/gcc/sections/common.d deleted file mode 100644 index b2d2322347d..00000000000 --- a/libphobos/libdruntime/gcc/sections/common.d +++ /dev/null @@ -1,48 +0,0 @@ -// Common support routines for retrieving platform-specific sections. -// Copyright (C) 2019 Free Software Foundation, Inc. - -// GCC is free software; you can redistribute it and/or modify it under -// the terms of the GNU General Public License as published by the Free -// Software Foundation; either version 3, or (at your option) any later -// version. - -// GCC is distributed in the hope that it will be useful, but WITHOUT ANY -// WARRANTY; without even the implied warranty of MERCHANTABILITY or -// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -// for more details. - -// Under Section 7 of GPL version 3, you are granted additional -// permissions described in the GCC Runtime Library Exception, version -// 3.1, as published by the Free Software Foundation. - -// You should have received a copy of the GNU General Public License and -// a copy of the GCC Runtime Library Exception along with this program; -// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -// . - -module gcc.sections.common; - -/**** - * 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.). - */ -void safeAssert(bool condition, scope string msg, size_t line = __LINE__) @nogc nothrow @safe -{ - import core.internal.abort; - condition || abort(msg, __FILE__, line); -} - -/**** - * 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]; } diff --git a/libphobos/libdruntime/gcc/sections/osx.d b/libphobos/libdruntime/gcc/sections/osx.d index 6ace2da4a8b..9ec5b79974e 100644 --- a/libphobos/libdruntime/gcc/sections/osx.d +++ b/libphobos/libdruntime/gcc/sections/osx.d @@ -24,17 +24,31 @@ module gcc.sections.osx; version (OSX): -import gcc.sections.common; - +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; version (GNU_EMUTLS) import gcc.emutls; +/**** + * 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.). + */ +void safeAssert(bool condition, scope string msg, size_t line = __LINE__) @nogc nothrow @safe +{ + import core.internal.abort; + condition || abort(msg, __FILE__, line); +} + alias DSO SectionGroup; struct DSO { @@ -74,8 +88,22 @@ struct DSO } 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 + } } /**** @@ -99,63 +127,185 @@ void finiSections() nothrow @nogc _isRuntimeInitialized = false; } -void[]* initTLSRanges() nothrow @nogc -{ - return null; -} +alias ScanDG = void delegate(void* pbeg, void* pend) nothrow; -void finiTLSRanges(void[]* rng) nothrow @nogc +version (Shared) { -} + /*** + * Called once per thread; returns array of thread local storage ranges + */ + Array!(ThreadDSO)* initTLSRanges() @nogc nothrow + { + return &_loadedDSOs(); + } -void scanTLSRanges(void[]* rng, scope void delegate(void* pbeg, void* pend) nothrow dg) nothrow -{ - version (GNU_EMUTLS) - _d_emutls_scan(dg); - else - static assert(0, "Native TLS unimplemented"); -} + 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"); + } -version (Shared) -{ // interface for core.thread to inherit loaded libraries void* pinLoadedLibraries() nothrow @nogc { - return null; + 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; } void unpinLoadedLibraries(void* p) nothrow @nogc { + auto pary = cast(Array!(ThreadDSO)*)p; + // In case something failed we need to undo the pinning. + foreach (ref tdso; *pary) + { + if (tdso._addCnt) + { + auto handle = tdso._pdso._handle; + safeAssert(handle !is null, "Invalid library handle."); + .dlclose(handle); + } + } + pary.reset(); + .free(pary); } // Called before TLS ctors are ran, copy over the loaded libraries // of the parent thread. void inheritLoadedLibraries(void* p) nothrow @nogc { + safeAssert(_loadedDSOs.empty, "DSOs have already been registered for this thread."); + _loadedDSOs.swap(*cast(Array!(ThreadDSO)*)p); + .free(p); } // Called after all TLS dtors ran, decrements all remaining dlopen refs. void cleanupLoadedLibraries() nothrow @nogc { + foreach (ref tdso; _loadedDSOs) + { + if (tdso._addCnt == 0) continue; + + auto handle = tdso._pdso._handle; + safeAssert(handle !is null, "Invalid DSO handle."); + for (; tdso._addCnt > 0; --tdso._addCnt) + .dlclose(handle); + } + + // Free the memory for the array contents. + _loadedDSOs.reset(); + } +} +else +{ + /*** + * Called once per thread; returns array of thread local storage ranges + */ + Array!(void[])* initTLSRanges() nothrow @nogc + { + return 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: -/* - * Static DSOs loaded by the runtime linker. This includes the - * executable. These can't be unloaded. - */ -@property ref Array!(DSO*) _loadedDSOs() @nogc nothrow +version (Shared) { - __gshared Array!(DSO*) x; - return x; + /* + * 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; + } -/* - * Set to true during rt_loadLibrary/rt_unloadLibrary calls. - */ -enum _rtLoading = false; + enum _rtLoading = false; +} + +/////////////////////////////////////////////////////////////////////////////// +// Compiler to runtime interface. +/////////////////////////////////////////////////////////////////////////////// struct MachHeader { @@ -163,9 +313,18 @@ struct MachHeader intptr_t slide; // virtural memory address slide amount } -/////////////////////////////////////////////////////////////////////////////// -// 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(). @@ -180,27 +339,57 @@ extern(C) void _d_dso_registry(CompilerDSOData* data) // 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 info = void; - const headerFound = findDSOHeaderForAddr(data._slot, &info); + MachHeader header = void; + const headerFound = findImageHeaderForAddr(data._slot, header); safeAssert(headerFound, "Failed to find image header."); - foreach (e; dataSegs) + 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 { - 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]); + foreach (p; _loadedDSOs) + safeAssert(p !is pdso, "DSO already registered."); + _loadedDSOs.insertBack(pdso); } - 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 @@ -208,22 +397,274 @@ extern(C) void _d_dso_registry(CompilerDSOData* data) DSO* pdso = cast(DSO*)*data._slot; *data._slot = null; - // static DSOs are unloaded in reverse order - safeAssert(pdso == _loadedDSOs.back, "DSO being unregistered isn't current last one."); - _loadedDSOs.popBack(); + // 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(); + } - pdso._gcRanges.reset(); - .free(pdso); + 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 @@ -231,28 +672,38 @@ extern(C) void _d_dso_registry(CompilerDSOData* data) * true if found, and *result is filled in */ -bool findDSOHeaderForAddr(in void* addr, MachHeader* result) +bool findImageHeaderForAddr(in void* addr, out MachHeader result) { + Dl_info info; + if (dladdr(addr, &info) == 0) + return false; + foreach (i; 0 .. _dyld_image_count()) { - const header = _dyld_get_image_header(i); - const slide = _dyld_get_image_vmaddr_slide(i); - const section = getSection(header, slide, SEG_DATA, SECT_DATA); - - // less than base address of section means quick reject - if (!section.length || addr < section.ptr) - continue; - - if (addr < section.ptr + section.length) + if (info.dli_fbase == _dyld_get_image_header(i)) { - result.header = header; - result.slide = slide; + 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;