+2019-04-13 Iain Buclaw <ibuclaw@gdcproject.org>
+
+ * libdruntime/Makefile.am (DRUNTIME_CSOURCES): Remove bss_sections.c.
+ (DRUNTIME_DSOURCES): Rename rt/sections_* modules to gcc/sections/*.
+ * libdruntime/Makefile.in: Regenerate.
+ * libdruntime/gcc/sections/android.d: New file.
+ * libdruntime/gcc/sections/elf_shared.d: New file.
+ * libdruntime/gcc/sections/osx.d: New file.
+ * libdruntime/gcc/sections/package.d: New file.
+ * libdruntime/gcc/sections/solaris.d: New file.
+ * libdruntime/gcc/sections/win32.d: New file.
+ * libdruntime/gcc/sections/win64.d: New file.
+ * libdruntime/rt/bss_section.c: Remove.
+ * libdruntime/rt/sections.d: Publicly import gcc.sections.
+ * libdruntime/rt/sections_android.d: Remove.
+ * libdruntime/rt/sections_elf_shared.d: Remove.
+ * libdruntime/rt/sections_osx.d: Remove.
+ * libdruntime/rt/sections_solaris.d: Remove.
+ * libdruntime/rt/sections_win32.d: Remove.
+ * libdruntime/rt/sections_win64.d: Remove.
+
2019-04-12 Iain Buclaw <ibuclaw@gdcproject.org>
* configure.ac (AM_INIT_AUTOMAKE): Add subdir-objects.
# https://www.gnu.org/software/automake/manual/html_node/Wildcards.html
DRUNTIME_SSOURCES = core/threadasm.S
-DRUNTIME_CSOURCES = core/stdc/errno_.c rt/bss_section.c
+DRUNTIME_CSOURCES = core/stdc/errno_.c
DRUNTIME_DSOURCES = core/atomic.d core/attribute.d core/bitop.d \
core/checkedint.d core/cpuid.d core/demangle.d core/exception.d \
core/sync/config.d core/sync/exception.d core/sync/mutex.d \
core/sync/rwmutex.d core/sync/semaphore.d core/thread.d core/time.d \
core/vararg.d gcc/attribute.d gcc/backtrace.d gcc/builtins.d gcc/deh.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/sections_android.d rt/sections_elf_shared.d rt/sections_osx.d \
- rt/sections_solaris.d rt/sections_win32.d rt/sections_win64.d \
+ gcc/sections/android.d gcc/sections/elf_shared.d gcc/sections/osx.d \
+ gcc/sections/package.d gcc/sections/solaris.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 \
core/sync/exception.lo core/sync/mutex.lo core/sync/rwmutex.lo \
core/sync/semaphore.lo core/thread.lo core/time.lo \
core/vararg.lo gcc/attribute.lo gcc/backtrace.lo \
- gcc/builtins.lo gcc/deh.lo gcc/unwind/arm.lo \
+ gcc/builtins.lo gcc/deh.lo gcc/sections/android.lo \
+ gcc/sections/elf_shared.lo gcc/sections/osx.lo \
+ gcc/sections/package.lo gcc/sections/solaris.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 \
rt/config.lo rt/critical_.lo rt/deh.lo rt/dmain2.lo \
rt/invariant.lo rt/lifetime.lo rt/memory.lo rt/minfo.lo \
rt/monitor_.lo rt/obj.lo rt/qsort.lo rt/sections.lo \
- rt/sections_android.lo rt/sections_elf_shared.lo \
- rt/sections_osx.lo rt/sections_solaris.lo rt/sections_win32.lo \
- rt/sections_win64.lo rt/switch_.lo rt/tlsgc.lo \
- rt/typeinfo/ti_Acdouble.lo rt/typeinfo/ti_Acfloat.lo \
- rt/typeinfo/ti_Acreal.lo rt/typeinfo/ti_Adouble.lo \
- rt/typeinfo/ti_Afloat.lo rt/typeinfo/ti_Ag.lo \
- rt/typeinfo/ti_Aint.lo rt/typeinfo/ti_Along.lo \
- rt/typeinfo/ti_Areal.lo rt/typeinfo/ti_Ashort.lo \
- rt/typeinfo/ti_C.lo rt/typeinfo/ti_byte.lo \
- rt/typeinfo/ti_cdouble.lo rt/typeinfo/ti_cent.lo \
- rt/typeinfo/ti_cfloat.lo rt/typeinfo/ti_char.lo \
- rt/typeinfo/ti_creal.lo rt/typeinfo/ti_dchar.lo \
- rt/typeinfo/ti_delegate.lo rt/typeinfo/ti_double.lo \
- rt/typeinfo/ti_float.lo rt/typeinfo/ti_idouble.lo \
- rt/typeinfo/ti_ifloat.lo rt/typeinfo/ti_int.lo \
- rt/typeinfo/ti_ireal.lo rt/typeinfo/ti_long.lo \
- rt/typeinfo/ti_n.lo rt/typeinfo/ti_ptr.lo \
- rt/typeinfo/ti_real.lo rt/typeinfo/ti_short.lo \
- rt/typeinfo/ti_ubyte.lo rt/typeinfo/ti_ucent.lo \
- rt/typeinfo/ti_uint.lo rt/typeinfo/ti_ulong.lo \
- rt/typeinfo/ti_ushort.lo rt/typeinfo/ti_void.lo \
- rt/typeinfo/ti_wchar.lo rt/util/array.lo \
- rt/util/container/array.lo rt/util/container/common.lo \
- rt/util/container/hashtab.lo rt/util/container/treap.lo \
- rt/util/random.lo rt/util/typeinfo.lo rt/util/utf.lo
+ rt/switch_.lo rt/tlsgc.lo rt/typeinfo/ti_Acdouble.lo \
+ rt/typeinfo/ti_Acfloat.lo rt/typeinfo/ti_Acreal.lo \
+ rt/typeinfo/ti_Adouble.lo rt/typeinfo/ti_Afloat.lo \
+ rt/typeinfo/ti_Ag.lo rt/typeinfo/ti_Aint.lo \
+ rt/typeinfo/ti_Along.lo rt/typeinfo/ti_Areal.lo \
+ rt/typeinfo/ti_Ashort.lo rt/typeinfo/ti_C.lo \
+ rt/typeinfo/ti_byte.lo rt/typeinfo/ti_cdouble.lo \
+ rt/typeinfo/ti_cent.lo rt/typeinfo/ti_cfloat.lo \
+ rt/typeinfo/ti_char.lo rt/typeinfo/ti_creal.lo \
+ rt/typeinfo/ti_dchar.lo rt/typeinfo/ti_delegate.lo \
+ rt/typeinfo/ti_double.lo rt/typeinfo/ti_float.lo \
+ rt/typeinfo/ti_idouble.lo rt/typeinfo/ti_ifloat.lo \
+ rt/typeinfo/ti_int.lo rt/typeinfo/ti_ireal.lo \
+ rt/typeinfo/ti_long.lo rt/typeinfo/ti_n.lo \
+ rt/typeinfo/ti_ptr.lo rt/typeinfo/ti_real.lo \
+ rt/typeinfo/ti_short.lo rt/typeinfo/ti_ubyte.lo \
+ rt/typeinfo/ti_ucent.lo rt/typeinfo/ti_uint.lo \
+ rt/typeinfo/ti_ulong.lo rt/typeinfo/ti_ushort.lo \
+ rt/typeinfo/ti_void.lo rt/typeinfo/ti_wchar.lo \
+ rt/util/array.lo rt/util/container/array.lo \
+ rt/util/container/common.lo rt/util/container/hashtab.lo \
+ rt/util/container/treap.lo rt/util/random.lo \
+ rt/util/typeinfo.lo rt/util/utf.lo
am__objects_2 = gc/bits.lo gc/config.lo gc/gcinterface.lo \
gc/impl/conservative/gc.lo gc/impl/manual/gc.lo gc/os.lo \
gc/pooltable.lo gc/proxy.lo
$(am__objects_13) $(am__objects_15) $(am__objects_17) \
$(am__objects_19) $(am__objects_21) $(am__objects_23) \
$(am__objects_25) $(am__objects_26)
-am__objects_28 = core/stdc/libgdruntime_la-errno_.lo \
- rt/libgdruntime_la-bss_section.lo
+am__objects_28 = core/stdc/libgdruntime_la-errno_.lo
am__objects_29 = core/libgdruntime_la-threadasm.lo
am__objects_30 = $(am__objects_27) $(am__objects_28) $(am__objects_29)
am_libgdruntime_la_OBJECTS = $(am__objects_30)
# Can't use wildcards here:
# https://www.gnu.org/software/automake/manual/html_node/Wildcards.html
DRUNTIME_SSOURCES = core/threadasm.S
-DRUNTIME_CSOURCES = core/stdc/errno_.c rt/bss_section.c
+DRUNTIME_CSOURCES = core/stdc/errno_.c
DRUNTIME_DSOURCES = core/atomic.d core/attribute.d core/bitop.d \
core/checkedint.d core/cpuid.d core/demangle.d core/exception.d \
core/internal/abort.d core/internal/arrayop.d core/internal/convert.d \
core/sync/config.d core/sync/exception.d core/sync/mutex.d \
core/sync/rwmutex.d core/sync/semaphore.d core/thread.d core/time.d \
core/vararg.d gcc/attribute.d gcc/backtrace.d gcc/builtins.d gcc/deh.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/sections_android.d rt/sections_elf_shared.d rt/sections_osx.d \
- rt/sections_solaris.d rt/sections_win32.d rt/sections_win64.d \
+ gcc/sections/android.d gcc/sections/elf_shared.d gcc/sections/osx.d \
+ gcc/sections/package.d gcc/sections/solaris.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 \
gcc/backtrace.lo: gcc/$(am__dirstamp)
gcc/builtins.lo: gcc/$(am__dirstamp)
gcc/deh.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/package.lo: gcc/sections/$(am__dirstamp)
+gcc/sections/solaris.lo: gcc/sections/$(am__dirstamp)
+gcc/sections/win32.lo: gcc/sections/$(am__dirstamp)
+gcc/sections/win64.lo: gcc/sections/$(am__dirstamp)
gcc/unwind/$(am__dirstamp):
@$(MKDIR_P) gcc/unwind
@: > gcc/unwind/$(am__dirstamp)
rt/obj.lo: rt/$(am__dirstamp)
rt/qsort.lo: rt/$(am__dirstamp)
rt/sections.lo: rt/$(am__dirstamp)
-rt/sections_android.lo: rt/$(am__dirstamp)
-rt/sections_elf_shared.lo: rt/$(am__dirstamp)
-rt/sections_osx.lo: rt/$(am__dirstamp)
-rt/sections_solaris.lo: rt/$(am__dirstamp)
-rt/sections_win32.lo: rt/$(am__dirstamp)
-rt/sections_win64.lo: rt/$(am__dirstamp)
rt/switch_.lo: rt/$(am__dirstamp)
rt/tlsgc.lo: rt/$(am__dirstamp)
rt/typeinfo/$(am__dirstamp):
gcc/config.lo: gcc/$(am__dirstamp)
gcc/libbacktrace.lo: gcc/$(am__dirstamp)
core/stdc/libgdruntime_la-errno_.lo: core/stdc/$(am__dirstamp)
-rt/libgdruntime_la-bss_section.lo: rt/$(am__dirstamp)
core/libgdruntime_la-threadasm.lo: core/$(am__dirstamp)
libgdruntime.la: $(libgdruntime_la_OBJECTS) $(libgdruntime_la_DEPENDENCIES) $(EXTRA_libgdruntime_la_DEPENDENCIES)
-rm -f gc/impl/manual/*.lo
-rm -f gcc/*.$(OBJEXT)
-rm -f gcc/*.lo
+ -rm -f gcc/sections/*.$(OBJEXT)
+ -rm -f gcc/sections/*.lo
-rm -f gcc/unwind/*.$(OBJEXT)
-rm -f gcc/unwind/*.lo
-rm -f gcstub/*.$(OBJEXT)
core/stdc/libgdruntime_la-errno_.lo: core/stdc/errno_.c
$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgdruntime_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o core/stdc/libgdruntime_la-errno_.lo `test -f 'core/stdc/errno_.c' || echo '$(srcdir)/'`core/stdc/errno_.c
-rt/libgdruntime_la-bss_section.lo: rt/bss_section.c
- $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgdruntime_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o rt/libgdruntime_la-bss_section.lo `test -f 'rt/bss_section.c' || echo '$(srcdir)/'`rt/bss_section.c
-
mostlyclean-libtool:
-rm -f *.lo
-rm -rf gc/impl/conservative/.libs gc/impl/conservative/_libs
-rm -rf gc/impl/manual/.libs gc/impl/manual/_libs
-rm -rf gcc/.libs gcc/_libs
+ -rm -rf gcc/sections/.libs gcc/sections/_libs
-rm -rf gcc/unwind/.libs gcc/unwind/_libs
-rm -rf gcstub/.libs gcstub/_libs
-rm -rf rt/.libs rt/_libs
-rm -f gc/impl/conservative/$(am__dirstamp)
-rm -f gc/impl/manual/$(am__dirstamp)
-rm -f gcc/$(am__dirstamp)
+ -rm -f gcc/sections/$(am__dirstamp)
-rm -f gcc/unwind/$(am__dirstamp)
-rm -f gcstub/$(am__dirstamp)
-rm -f rt/$(am__dirstamp)
--- /dev/null
+// Bionic-specific support for sections.
+// Copyright (C) 2019 Free Software Foundation, Inc.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+// for more details.
+
+// Under Section 7 of GPL version 3, you are granted additional
+// permissions described in the GCC Runtime Library Exception, version
+// 3.1, as published by the Free Software Foundation.
+
+// You should have received a copy of the GNU General Public License and
+// a copy of the GCC Runtime Library Exception along with this program;
+// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
+// <http://www.gnu.org/licenses/>.
+
+module gcc.sections.android;
+
+version (CRuntime_Bionic):
+
+// debug = PRINTF;
+debug(PRINTF) import core.stdc.stdio;
+import core.stdc.stdlib : malloc, free;
+import rt.deh, rt.minfo;
+import core.sys.posix.pthread;
+import core.stdc.stdlib : calloc;
+import core.stdc.string : memcpy;
+
+struct SectionGroup
+{
+ static int opApply(scope int delegate(ref SectionGroup) dg)
+ {
+ return dg(_sections);
+ }
+
+ static int opApplyReverse(scope int delegate(ref SectionGroup) dg)
+ {
+ return dg(_sections);
+ }
+
+ @property immutable(ModuleInfo*)[] modules() const nothrow @nogc
+ {
+ return _moduleGroup.modules;
+ }
+
+ @property ref inout(ModuleGroup) moduleGroup() inout nothrow @nogc
+ {
+ return _moduleGroup;
+ }
+
+ @property immutable(FuncTable)[] ehTables() const nothrow @nogc
+ {
+ auto pbeg = cast(immutable(FuncTable)*)&__start_deh;
+ auto pend = cast(immutable(FuncTable)*)&__stop_deh;
+ return pbeg[0 .. pend - pbeg];
+ }
+
+ @property inout(void[])[] gcRanges() inout nothrow @nogc
+ {
+ return _gcRanges[];
+ }
+
+private:
+ ModuleGroup _moduleGroup;
+ void[][1] _gcRanges;
+}
+
+void initSections() nothrow @nogc
+{
+ pthread_key_create(&_tlsKey, null);
+
+ auto mbeg = cast(immutable ModuleInfo**)&__start_minfo;
+ auto mend = cast(immutable ModuleInfo**)&__stop_minfo;
+ _sections.moduleGroup = ModuleGroup(mbeg[0 .. mend - mbeg]);
+
+ auto pbeg = cast(void*)&_tlsend;
+ auto pend = cast(void*)&__bss_end__;
+ // _tlsend is a 32-bit int and may not be 64-bit void*-aligned, so align pbeg.
+ version (D_LP64) pbeg = cast(void*)(cast(size_t)(pbeg + 7) & ~cast(size_t)7);
+ _sections._gcRanges[0] = pbeg[0 .. pend - pbeg];
+}
+
+void finiSections() nothrow @nogc
+{
+ pthread_key_delete(_tlsKey);
+}
+
+void[]* initTLSRanges() nothrow @nogc
+{
+ return &getTLSBlock();
+}
+
+void finiTLSRanges(void[]* rng) nothrow @nogc
+{
+ .free(rng.ptr);
+ .free(rng);
+}
+
+void scanTLSRanges(void[]* rng, scope void delegate(void* pbeg, void* pend) nothrow dg) nothrow
+{
+ dg(rng.ptr, rng.ptr + rng.length);
+}
+
+/* NOTE: The Bionic C library ignores thread-local data stored in the normal
+ * .tbss/.tdata ELF sections, which are marked with the SHF_TLS/STT_TLS
+ * flags. So instead we roll our own by keeping TLS data in the
+ * .tdata/.tbss sections but removing the SHF_TLS/STT_TLS flags, and
+ * access the TLS data using this function and the _tlsstart/_tlsend
+ * symbols as delimiters.
+ *
+ * This function is called by the code emitted by the compiler. It
+ * is expected to translate an address in the TLS static data to
+ * the corresponding address in the TLS dynamic per-thread data.
+ */
+
+extern(C) void* __tls_get_addr( void* p ) nothrow @nogc
+{
+ debug(PRINTF) printf(" __tls_get_addr input - %p\n", p);
+ immutable offset = cast(size_t)(p - cast(void*)&_tlsstart);
+ auto tls = getTLSBlockAlloc();
+ assert(offset < tls.length);
+ return tls.ptr + offset;
+}
+
+private:
+
+__gshared pthread_key_t _tlsKey;
+
+ref void[] getTLSBlock() nothrow @nogc
+{
+ auto pary = cast(void[]*)pthread_getspecific(_tlsKey);
+ if (pary is null)
+ {
+ pary = cast(void[]*).calloc(1, (void[]).sizeof);
+ if (pthread_setspecific(_tlsKey, pary) != 0)
+ {
+ import core.stdc.stdio;
+ perror("pthread_setspecific failed with");
+ assert(0);
+ }
+ }
+ return *pary;
+}
+
+ref void[] getTLSBlockAlloc() nothrow @nogc
+{
+ auto pary = &getTLSBlock();
+ if (!pary.length)
+ {
+ auto pbeg = cast(void*)&_tlsstart;
+ auto pend = cast(void*)&_tlsend;
+ auto p = .malloc(pend - pbeg);
+ memcpy(p, pbeg, pend - pbeg);
+ *pary = p[0 .. pend - pbeg];
+ }
+ return *pary;
+}
+
+__gshared SectionGroup _sections;
+
+extern(C)
+{
+ /* Symbols created by the compiler/linker and inserted into the
+ * object file that 'bracket' sections.
+ */
+ extern __gshared
+ {
+ void* __start_deh;
+ void* __stop_deh;
+ void* __start_minfo;
+ void* __stop_minfo;
+
+ size_t __bss_end__;
+
+ int _tlsstart;
+ int _tlsend;
+ }
+}
--- /dev/null
+// ELF-specific support for sections with shared libraries.
+// Copyright (C) 2019 Free Software Foundation, Inc.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+// for more details.
+
+// Under Section 7 of GPL version 3, you are granted additional
+// permissions described in the GCC Runtime Library Exception, version
+// 3.1, as published by the Free Software Foundation.
+
+// You should have received a copy of the GNU General Public License and
+// a copy of the GCC Runtime Library Exception along with this program;
+// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
+// <http://www.gnu.org/licenses/>.
+
+module gcc.sections.elf_shared;
+
+version (CRuntime_Glibc) enum SharedELF = true;
+else version (CRuntime_Musl) enum SharedELF = true;
+else version (FreeBSD) enum SharedELF = true;
+else version (NetBSD) enum SharedELF = true;
+else version (DragonFlyBSD) enum SharedELF = true;
+else version (CRuntime_UClibc) enum SharedELF = true;
+else enum SharedELF = false;
+static if (SharedELF):
+
+// debug = PRINTF;
+import core.memory;
+import core.stdc.config;
+import core.stdc.stdio;
+import core.stdc.stdlib : calloc, exit, free, malloc, EXIT_FAILURE;
+import core.stdc.string : strlen;
+version (linux)
+{
+ import core.sys.linux.dlfcn;
+ import core.sys.linux.elf;
+ import core.sys.linux.link;
+}
+else version (FreeBSD)
+{
+ import core.sys.freebsd.dlfcn;
+ import core.sys.freebsd.sys.elf;
+ import core.sys.freebsd.sys.link_elf;
+}
+else version (NetBSD)
+{
+ import core.sys.netbsd.dlfcn;
+ import core.sys.netbsd.sys.elf;
+ import core.sys.netbsd.sys.link_elf;
+}
+else version (DragonFlyBSD)
+{
+ import core.sys.dragonflybsd.dlfcn;
+ import core.sys.dragonflybsd.sys.elf;
+ import core.sys.dragonflybsd.sys.link_elf;
+}
+else
+{
+ static assert(0, "unimplemented");
+}
+import core.sys.posix.pthread;
+import rt.deh;
+import rt.dmain2;
+import rt.minfo;
+import rt.util.container.array;
+import rt.util.container.hashtab;
+
+/****
+ * Asserts the specified condition, independent from -release, by abort()ing.
+ * Regular assertions throw an AssertError and thus require an initialized
+ * GC, which isn't the case (yet or anymore) for the startup/shutdown code in
+ * this module (called by CRT ctors/dtors etc.).
+ */
+private void safeAssert(bool condition, scope string msg, size_t line = __LINE__) @nogc nothrow @safe
+{
+ import core.internal.abort;
+ condition || abort(msg, __FILE__, line);
+}
+
+alias DSO SectionGroup;
+struct DSO
+{
+ static int opApply(scope int delegate(ref DSO) dg)
+ {
+ foreach (dso; _loadedDSOs)
+ {
+ if (auto res = dg(*dso))
+ return res;
+ }
+ return 0;
+ }
+
+ static int opApplyReverse(scope int delegate(ref DSO) dg)
+ {
+ foreach_reverse (dso; _loadedDSOs)
+ {
+ if (auto res = dg(*dso))
+ return res;
+ }
+ return 0;
+ }
+
+ @property immutable(ModuleInfo*)[] modules() const nothrow @nogc
+ {
+ return _moduleGroup.modules;
+ }
+
+ @property ref inout(ModuleGroup) moduleGroup() inout nothrow @nogc
+ {
+ return _moduleGroup;
+ }
+
+ @property immutable(FuncTable)[] ehTables() const nothrow @nogc
+ {
+ return null;
+ }
+
+ @property inout(void[])[] gcRanges() inout nothrow @nogc
+ {
+ return _gcRanges[];
+ }
+
+private:
+
+ invariant()
+ {
+ safeAssert(_moduleGroup.modules.length > 0, "No modules for DSO.");
+ safeAssert(_tlsMod || !_tlsSize, "Inconsistent TLS fields for DSO.");
+ }
+
+ ModuleGroup _moduleGroup;
+ Array!(void[]) _gcRanges;
+ size_t _tlsMod;
+ size_t _tlsSize;
+
+ version (Shared)
+ {
+ Array!(void[]) _codeSegments; // array of code segments
+ Array!(DSO*) _deps; // D libraries needed by this DSO
+ void* _handle; // corresponding handle
+ }
+
+ // get the TLS range for the executing thread
+ void[] tlsRange() const nothrow @nogc
+ {
+ return getTLSRange(_tlsMod, _tlsSize);
+ }
+}
+
+/****
+ * Boolean flag set to true while the runtime is initialized.
+ */
+__gshared bool _isRuntimeInitialized;
+
+
+version (FreeBSD) private __gshared void* dummy_ref;
+version (DragonFlyBSD) private __gshared void* dummy_ref;
+version (NetBSD) private __gshared void* dummy_ref;
+
+/****
+ * Gets called on program startup just before GC is initialized.
+ */
+void initSections() nothrow @nogc
+{
+ _isRuntimeInitialized = true;
+ // reference symbol to support weak linkage
+ version (FreeBSD) dummy_ref = &_d_dso_registry;
+ version (DragonFlyBSD) dummy_ref = &_d_dso_registry;
+ version (NetBSD) dummy_ref = &_d_dso_registry;
+}
+
+
+/***
+ * Gets called on program shutdown just after GC is terminated.
+ */
+void finiSections() nothrow @nogc
+{
+ _isRuntimeInitialized = false;
+}
+
+alias ScanDG = void delegate(void* pbeg, void* pend) nothrow;
+
+version (Shared)
+{
+ /***
+ * Called once per thread; returns array of thread local storage ranges
+ */
+ Array!(ThreadDSO)* initTLSRanges() @nogc nothrow
+ {
+ return &_loadedDSOs();
+ }
+
+ void finiTLSRanges(Array!(ThreadDSO)* tdsos) @nogc nothrow
+ {
+ // Nothing to do here. tdsos used to point to the _loadedDSOs instance
+ // in the dying thread's TLS segment and as such is not valid anymore.
+ // The memory for the array contents was already reclaimed in
+ // cleanupLoadedLibraries().
+ }
+
+ void scanTLSRanges(Array!(ThreadDSO)* tdsos, scope ScanDG dg) nothrow
+ {
+ foreach (ref tdso; *tdsos)
+ dg(tdso._tlsRange.ptr, tdso._tlsRange.ptr + tdso._tlsRange.length);
+ }
+
+ // interface for core.thread to inherit loaded libraries
+ void* pinLoadedLibraries() nothrow @nogc
+ {
+ auto res = cast(Array!(ThreadDSO)*)calloc(1, Array!(ThreadDSO).sizeof);
+ res.length = _loadedDSOs.length;
+ foreach (i, ref tdso; _loadedDSOs)
+ {
+ (*res)[i] = tdso;
+ if (tdso._addCnt)
+ {
+ // Increment the dlopen ref for explicitly loaded libraries to pin them.
+ const success = .dlopen(linkMapForHandle(tdso._pdso._handle).l_name, RTLD_LAZY) !is null;
+ safeAssert(success, "Failed to increment dlopen ref.");
+ (*res)[i]._addCnt = 1; // new array takes over the additional ref count
+ }
+ }
+ return res;
+ }
+
+ void unpinLoadedLibraries(void* p) nothrow @nogc
+ {
+ auto pary = cast(Array!(ThreadDSO)*)p;
+ // In case something failed we need to undo the pinning.
+ foreach (ref tdso; *pary)
+ {
+ if (tdso._addCnt)
+ {
+ auto handle = tdso._pdso._handle;
+ safeAssert(handle !is null, "Invalid library handle.");
+ .dlclose(handle);
+ }
+ }
+ pary.reset();
+ .free(pary);
+ }
+
+ // Called before TLS ctors are ran, copy over the loaded libraries
+ // of the parent thread.
+ void inheritLoadedLibraries(void* p) nothrow @nogc
+ {
+ safeAssert(_loadedDSOs.empty, "DSOs have already been registered for this thread.");
+ _loadedDSOs.swap(*cast(Array!(ThreadDSO)*)p);
+ .free(p);
+ foreach (ref dso; _loadedDSOs)
+ {
+ // the copied _tlsRange corresponds to parent thread
+ dso.updateTLSRange();
+ }
+ }
+
+ // Called after all TLS dtors ran, decrements all remaining dlopen refs.
+ void cleanupLoadedLibraries() nothrow @nogc
+ {
+ foreach (ref tdso; _loadedDSOs)
+ {
+ if (tdso._addCnt == 0) continue;
+
+ auto handle = tdso._pdso._handle;
+ safeAssert(handle !is null, "Invalid DSO handle.");
+ for (; tdso._addCnt > 0; --tdso._addCnt)
+ .dlclose(handle);
+ }
+
+ // Free the memory for the array contents.
+ _loadedDSOs.reset();
+ }
+}
+else
+{
+ /***
+ * Called once per thread; returns array of thread local storage ranges
+ */
+ Array!(void[])* initTLSRanges() nothrow @nogc
+ {
+ return &_tlsRanges();
+ }
+
+ void finiTLSRanges(Array!(void[])* rngs) nothrow @nogc
+ {
+ rngs.reset();
+ }
+
+ void scanTLSRanges(Array!(void[])* rngs, scope ScanDG dg) nothrow
+ {
+ foreach (rng; *rngs)
+ dg(rng.ptr, rng.ptr + rng.length);
+ }
+}
+
+private:
+
+version (Shared)
+{
+ /*
+ * Array of thread local DSO metadata for all libraries loaded and
+ * initialized in this thread.
+ *
+ * Note:
+ * A newly spawned thread will inherit these libraries.
+ * Note:
+ * We use an array here to preserve the order of
+ * initialization. If that became a performance issue, we
+ * could use a hash table and enumerate the DSOs during
+ * loading so that the hash table values could be sorted when
+ * necessary.
+ */
+ struct ThreadDSO
+ {
+ DSO* _pdso;
+ static if (_pdso.sizeof == 8) uint _refCnt, _addCnt;
+ else static if (_pdso.sizeof == 4) ushort _refCnt, _addCnt;
+ else static assert(0, "unimplemented");
+ void[] _tlsRange;
+ alias _pdso this;
+ // update the _tlsRange for the executing thread
+ void updateTLSRange() nothrow @nogc
+ {
+ _tlsRange = _pdso.tlsRange();
+ }
+ }
+ @property ref Array!(ThreadDSO) _loadedDSOs() @nogc nothrow { static Array!(ThreadDSO) x; return x; }
+
+ /*
+ * Set to true during rt_loadLibrary/rt_unloadLibrary calls.
+ */
+ bool _rtLoading;
+
+ /*
+ * Hash table to map link_map* to corresponding DSO*.
+ * The hash table is protected by a Mutex.
+ */
+ __gshared pthread_mutex_t _handleToDSOMutex;
+ @property ref HashTab!(void*, DSO*) _handleToDSO() @nogc nothrow { __gshared HashTab!(void*, DSO*) x; return x; }
+
+ /*
+ * Section in executable that contains copy relocations.
+ * Might be null when druntime is dynamically loaded by a C host.
+ */
+ __gshared const(void)[] _copyRelocSection;
+}
+else
+{
+ /*
+ * Static DSOs loaded by the runtime linker. This includes the
+ * executable. These can't be unloaded.
+ */
+ @property ref Array!(DSO*) _loadedDSOs() @nogc nothrow { __gshared Array!(DSO*) x; return x; }
+
+ /*
+ * Thread local array that contains TLS memory ranges for each
+ * library initialized in this thread.
+ */
+ @property ref Array!(void[]) _tlsRanges() @nogc nothrow { static Array!(void[]) x; return x; }
+
+ enum _rtLoading = false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Compiler to runtime interface.
+///////////////////////////////////////////////////////////////////////////////
+
+/*
+ * This data structure is generated by the compiler, and then passed to
+ * _d_dso_registry().
+ */
+struct CompilerDSOData
+{
+ size_t _version; // currently 1
+ void** _slot; // can be used to store runtime data
+ immutable(object.ModuleInfo*)* _minfo_beg, _minfo_end; // array of modules in this object file
+}
+
+T[] toRange(T)(T* beg, T* end) { return beg[0 .. end - beg]; }
+
+/* For each shared library and executable, the compiler generates code that
+ * sets up CompilerDSOData and calls _d_dso_registry().
+ * A pointer to that code is inserted into both the .ctors and .dtors
+ * segment so it gets called by the loader on startup and shutdown.
+ */
+extern(C) void _d_dso_registry(CompilerDSOData* data)
+{
+ // only one supported currently
+ safeAssert(data._version >= 1, "Incompatible compiler-generated DSO data version.");
+
+ // no backlink => register
+ if (*data._slot is null)
+ {
+ immutable firstDSO = _loadedDSOs.empty;
+ if (firstDSO) initLocks();
+
+ DSO* pdso = cast(DSO*).calloc(1, DSO.sizeof);
+ assert(typeid(DSO).initializer().ptr is null);
+ *data._slot = pdso; // store backlink in library record
+
+ pdso._moduleGroup = ModuleGroup(toRange(data._minfo_beg, data._minfo_end));
+
+ dl_phdr_info info = void;
+ const headerFound = findDSOInfoForAddr(data._slot, &info);
+ safeAssert(headerFound, "Failed to find image header.");
+
+ scanSegments(info, pdso);
+
+ version (Shared)
+ {
+ auto handle = handleForAddr(data._slot);
+
+ getDependencies(info, pdso._deps);
+ pdso._handle = handle;
+ setDSOForHandle(pdso, pdso._handle);
+
+ if (!_rtLoading)
+ {
+ /* This DSO was not loaded by rt_loadLibrary which
+ * happens for all dependencies of an executable or
+ * the first dlopen call from a C program.
+ * In this case we add the DSO to the _loadedDSOs of this
+ * thread with a refCnt of 1 and call the TlsCtors.
+ */
+ immutable ushort refCnt = 1, addCnt = 0;
+ _loadedDSOs.insertBack(ThreadDSO(pdso, refCnt, addCnt, pdso.tlsRange()));
+ }
+ }
+ else
+ {
+ foreach (p; _loadedDSOs)
+ safeAssert(p !is pdso, "DSO already registered.");
+ _loadedDSOs.insertBack(pdso);
+ _tlsRanges.insertBack(pdso.tlsRange());
+ }
+
+ // don't initialize modules before rt_init was called (see Bugzilla 11378)
+ if (_isRuntimeInitialized)
+ {
+ registerGCRanges(pdso);
+ // rt_loadLibrary will run tls ctors, so do this only for dlopen
+ immutable runTlsCtors = !_rtLoading;
+ runModuleConstructors(pdso, runTlsCtors);
+ }
+ }
+ // has backlink => unregister
+ else
+ {
+ DSO* pdso = cast(DSO*)*data._slot;
+ *data._slot = null;
+
+ // don't finalizes modules after rt_term was called (see Bugzilla 11378)
+ if (_isRuntimeInitialized)
+ {
+ // rt_unloadLibrary already ran tls dtors, so do this only for dlclose
+ immutable runTlsDtors = !_rtLoading;
+ runModuleDestructors(pdso, runTlsDtors);
+ unregisterGCRanges(pdso);
+ // run finalizers after module dtors (same order as in rt_term)
+ version (Shared) runFinalizers(pdso);
+ }
+
+ version (Shared)
+ {
+ if (!_rtLoading)
+ {
+ /* This DSO was not unloaded by rt_unloadLibrary so we
+ * have to remove it from _loadedDSOs here.
+ */
+ foreach (i, ref tdso; _loadedDSOs)
+ {
+ if (tdso._pdso == pdso)
+ {
+ _loadedDSOs.remove(i);
+ break;
+ }
+ }
+ }
+
+ unsetDSOForHandle(pdso, pdso._handle);
+ }
+ else
+ {
+ // static DSOs are unloaded in reverse order
+ safeAssert(pdso == _loadedDSOs.back, "DSO being unregistered isn't current last one.");
+ _loadedDSOs.popBack();
+ }
+
+ freeDSO(pdso);
+
+ // last DSO being unloaded => shutdown registry
+ if (_loadedDSOs.empty)
+ {
+ version (Shared)
+ {
+ safeAssert(_handleToDSO.empty, "_handleToDSO not in sync with _loadedDSOs.");
+ _handleToDSO.reset();
+ }
+ finiLocks();
+ }
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Dynamic loading
+///////////////////////////////////////////////////////////////////////////////
+
+// Shared D libraries are only supported when linking against a shared druntime library.
+
+version (Shared)
+{
+ ThreadDSO* findThreadDSO(DSO* pdso) nothrow @nogc
+ {
+ foreach (ref tdata; _loadedDSOs)
+ if (tdata._pdso == pdso) return &tdata;
+ return null;
+ }
+
+ void incThreadRef(DSO* pdso, bool incAdd)
+ {
+ if (auto tdata = findThreadDSO(pdso)) // already initialized
+ {
+ if (incAdd && ++tdata._addCnt > 1) return;
+ ++tdata._refCnt;
+ }
+ else
+ {
+ foreach (dep; pdso._deps)
+ incThreadRef(dep, false);
+ immutable ushort refCnt = 1, addCnt = incAdd ? 1 : 0;
+ _loadedDSOs.insertBack(ThreadDSO(pdso, refCnt, addCnt, pdso.tlsRange()));
+ pdso._moduleGroup.runTlsCtors();
+ }
+ }
+
+ void decThreadRef(DSO* pdso, bool decAdd)
+ {
+ auto tdata = findThreadDSO(pdso);
+ safeAssert(tdata !is null, "Failed to find thread DSO.");
+ safeAssert(!decAdd || tdata._addCnt > 0, "Mismatching rt_unloadLibrary call.");
+
+ if (decAdd && --tdata._addCnt > 0) return;
+ if (--tdata._refCnt > 0) return;
+
+ pdso._moduleGroup.runTlsDtors();
+ foreach (i, ref td; _loadedDSOs)
+ if (td._pdso == pdso) _loadedDSOs.remove(i);
+ foreach (dep; pdso._deps)
+ decThreadRef(dep, false);
+ }
+
+ extern(C) void* rt_loadLibrary(const char* name)
+ {
+ immutable save = _rtLoading;
+ _rtLoading = true;
+ scope (exit) _rtLoading = save;
+
+ auto handle = .dlopen(name, RTLD_LAZY);
+ if (handle is null) return null;
+
+ // if it's a D library
+ if (auto pdso = dsoForHandle(handle))
+ incThreadRef(pdso, true);
+ return handle;
+ }
+
+ extern(C) int rt_unloadLibrary(void* handle)
+ {
+ if (handle is null) return false;
+
+ immutable save = _rtLoading;
+ _rtLoading = true;
+ scope (exit) _rtLoading = save;
+
+ // if it's a D library
+ if (auto pdso = dsoForHandle(handle))
+ decThreadRef(pdso, true);
+ return .dlclose(handle) == 0;
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Helper functions
+///////////////////////////////////////////////////////////////////////////////
+
+void initLocks() nothrow @nogc
+{
+ version (Shared)
+ !pthread_mutex_init(&_handleToDSOMutex, null) || assert(0);
+}
+
+void finiLocks() nothrow @nogc
+{
+ version (Shared)
+ !pthread_mutex_destroy(&_handleToDSOMutex) || assert(0);
+}
+
+void runModuleConstructors(DSO* pdso, bool runTlsCtors)
+{
+ pdso._moduleGroup.sortCtors();
+ pdso._moduleGroup.runCtors();
+ if (runTlsCtors) pdso._moduleGroup.runTlsCtors();
+}
+
+void runModuleDestructors(DSO* pdso, bool runTlsDtors)
+{
+ if (runTlsDtors) pdso._moduleGroup.runTlsDtors();
+ pdso._moduleGroup.runDtors();
+}
+
+void registerGCRanges(DSO* pdso) nothrow @nogc
+{
+ foreach (rng; pdso._gcRanges)
+ GC.addRange(rng.ptr, rng.length);
+}
+
+void unregisterGCRanges(DSO* pdso) nothrow @nogc
+{
+ foreach (rng; pdso._gcRanges)
+ GC.removeRange(rng.ptr);
+}
+
+version (Shared) void runFinalizers(DSO* pdso)
+{
+ foreach (seg; pdso._codeSegments)
+ GC.runFinalizers(seg);
+}
+
+void freeDSO(DSO* pdso) nothrow @nogc
+{
+ pdso._gcRanges.reset();
+ version (Shared)
+ {
+ pdso._codeSegments.reset();
+ pdso._deps.reset();
+ pdso._handle = null;
+ }
+ .free(pdso);
+}
+
+version (Shared)
+{
+@nogc nothrow:
+ link_map* linkMapForHandle(void* handle)
+ {
+ link_map* map;
+ const success = dlinfo(handle, RTLD_DI_LINKMAP, &map) == 0;
+ safeAssert(success, "Failed to get DSO info.");
+ return map;
+ }
+
+ link_map* exeLinkMap(link_map* map)
+ {
+ safeAssert(map !is null, "Invalid link_map.");
+ while (map.l_prev !is null)
+ map = map.l_prev;
+ return map;
+ }
+
+ DSO* dsoForHandle(void* handle)
+ {
+ DSO* pdso;
+ !pthread_mutex_lock(&_handleToDSOMutex) || assert(0);
+ if (auto ppdso = handle in _handleToDSO)
+ pdso = *ppdso;
+ !pthread_mutex_unlock(&_handleToDSOMutex) || assert(0);
+ return pdso;
+ }
+
+ void setDSOForHandle(DSO* pdso, void* handle)
+ {
+ !pthread_mutex_lock(&_handleToDSOMutex) || assert(0);
+ safeAssert(handle !in _handleToDSO, "DSO already registered.");
+ _handleToDSO[handle] = pdso;
+ !pthread_mutex_unlock(&_handleToDSOMutex) || assert(0);
+ }
+
+ void unsetDSOForHandle(DSO* pdso, void* handle)
+ {
+ !pthread_mutex_lock(&_handleToDSOMutex) || assert(0);
+ safeAssert(_handleToDSO[handle] == pdso, "Handle doesn't match registered DSO.");
+ _handleToDSO.remove(handle);
+ !pthread_mutex_unlock(&_handleToDSOMutex) || assert(0);
+ }
+
+ void getDependencies(in ref dl_phdr_info info, ref Array!(DSO*) deps)
+ {
+ // get the entries of the .dynamic section
+ ElfW!"Dyn"[] dyns;
+ foreach (ref phdr; info.dlpi_phdr[0 .. info.dlpi_phnum])
+ {
+ if (phdr.p_type == PT_DYNAMIC)
+ {
+ auto p = cast(ElfW!"Dyn"*)(info.dlpi_addr + (phdr.p_vaddr & ~(size_t.sizeof - 1)));
+ dyns = p[0 .. phdr.p_memsz / ElfW!"Dyn".sizeof];
+ break;
+ }
+ }
+ // find the string table which contains the sonames
+ const(char)* strtab;
+ foreach (dyn; dyns)
+ {
+ if (dyn.d_tag == DT_STRTAB)
+ {
+ version (CRuntime_Musl)
+ strtab = cast(const(char)*)(info.dlpi_addr + dyn.d_un.d_ptr); // relocate
+ else version (linux)
+ strtab = cast(const(char)*)dyn.d_un.d_ptr;
+ else version (FreeBSD)
+ strtab = cast(const(char)*)(info.dlpi_addr + dyn.d_un.d_ptr); // relocate
+ else version (NetBSD)
+ strtab = cast(const(char)*)(info.dlpi_addr + dyn.d_un.d_ptr); // relocate
+ else version (DragonFlyBSD)
+ strtab = cast(const(char)*)(info.dlpi_addr + dyn.d_un.d_ptr); // relocate
+ else
+ static assert(0, "unimplemented");
+ break;
+ }
+ }
+ foreach (dyn; dyns)
+ {
+ immutable tag = dyn.d_tag;
+ if (!(tag == DT_NEEDED || tag == DT_AUXILIARY || tag == DT_FILTER))
+ continue;
+
+ // soname of the dependency
+ auto name = strtab + dyn.d_un.d_val;
+ // get handle without loading the library
+ auto handle = handleForName(name);
+ // the runtime linker has already loaded all dependencies
+ safeAssert(handle !is null, "Failed to get library handle.");
+ // if it's a D library
+ if (auto pdso = dsoForHandle(handle))
+ deps.insertBack(pdso); // append it to the dependencies
+ }
+ }
+
+ void* handleForName(const char* name)
+ {
+ auto handle = .dlopen(name, RTLD_NOLOAD | RTLD_LAZY);
+ if (handle !is null) .dlclose(handle); // drop reference count
+ return handle;
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Elf program header iteration
+///////////////////////////////////////////////////////////////////////////////
+
+/************
+ * Scan segments in Linux dl_phdr_info struct and store
+ * the TLS and writeable data segments in *pdso.
+ */
+void scanSegments(in ref dl_phdr_info info, DSO* pdso) nothrow @nogc
+{
+ foreach (ref phdr; info.dlpi_phdr[0 .. info.dlpi_phnum])
+ {
+ switch (phdr.p_type)
+ {
+ case PT_LOAD:
+ if (phdr.p_flags & PF_W) // writeable data segment
+ {
+ auto beg = cast(void*)(info.dlpi_addr + (phdr.p_vaddr & ~(size_t.sizeof - 1)));
+ pdso._gcRanges.insertBack(beg[0 .. phdr.p_memsz]);
+ }
+ version (Shared) if (phdr.p_flags & PF_X) // code segment
+ {
+ auto beg = cast(void*)(info.dlpi_addr + (phdr.p_vaddr & ~(size_t.sizeof - 1)));
+ pdso._codeSegments.insertBack(beg[0 .. phdr.p_memsz]);
+ }
+ break;
+
+ case PT_TLS: // TLS segment
+ safeAssert(!pdso._tlsSize, "Multiple TLS segments in image header.");
+ pdso._tlsMod = info.dlpi_tls_modid;
+ pdso._tlsSize = phdr.p_memsz;
+ break;
+
+ default:
+ break;
+ }
+ }
+}
+
+/**************************
+ * Input:
+ * result where the output is to be written; dl_phdr_info is an OS struct
+ * Returns:
+ * true if found, and *result is filled in
+ * References:
+ * http://linux.die.net/man/3/dl_iterate_phdr
+ */
+bool findDSOInfoForAddr(in void* addr, dl_phdr_info* result=null) nothrow @nogc
+{
+ version (linux) enum IterateManually = true;
+ else version (NetBSD) enum IterateManually = true;
+ else enum IterateManually = false;
+
+ static if (IterateManually)
+ {
+ static struct DG { const(void)* addr; dl_phdr_info* result; }
+
+ extern(C) int callback(dl_phdr_info* info, size_t sz, void* arg) nothrow @nogc
+ {
+ auto p = cast(DG*)arg;
+ if (findSegmentForAddr(*info, p.addr))
+ {
+ if (p.result !is null) *p.result = *info;
+ return 1; // break;
+ }
+ return 0; // continue iteration
+ }
+
+ auto dg = DG(addr, result);
+
+ /* OS function that walks through the list of an application's shared objects and
+ * calls 'callback' once for each object, until either all shared objects
+ * have been processed or 'callback' returns a nonzero value.
+ */
+ return dl_iterate_phdr(&callback, &dg) != 0;
+ }
+ else version (FreeBSD)
+ {
+ return !!_rtld_addr_phdr(addr, result);
+ }
+ else version (DragonFlyBSD)
+ {
+ return !!_rtld_addr_phdr(addr, result);
+ }
+ else
+ static assert(0, "unimplemented");
+}
+
+/*********************************
+ * Determine if 'addr' lies within shared object 'info'.
+ * If so, return true and fill in 'result' with the corresponding ELF program header.
+ */
+bool findSegmentForAddr(in ref dl_phdr_info info, in void* addr, ElfW!"Phdr"* result=null) nothrow @nogc
+{
+ if (addr < cast(void*)info.dlpi_addr) // less than base address of object means quick reject
+ return false;
+
+ foreach (ref phdr; info.dlpi_phdr[0 .. info.dlpi_phnum])
+ {
+ auto beg = cast(void*)(info.dlpi_addr + phdr.p_vaddr);
+ if (cast(size_t)(addr - beg) < phdr.p_memsz)
+ {
+ if (result !is null) *result = phdr;
+ return true;
+ }
+ }
+ return false;
+}
+
+version (linux) import core.sys.linux.errno : program_invocation_name;
+// should be in core.sys.freebsd.stdlib
+version (FreeBSD) extern(C) const(char)* getprogname() nothrow @nogc;
+version (DragonFlyBSD) extern(C) const(char)* getprogname() nothrow @nogc;
+version (NetBSD) extern(C) const(char)* getprogname() nothrow @nogc;
+
+@property const(char)* progname() nothrow @nogc
+{
+ version (linux) return program_invocation_name;
+ version (FreeBSD) return getprogname();
+ version (DragonFlyBSD) return getprogname();
+ version (NetBSD) return getprogname();
+}
+
+const(char)[] dsoName(const char* dlpi_name) nothrow @nogc
+{
+ // the main executable doesn't have a name in its dlpi_name field
+ const char* p = dlpi_name[0] != 0 ? dlpi_name : progname;
+ return p[0 .. strlen(p)];
+}
+
+/**************************
+ * Input:
+ * addr an internal address of a DSO
+ * Returns:
+ * the dlopen handle for that DSO or null if addr is not within a loaded DSO
+ */
+version (Shared) void* handleForAddr(void* addr) nothrow @nogc
+{
+ Dl_info info = void;
+ if (dladdr(addr, &info) != 0)
+ return handleForName(info.dli_fname);
+ return null;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// TLS module helper
+///////////////////////////////////////////////////////////////////////////////
+
+
+/*
+ * Returns: the TLS memory range for a given module and the calling
+ * thread or null if that module has no TLS.
+ *
+ * Note: This will cause the TLS memory to be eagerly allocated.
+ */
+struct tls_index
+{
+ version (CRuntime_Glibc)
+ {
+ // For x86_64, fields are of type uint64_t, this is important for x32
+ // where tls_index would otherwise have the wrong size.
+ // See https://sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/x86_64/dl-tls.h
+ version (X86_64)
+ {
+ ulong ti_module;
+ ulong ti_offset;
+ }
+ else
+ {
+ c_ulong ti_module;
+ c_ulong ti_offset;
+ }
+ }
+ else
+ {
+ size_t ti_module;
+ size_t ti_offset;
+ }
+}
+
+extern(C) void* __tls_get_addr(tls_index* ti) nothrow @nogc;
+
+/* The dynamic thread vector (DTV) pointers may point 0x8000 past the start of
+ * each TLS block. This is at least true for PowerPC and Mips platforms.
+ * See: https://sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/powerpc/dl-tls.h;h=f7cf6f96ebfb505abfd2f02be0ad0e833107c0cd;hb=HEAD#l34
+ * https://sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/mips/dl-tls.h;h=93a6dc050cb144b9f68b96fb3199c60f5b1fcd18;hb=HEAD#l32
+ * https://sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/riscv/dl-tls.h;h=ab2d860314de94c18812bc894ff6b3f55368f20f;hb=HEAD#l32
+ */
+version (X86)
+ enum TLS_DTV_OFFSET = 0x0;
+else version (X86_64)
+ enum TLS_DTV_OFFSET = 0x0;
+else version (ARM)
+ enum TLS_DTV_OFFSET = 0x0;
+else version (AArch64)
+ enum TLS_DTV_OFFSET = 0x0;
+else version (RISCV32)
+ enum TLS_DTV_OFFSET = 0x800;
+else version (RISCV64)
+ enum TLS_DTV_OFFSET = 0x800;
+else version (HPPA)
+ enum TLS_DTV_OFFSET = 0x0;
+else version (SPARC)
+ enum TLS_DTV_OFFSET = 0x0;
+else version (SPARC64)
+ enum TLS_DTV_OFFSET = 0x0;
+else version (PPC)
+ enum TLS_DTV_OFFSET = 0x8000;
+else version (PPC64)
+ enum TLS_DTV_OFFSET = 0x8000;
+else version (MIPS32)
+ enum TLS_DTV_OFFSET = 0x8000;
+else version (MIPS64)
+ enum TLS_DTV_OFFSET = 0x8000;
+else
+ static assert( false, "Platform not supported." );
+
+void[] getTLSRange(size_t mod, size_t sz) nothrow @nogc
+{
+ if (mod == 0)
+ return null;
+
+ // base offset
+ auto ti = tls_index(mod, 0);
+ return (__tls_get_addr(&ti)-TLS_DTV_OFFSET)[0 .. sz];
+}
--- /dev/null
+// OSX-specific support for sections.
+// Copyright (C) 2019 Free Software Foundation, Inc.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+// for more details.
+
+// Under Section 7 of GPL version 3, you are granted additional
+// permissions described in the GCC Runtime Library Exception, version
+// 3.1, as published by the Free Software Foundation.
+
+// You should have received a copy of the GNU General Public License and
+// a copy of the GCC Runtime Library Exception along with this program;
+// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
+// <http://www.gnu.org/licenses/>.
+
+module gcc.sections.osx;
+
+version (OSX):
+
+// debug = PRINTF;
+import core.stdc.stdio;
+import core.stdc.string, core.stdc.stdlib;
+import core.sys.posix.pthread;
+import core.sys.darwin.mach.dyld;
+import core.sys.darwin.mach.getsect;
+import rt.deh, rt.minfo;
+import rt.util.container.array;
+
+struct SectionGroup
+{
+ static int opApply(scope int delegate(ref SectionGroup) dg)
+ {
+ return dg(_sections);
+ }
+
+ static int opApplyReverse(scope int delegate(ref SectionGroup) dg)
+ {
+ return dg(_sections);
+ }
+
+ @property immutable(ModuleInfo*)[] modules() const nothrow @nogc
+ {
+ return _moduleGroup.modules;
+ }
+
+ @property ref inout(ModuleGroup) moduleGroup() inout nothrow @nogc
+ {
+ return _moduleGroup;
+ }
+
+ @property inout(void[])[] gcRanges() inout nothrow @nogc
+ {
+ return _gcRanges[];
+ }
+
+ @property immutable(FuncTable)[] ehTables() const nothrow @nogc
+ {
+ return _ehTables[];
+ }
+
+private:
+ immutable(FuncTable)[] _ehTables;
+ ModuleGroup _moduleGroup;
+ Array!(void[]) _gcRanges;
+ immutable(void)[][2] _tlsImage;
+}
+
+/****
+ * Boolean flag set to true while the runtime is initialized.
+ */
+__gshared bool _isRuntimeInitialized;
+
+/****
+ * Gets called on program startup just before GC is initialized.
+ */
+void initSections() nothrow @nogc
+{
+ pthread_key_create(&_tlsKey, null);
+ _dyld_register_func_for_add_image(§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;
+}
--- /dev/null
+// Run-time support for retrieving platform-specific sections.
+// Copyright (C) 2019 Free Software Foundation, Inc.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+// for more details.
+
+// Under Section 7 of GPL version 3, you are granted additional
+// permissions described in the GCC Runtime Library Exception, version
+// 3.1, as published by the Free Software Foundation.
+
+// You should have received a copy of the GNU General Public License and
+// a copy of the GCC Runtime Library Exception along with this program;
+// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
+// <http://www.gnu.org/licenses/>.
+
+module gcc.sections;
+
+version (CRuntime_Glibc)
+ public import gcc.sections.elf_shared;
+else version (CRuntime_Musl)
+ public import gcc.sections.elf_shared;
+else version (CRuntime_UClibc)
+ public import gcc.sections.elf_shared;
+else version (FreeBSD)
+ public import gcc.sections.elf_shared;
+else version (NetBSD)
+ public import gcc.sections.elf_shared;
+else version (DragonFlyBSD)
+ public import gcc.sections.elf_shared;
+else version (Solaris)
+ public import gcc.sections.solaris;
+else version (OSX)
+ public import gcc.sections.osx;
+else version (CRuntime_DigitalMars)
+ public import gcc.sections.win32;
+else version (CRuntime_Microsoft)
+ public import gcc.sections.win64;
+else version (CRuntime_Bionic)
+ public import gcc.sections.android;
+else
+ static assert(0, "unimplemented");
--- /dev/null
+// Solaris-specific support for sections.
+// Copyright (C) 2019 Free Software Foundation, Inc.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+// for more details.
+
+// Under Section 7 of GPL version 3, you are granted additional
+// permissions described in the GCC Runtime Library Exception, version
+// 3.1, as published by the Free Software Foundation.
+
+// You should have received a copy of the GNU General Public License and
+// a copy of the GCC Runtime Library Exception along with this program;
+// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
+// <http://www.gnu.org/licenses/>.
+
+module gcc.sections.solaris;
+
+version (Solaris):
+
+// debug = PRINTF;
+debug(PRINTF) import core.stdc.stdio;
+import core.stdc.stdlib : malloc, free;
+import rt.deh, rt.minfo;
+
+struct SectionGroup
+{
+ static int opApply(scope int delegate(ref SectionGroup) dg)
+ {
+ return dg(_sections);
+ }
+
+ static int opApplyReverse(scope int delegate(ref SectionGroup) dg)
+ {
+ return dg(_sections);
+ }
+
+ @property immutable(ModuleInfo*)[] modules() const nothrow @nogc
+ {
+ return _moduleGroup.modules;
+ }
+
+ @property ref inout(ModuleGroup) moduleGroup() inout nothrow @nogc
+ {
+ return _moduleGroup;
+ }
+
+ @property immutable(FuncTable)[] ehTables() const nothrow @nogc
+ {
+ auto pbeg = cast(immutable(FuncTable)*)&__start_deh;
+ auto pend = cast(immutable(FuncTable)*)&__stop_deh;
+ return pbeg[0 .. pend - pbeg];
+ }
+
+ @property inout(void[])[] gcRanges() inout nothrow @nogc
+ {
+ return _gcRanges[];
+ }
+
+private:
+ ModuleGroup _moduleGroup;
+ void[][1] _gcRanges;
+}
+
+void initSections() nothrow @nogc
+{
+ auto mbeg = cast(immutable ModuleInfo**)&__start_minfo;
+ auto mend = cast(immutable ModuleInfo**)&__stop_minfo;
+ _sections.moduleGroup = ModuleGroup(mbeg[0 .. mend - mbeg]);
+
+ auto pbeg = cast(void*)&__dso_handle;
+ auto pend = cast(void*)&_end;
+ _sections._gcRanges[0] = pbeg[0 .. pend - pbeg];
+}
+
+void finiSections() nothrow @nogc
+{
+}
+
+void[] initTLSRanges() nothrow @nogc
+{
+ auto pbeg = cast(void*)&_tlsstart;
+ auto pend = cast(void*)&_tlsend;
+ return pbeg[0 .. pend - pbeg];
+}
+
+void finiTLSRanges(void[] rng) nothrow @nogc
+{
+}
+
+void scanTLSRanges(void[] rng, scope void delegate(void* pbeg, void* pend) nothrow dg) nothrow
+{
+ dg(rng.ptr, rng.ptr + rng.length);
+}
+
+private:
+
+__gshared SectionGroup _sections;
+
+extern(C)
+{
+ /* Symbols created by the compiler/linker and inserted into the
+ * object file that 'bracket' sections.
+ */
+ extern __gshared
+ {
+ void* __start_deh;
+ void* __stop_deh;
+ void* __start_minfo;
+ void* __stop_minfo;
+ int __dso_handle;
+ int _end;
+ }
+
+ extern
+ {
+ void* _tlsstart;
+ void* _tlsend;
+ }
+}
--- /dev/null
+// Win32-specific support for sections.
+// Copyright (C) 2019 Free Software Foundation, Inc.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+// for more details.
+
+// Under Section 7 of GPL version 3, you are granted additional
+// permissions described in the GCC Runtime Library Exception, version
+// 3.1, as published by the Free Software Foundation.
+
+// You should have received a copy of the GNU General Public License and
+// a copy of the GCC Runtime Library Exception along with this program;
+// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
+// <http://www.gnu.org/licenses/>.
+
+module gcc.sections.win32;
+
+version (CRuntime_DigitalMars):
+
+// debug = PRINTF;
+debug(PRINTF) import core.stdc.stdio;
+import rt.minfo;
+import core.stdc.stdlib : malloc, free;
+
+struct SectionGroup
+{
+ static int opApply(scope int delegate(ref SectionGroup) dg)
+ {
+ return dg(_sections);
+ }
+
+ static int opApplyReverse(scope int delegate(ref SectionGroup) dg)
+ {
+ return dg(_sections);
+ }
+
+ @property immutable(ModuleInfo*)[] modules() const nothrow @nogc
+ {
+ return _moduleGroup.modules;
+ }
+
+ @property ref inout(ModuleGroup) moduleGroup() inout nothrow @nogc
+ {
+ return _moduleGroup;
+ }
+
+ @property inout(void[])[] gcRanges() inout nothrow @nogc
+ {
+ return _gcRanges[];
+ }
+
+private:
+ ModuleGroup _moduleGroup;
+ void[][] _gcRanges;
+}
+
+shared(bool) conservative;
+
+void initSections() nothrow @nogc
+{
+ _sections._moduleGroup = ModuleGroup(getModuleInfos());
+
+ import rt.sections;
+ conservative = !scanDataSegPrecisely();
+
+ if (conservative)
+ {
+ _sections._gcRanges = (cast(void[]*) malloc(2 * (void[]).sizeof))[0..2];
+
+ auto databeg = cast(void*)&_xi_a;
+ auto dataend = cast(void*)_moduleinfo_array.ptr;
+ _sections._gcRanges[0] = databeg[0 .. dataend - databeg];
+
+ // skip module info and CONST segment
+ auto bssbeg = cast(void*)&_edata;
+ auto bssend = cast(void*)&_end;
+ _sections._gcRanges[1] = bssbeg[0 .. bssend - bssbeg];
+ }
+ else
+ {
+ size_t count = &_DPend - &_DPbegin;
+ auto ranges = cast(void[]*) malloc(count * (void[]).sizeof);
+ size_t r = 0;
+ void* prev = null;
+ for (size_t i = 0; i < count; i++)
+ {
+ void* addr = (&_DPbegin)[i];
+ if (prev + (void*).sizeof == addr)
+ ranges[r-1] = ranges[r-1].ptr[0 .. ranges[r-1].length + (void*).sizeof];
+ else
+ ranges[r++] = (cast(void**)addr)[0..1];
+ prev = addr;
+ }
+ _sections._gcRanges = ranges[0..r];
+ }
+}
+
+void finiSections() nothrow @nogc
+{
+ free(_sections._gcRanges.ptr);
+}
+
+void[] initTLSRanges() nothrow @nogc
+{
+ auto pbeg = cast(void*)&_tlsstart;
+ auto pend = cast(void*)&_tlsend;
+ return pbeg[0 .. pend - pbeg];
+}
+
+void finiTLSRanges(void[] rng) nothrow @nogc
+{
+}
+
+void scanTLSRanges(void[] rng, scope void delegate(void* pbeg, void* pend) nothrow dg) nothrow
+{
+ if (conservative)
+ {
+ dg(rng.ptr, rng.ptr + rng.length);
+ }
+ else
+ {
+ for (auto p = &_TPbegin; p < &_TPend; )
+ {
+ uint beg = *p++;
+ uint end = beg + cast(uint)((void*).sizeof);
+ while (p < &_TPend && *p == end)
+ {
+ end += (void*).sizeof;
+ p++;
+ }
+ dg(rng.ptr + beg, rng.ptr + end);
+ }
+ }
+}
+
+private:
+
+__gshared SectionGroup _sections;
+
+// Windows: this gets initialized by minit.asm
+extern(C) __gshared immutable(ModuleInfo*)[] _moduleinfo_array;
+extern(C) void _minit() nothrow @nogc;
+
+immutable(ModuleInfo*)[] getModuleInfos() nothrow @nogc
+out (result)
+{
+ foreach (m; result)
+ assert(m !is null);
+}
+body
+{
+ // _minit directly alters the global _moduleinfo_array
+ _minit();
+ return _moduleinfo_array;
+}
+
+extern(C)
+{
+ extern __gshared
+ {
+ int _xi_a; // &_xi_a just happens to be start of data segment
+ int _edata; // &_edata is start of BSS segment
+ int _end; // &_end is past end of BSS
+
+ void* _DPbegin; // first entry in the array of pointers addresses
+ void* _DPend; // &_DPend points after last entry of array
+ uint _TPbegin; // first entry in the array of TLS offsets of pointers
+ uint _TPend; // &_DPend points after last entry of array
+ }
+
+ extern
+ {
+ int _tlsstart;
+ int _tlsend;
+ }
+}
--- /dev/null
+// Win64-specific support for sections.
+// Copyright (C) 2019 Free Software Foundation, Inc.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+// for more details.
+
+// Under Section 7 of GPL version 3, you are granted additional
+// permissions described in the GCC Runtime Library Exception, version
+// 3.1, as published by the Free Software Foundation.
+
+// You should have received a copy of the GNU General Public License and
+// a copy of the GCC Runtime Library Exception along with this program;
+// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
+// <http://www.gnu.org/licenses/>.
+
+module gcc.sections.win64;
+
+version (CRuntime_Microsoft):
+
+// debug = PRINTF;
+debug(PRINTF) import core.stdc.stdio;
+import core.stdc.stdlib : malloc, free;
+import rt.deh, rt.minfo;
+
+struct SectionGroup
+{
+ static int opApply(scope int delegate(ref SectionGroup) dg)
+ {
+ return dg(_sections);
+ }
+
+ static int opApplyReverse(scope int delegate(ref SectionGroup) dg)
+ {
+ return dg(_sections);
+ }
+
+ @property immutable(ModuleInfo*)[] modules() const nothrow @nogc
+ {
+ return _moduleGroup.modules;
+ }
+
+ @property ref inout(ModuleGroup) moduleGroup() inout nothrow @nogc
+ {
+ return _moduleGroup;
+ }
+
+ version (Win64)
+ @property immutable(FuncTable)[] ehTables() const nothrow @nogc
+ {
+ auto pbeg = cast(immutable(FuncTable)*)&_deh_beg;
+ auto pend = cast(immutable(FuncTable)*)&_deh_end;
+ return pbeg[0 .. pend - pbeg];
+ }
+
+ @property inout(void[])[] gcRanges() inout nothrow @nogc
+ {
+ return _gcRanges[];
+ }
+
+private:
+ ModuleGroup _moduleGroup;
+ void[][] _gcRanges;
+}
+
+shared(bool) conservative;
+
+void initSections() nothrow @nogc
+{
+ _sections._moduleGroup = ModuleGroup(getModuleInfos());
+
+ // the ".data" image section includes both object file sections ".data" and ".bss"
+ void[] dataSection = findImageSection(".data");
+ debug(PRINTF) printf("found .data section: [%p,+%llx]\n", dataSection.ptr,
+ cast(ulong)dataSection.length);
+
+ import rt.sections;
+ conservative = !scanDataSegPrecisely();
+
+ if (conservative)
+ {
+ _sections._gcRanges = (cast(void[]*) malloc((void[]).sizeof))[0..1];
+ _sections._gcRanges[0] = dataSection;
+ }
+ else
+ {
+ size_t count = &_DP_end - &_DP_beg;
+ auto ranges = cast(void[]*) malloc(count * (void[]).sizeof);
+ size_t r = 0;
+ void* prev = null;
+ for (size_t i = 0; i < count; i++)
+ {
+ auto off = (&_DP_beg)[i];
+ if (off == 0) // skip zero entries added by incremental linking
+ continue; // assumes there is no D-pointer at the very beginning of .data
+ void* addr = dataSection.ptr + off;
+ debug(PRINTF) printf(" scan %p\n", addr);
+ // combine consecutive pointers into single range
+ if (prev + (void*).sizeof == addr)
+ ranges[r-1] = ranges[r-1].ptr[0 .. ranges[r-1].length + (void*).sizeof];
+ else
+ ranges[r++] = (cast(void**)addr)[0..1];
+ prev = addr;
+ }
+ _sections._gcRanges = ranges[0..r];
+ }
+}
+
+void finiSections() nothrow @nogc
+{
+ .free(cast(void*)_sections.modules.ptr);
+ .free(_sections._gcRanges.ptr);
+}
+
+void[] initTLSRanges() nothrow @nogc
+{
+ void* pbeg;
+ void* pend;
+ // with VS2017 15.3.1, the linker no longer puts TLS segments into a
+ // separate image section. That way _tls_start and _tls_end no
+ // longer generate offsets into .tls, but DATA.
+ // Use the TEB entry to find the start of TLS instead and read the
+ // length from the TLS directory
+ version (D_InlineAsm_X86)
+ {
+ asm @nogc nothrow
+ {
+ mov EAX, _tls_index;
+ mov ECX, FS:[0x2C]; // _tls_array
+ mov EAX, [ECX+4*EAX];
+ mov pbeg, EAX;
+ add EAX, [_tls_used+4]; // end
+ sub EAX, [_tls_used+0]; // start
+ mov pend, EAX;
+ }
+ }
+ else version (D_InlineAsm_X86_64)
+ {
+ asm @nogc nothrow
+ {
+ xor RAX, RAX;
+ mov EAX, _tls_index;
+ mov RCX, 0x58;
+ mov RCX, GS:[RCX]; // _tls_array (immediate value causes fixup)
+ mov RAX, [RCX+8*RAX];
+ mov pbeg, RAX;
+ add RAX, [_tls_used+8]; // end
+ sub RAX, [_tls_used+0]; // start
+ mov pend, RAX;
+ }
+ }
+ else
+ static assert(false, "Architecture not supported.");
+
+ return pbeg[0 .. pend - pbeg];
+}
+
+void finiTLSRanges(void[] rng) nothrow @nogc
+{
+}
+
+void scanTLSRanges(void[] rng, scope void delegate(void* pbeg, void* pend) nothrow dg) nothrow
+{
+ if (conservative)
+ {
+ dg(rng.ptr, rng.ptr + rng.length);
+ }
+ else
+ {
+ for (auto p = &_TP_beg; p < &_TP_end; )
+ {
+ uint beg = *p++;
+ uint end = beg + cast(uint)((void*).sizeof);
+ while (p < &_TP_end && *p == end)
+ {
+ end += (void*).sizeof;
+ p++;
+ }
+ dg(rng.ptr + beg, rng.ptr + end);
+ }
+ }
+}
+
+private:
+__gshared SectionGroup _sections;
+
+extern(C)
+{
+ extern __gshared void* _minfo_beg;
+ extern __gshared void* _minfo_end;
+}
+
+immutable(ModuleInfo*)[] getModuleInfos() nothrow @nogc
+out (result)
+{
+ foreach (m; result)
+ assert(m !is null);
+}
+body
+{
+ auto m = (cast(immutable(ModuleInfo*)*)&_minfo_beg)[1 .. &_minfo_end - &_minfo_beg];
+ /* Because of alignment inserted by the linker, various null pointers
+ * are there. We need to filter them out.
+ */
+ auto p = m.ptr;
+ auto pend = m.ptr + m.length;
+
+ // count non-null pointers
+ size_t cnt;
+ for (; p < pend; ++p)
+ {
+ if (*p !is null) ++cnt;
+ }
+
+ auto result = (cast(immutable(ModuleInfo)**).malloc(cnt * size_t.sizeof))[0 .. cnt];
+
+ p = m.ptr;
+ cnt = 0;
+ for (; p < pend; ++p)
+ if (*p !is null) result[cnt++] = *p;
+
+ return cast(immutable)result;
+}
+
+extern(C)
+{
+ /* Symbols created by the compiler/linker and inserted into the
+ * object file that 'bracket' sections.
+ */
+ extern __gshared
+ {
+ void* __ImageBase;
+
+ void* _deh_beg;
+ void* _deh_end;
+
+ uint _DP_beg;
+ uint _DP_end;
+ uint _TP_beg;
+ uint _TP_end;
+
+ void*[2] _tls_used; // start, end
+ int _tls_index;
+ }
+}
+
+/////////////////////////////////////////////////////////////////////
+
+enum IMAGE_DOS_SIGNATURE = 0x5A4D; // MZ
+
+struct IMAGE_DOS_HEADER // DOS .EXE header
+{
+ ushort e_magic; // Magic number
+ ushort[29] e_res2; // Reserved ushorts
+ int e_lfanew; // File address of new exe header
+}
+
+struct IMAGE_FILE_HEADER
+{
+ ushort Machine;
+ ushort NumberOfSections;
+ uint TimeDateStamp;
+ uint PointerToSymbolTable;
+ uint NumberOfSymbols;
+ ushort SizeOfOptionalHeader;
+ ushort Characteristics;
+}
+
+struct IMAGE_NT_HEADERS
+{
+ uint Signature;
+ IMAGE_FILE_HEADER FileHeader;
+ // optional header follows
+}
+
+struct IMAGE_SECTION_HEADER
+{
+ char[8] Name = 0;
+ union {
+ uint PhysicalAddress;
+ uint VirtualSize;
+ }
+ uint VirtualAddress;
+ uint SizeOfRawData;
+ uint PointerToRawData;
+ uint PointerToRelocations;
+ uint PointerToLinenumbers;
+ ushort NumberOfRelocations;
+ ushort NumberOfLinenumbers;
+ uint Characteristics;
+}
+
+bool compareSectionName(ref IMAGE_SECTION_HEADER section, string name) nothrow @nogc
+{
+ if (name[] != section.Name[0 .. name.length])
+ return false;
+ return name.length == 8 || section.Name[name.length] == 0;
+}
+
+void[] findImageSection(string name) nothrow @nogc
+{
+ if (name.length > 8) // section name from string table not supported
+ return null;
+ IMAGE_DOS_HEADER* doshdr = cast(IMAGE_DOS_HEADER*) &__ImageBase;
+ if (doshdr.e_magic != IMAGE_DOS_SIGNATURE)
+ return null;
+
+ auto nthdr = cast(IMAGE_NT_HEADERS*)(cast(void*)doshdr + doshdr.e_lfanew);
+ auto sections = cast(IMAGE_SECTION_HEADER*)(cast(void*)nthdr + IMAGE_NT_HEADERS.sizeof + nthdr.FileHeader.SizeOfOptionalHeader);
+ for (ushort i = 0; i < nthdr.FileHeader.NumberOfSections; i++)
+ if (compareSectionName (sections[i], name))
+ return (cast(void*)&__ImageBase + sections[i].VirtualAddress)[0 .. sections[i].VirtualSize];
+
+ return null;
+}
+++ /dev/null
-/**
- * This module is used to detect copy relocated ModuleInfos (located in .bss section).
- *
- * Copyright: Copyright Martin Nowak 2014-.
- * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
- * Authors: Martin Nowak
- * Source: $(DRUNTIMESRC src/rt/_bss_section.c)
- */
-
-/* These symbols are defined in the linker script and bracket the
- * .bss, .lbss, .lrodata and .ldata sections.
- */
-#if defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__)
-// Need to use weak linkage to workaround a bug in ld.bfd (Bugzilla 13025).
-extern int __attribute__((weak)) __bss_start, _end;
-
-__attribute__ ((visibility ("hidden"))) void* rt_get_bss_start();
-__attribute__ ((visibility ("hidden"))) void* rt_get_end();
-void* rt_get_bss_start() { return (void*)&__bss_start; }
-void* rt_get_end() { return (void*)&_end; }
-#endif
* Source: $(DRUNTIMESRC src/rt/_sections.d)
*/
+/* NOTE: This file has been patched from the original DMD distribution to
+ * work with the GDC compiler.
+ */
module rt.sections;
version (OSX)
else version (WatchOS)
version = Darwin;
-version (CRuntime_Glibc)
+version (GNU)
+ public import gcc.sections;
+else version (CRuntime_Glibc)
public import rt.sections_elf_shared;
else version (FreeBSD)
public import rt.sections_elf_shared;
+++ /dev/null
-/**
- * Written in the D programming language.
- * This module provides bionic-specific support for sections.
- *
- * Copyright: Copyright Martin Nowak 2012-2013.
- * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
- * Authors: Martin Nowak
- * Source: $(DRUNTIMESRC src/rt/_sections_android.d)
- */
-
-module rt.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__;
- _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.
- */
-
-version (X86)
-{
- // NB: the compiler mangles this function as '___tls_get_addr'
- // even though it is extern(D)
- extern(D) 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;
- }
-}
-else version (ARM)
-{
- 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;
- }
-}
-else
- static assert( false, "Android architecture not supported." );
-
-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__;
-
- void* _tlsstart;
- void* _tlsend;
- }
-}
+++ /dev/null
-/**
- * Written in the D programming language.
- * This module provides ELF-specific support for sections with shared libraries.
- *
- * Copyright: Copyright Martin Nowak 2012-2013.
- * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
- * Authors: Martin Nowak
- * Source: $(DRUNTIMESRC src/rt/_sections_linux.d)
- */
-
-module rt.sections_elf_shared;
-
-version (CRuntime_Glibc) enum SharedELF = true;
-else version (FreeBSD) enum SharedELF = true;
-else version (NetBSD) enum SharedELF = true;
-else enum SharedELF = false;
-static if (SharedELF):
-
-// debug = PRINTF;
-import core.memory;
-import core.stdc.config;
-import core.stdc.stdio;
-import core.stdc.stdlib : calloc, exit, free, malloc, EXIT_FAILURE;
-import core.stdc.string : strlen;
-version (linux)
-{
- import core.sys.linux.dlfcn;
- import core.sys.linux.elf;
- import core.sys.linux.link;
-}
-else version (FreeBSD)
-{
- import core.sys.freebsd.dlfcn;
- import core.sys.freebsd.sys.elf;
- import core.sys.freebsd.sys.link_elf;
-}
-else version (NetBSD)
-{
- import core.sys.netbsd.dlfcn;
- import core.sys.netbsd.sys.elf;
- import core.sys.netbsd.sys.link_elf;
-}
-else
-{
- static assert(0, "unimplemented");
-}
-import core.sys.posix.pthread;
-import rt.deh;
-import rt.dmain2;
-import rt.minfo;
-import rt.util.container.array;
-import rt.util.container.hashtab;
-
-alias DSO SectionGroup;
-struct DSO
-{
- static int opApply(scope int delegate(ref DSO) dg)
- {
- foreach (dso; _loadedDSOs)
- {
- if (auto res = dg(*dso))
- return res;
- }
- return 0;
- }
-
- static int opApplyReverse(scope int delegate(ref DSO) dg)
- {
- foreach_reverse (dso; _loadedDSOs)
- {
- if (auto res = dg(*dso))
- return res;
- }
- return 0;
- }
-
- @property immutable(ModuleInfo*)[] modules() const nothrow @nogc
- {
- return _moduleGroup.modules;
- }
-
- @property ref inout(ModuleGroup) moduleGroup() inout nothrow @nogc
- {
- return _moduleGroup;
- }
-
- @property immutable(FuncTable)[] ehTables() const nothrow @nogc
- {
- return null;
- }
-
- @property inout(void[])[] gcRanges() inout nothrow @nogc
- {
- return _gcRanges[];
- }
-
-private:
-
- invariant()
- {
- assert(_moduleGroup.modules.length);
- assert(_tlsMod || !_tlsSize);
- }
-
- ModuleGroup _moduleGroup;
- Array!(void[]) _gcRanges;
- size_t _tlsMod;
- size_t _tlsSize;
-
- version (Shared)
- {
- Array!(void[]) _codeSegments; // array of code segments
- Array!(DSO*) _deps; // D libraries needed by this DSO
- void* _handle; // corresponding handle
- }
-}
-
-/****
- * Boolean flag set to true while the runtime is initialized.
- */
-__gshared bool _isRuntimeInitialized;
-
-
-version (FreeBSD) private __gshared void* dummy_ref;
-version (NetBSD) private __gshared void* dummy_ref;
-
-/****
- * Gets called on program startup just before GC is initialized.
- */
-void initSections() nothrow @nogc
-{
- _isRuntimeInitialized = true;
- // reference symbol to support weak linkage
- version (FreeBSD) dummy_ref = &_d_dso_registry;
- version (NetBSD) dummy_ref = &_d_dso_registry;
-}
-
-
-/***
- * Gets called on program shutdown just after GC is terminated.
- */
-void finiSections() nothrow @nogc
-{
- _isRuntimeInitialized = false;
-}
-
-alias ScanDG = void delegate(void* pbeg, void* pend) nothrow;
-
-version (Shared)
-{
- /***
- * Called once per thread; returns array of thread local storage ranges
- */
- Array!(ThreadDSO)* initTLSRanges() @nogc nothrow
- {
- return &_loadedDSOs;
- }
-
- void finiTLSRanges(Array!(ThreadDSO)* tdsos) @nogc nothrow
- {
- // Nothing to do here. tdsos used to point to the _loadedDSOs instance
- // in the dying thread's TLS segment and as such is not valid anymore.
- // The memory for the array contents was already reclaimed in
- // cleanupLoadedLibraries().
- }
-
- void scanTLSRanges(Array!(ThreadDSO)* tdsos, scope ScanDG dg) nothrow
- {
- foreach (ref tdso; *tdsos)
- dg(tdso._tlsRange.ptr, tdso._tlsRange.ptr + tdso._tlsRange.length);
- }
-
- // interface for core.thread to inherit loaded libraries
- void* pinLoadedLibraries() nothrow @nogc
- {
- auto res = cast(Array!(ThreadDSO)*)calloc(1, Array!(ThreadDSO).sizeof);
- res.length = _loadedDSOs.length;
- foreach (i, ref tdso; _loadedDSOs)
- {
- (*res)[i] = tdso;
- if (tdso._addCnt)
- {
- // Increment the dlopen ref for explicitly loaded libraries to pin them.
- .dlopen(linkMapForHandle(tdso._pdso._handle).l_name, RTLD_LAZY) !is null || assert(0);
- (*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;
- handle !is null || assert(0);
- .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
- {
- assert(_loadedDSOs.empty);
- _loadedDSOs.swap(*cast(Array!(ThreadDSO)*)p);
- .free(p);
- foreach (ref dso; _loadedDSOs)
- {
- // the copied _tlsRange corresponds to parent thread
- dso.updateTLSRange();
- }
- }
-
- // Called after all TLS dtors ran, decrements all remaining dlopen refs.
- void cleanupLoadedLibraries() nothrow @nogc
- {
- foreach (ref tdso; _loadedDSOs)
- {
- if (tdso._addCnt == 0) continue;
-
- auto handle = tdso._pdso._handle;
- handle !is null || assert(0);
- for (; tdso._addCnt > 0; --tdso._addCnt)
- .dlclose(handle);
- }
-
- // Free the memory for the array contents.
- _loadedDSOs.reset();
- }
-}
-else
-{
- /***
- * Called once per thread; returns array of thread local storage ranges
- */
- Array!(void[])* initTLSRanges() nothrow @nogc
- {
- return &_tlsRanges;
- }
-
- void finiTLSRanges(Array!(void[])* rngs) nothrow @nogc
- {
- rngs.reset();
- }
-
- void scanTLSRanges(Array!(void[])* rngs, scope ScanDG dg) nothrow
- {
- foreach (rng; *rngs)
- dg(rng.ptr, rng.ptr + rng.length);
- }
-}
-
-private:
-
-// start of linked list for ModuleInfo references
-version (FreeBSD) deprecated extern (C) __gshared void* _Dmodule_ref;
-version (NetBSD) deprecated extern (C) __gshared void* _Dmodule_ref;
-
-version (Shared)
-{
- /*
- * Array of thread local DSO metadata for all libraries loaded and
- * initialized in this thread.
- *
- * Note:
- * A newly spawned thread will inherit these libraries.
- * Note:
- * We use an array here to preserve the order of
- * initialization. If that became a performance issue, we
- * could use a hash table and enumerate the DSOs during
- * loading so that the hash table values could be sorted when
- * necessary.
- */
- struct ThreadDSO
- {
- DSO* _pdso;
- static if (_pdso.sizeof == 8) uint _refCnt, _addCnt;
- else static if (_pdso.sizeof == 4) ushort _refCnt, _addCnt;
- else static assert(0, "unimplemented");
- void[] _tlsRange;
- alias _pdso this;
- // update the _tlsRange for the executing thread
- void updateTLSRange() nothrow @nogc
- {
- _tlsRange = getTLSRange(_pdso._tlsMod, _pdso._tlsSize);
- }
- }
- Array!(ThreadDSO) _loadedDSOs;
-
- /*
- * Set to true during rt_loadLibrary/rt_unloadLibrary calls.
- */
- bool _rtLoading;
-
- /*
- * Hash table to map link_map* to corresponding DSO*.
- * The hash table is protected by a Mutex.
- */
- __gshared pthread_mutex_t _handleToDSOMutex;
- __gshared HashTab!(void*, DSO*) _handleToDSO;
-
- /*
- * Section in executable that contains copy relocations.
- * Might be null when druntime is dynamically loaded by a C host.
- */
- __gshared const(void)[] _copyRelocSection;
-}
-else
-{
- /*
- * Static DSOs loaded by the runtime linker. This includes the
- * executable. These can't be unloaded.
- */
- __gshared Array!(DSO*) _loadedDSOs;
-
- /*
- * Thread local array that contains TLS memory ranges for each
- * library initialized in this thread.
- */
- Array!(void[]) _tlsRanges;
-
- enum _rtLoading = false;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Compiler to runtime interface.
-///////////////////////////////////////////////////////////////////////////////
-
-import gcc.config;
-
-/*
- * 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
- data._version >= 1 || assert(0, "corrupt DSO data version");
-
- // no backlink => register
- if (*data._slot is null)
- {
- immutable firstDSO = _loadedDSOs.empty;
- if (firstDSO) initLocks();
-
- DSO* pdso = cast(DSO*).calloc(1, DSO.sizeof);
- assert(typeid(DSO).initializer().ptr is null);
- *data._slot = pdso; // store backlink in library record
-
- pdso._moduleGroup = ModuleGroup(toRange(data._minfo_beg, data._minfo_end));
-
- dl_phdr_info info = void;
- findDSOInfoForAddr(data._slot, &info) || assert(0);
-
- scanSegments(info, pdso);
-
- version (Shared)
- {
- auto handle = handleForAddr(data._slot);
-
- if (firstDSO)
- {
- /// Assert that the first loaded DSO is druntime itself. Use a
- /// local druntime symbol (rt_get_bss_start) to get the handle.
- assert(handleForAddr(data._slot) == handleForAddr(&rt_get_bss_start));
- _copyRelocSection = getCopyRelocSection();
- }
- checkModuleCollisions(info, pdso._moduleGroup.modules, _copyRelocSection);
-
- getDependencies(info, pdso._deps);
- pdso._handle = handle;
- setDSOForHandle(pdso, pdso._handle);
-
- if (!_rtLoading)
- {
- /* This DSO was not loaded by rt_loadLibrary which
- * happens for all dependencies of an executable or
- * the first dlopen call from a C program.
- * In this case we add the DSO to the _loadedDSOs of this
- * thread with a refCnt of 1 and call the TlsCtors.
- */
- immutable ushort refCnt = 1, addCnt = 0;
- auto tlsRng = getTLSRange(pdso._tlsMod, pdso._tlsSize);
- _loadedDSOs.insertBack(ThreadDSO(pdso, refCnt, addCnt, tlsRng));
- }
- }
- else
- {
- foreach (p; _loadedDSOs) assert(p !is pdso);
- _loadedDSOs.insertBack(pdso);
- _tlsRanges.insertBack(getTLSRange(pdso._tlsMod, pdso._tlsSize));
- }
-
- // don't initialize modules before rt_init was called (see Bugzilla 11378)
- if (_isRuntimeInitialized)
- {
- registerGCRanges(pdso);
- // rt_loadLibrary will run tls ctors, so do this only for dlopen
- immutable runTlsCtors = !_rtLoading;
- runModuleConstructors(pdso, runTlsCtors);
- }
- }
- // has backlink => unregister
- else
- {
- DSO* pdso = cast(DSO*)*data._slot;
- *data._slot = null;
-
- // don't finalizes modules after rt_term was called (see Bugzilla 11378)
- if (_isRuntimeInitialized)
- {
- // rt_unloadLibrary already ran tls dtors, so do this only for dlclose
- immutable runTlsDtors = !_rtLoading;
- runModuleDestructors(pdso, runTlsDtors);
- unregisterGCRanges(pdso);
- // run finalizers after module dtors (same order as in rt_term)
- version (Shared) runFinalizers(pdso);
- }
-
- version (Shared)
- {
- if (!_rtLoading)
- {
- /* This DSO was not unloaded by rt_unloadLibrary so we
- * have to remove it from _loadedDSOs here.
- */
- foreach (i, ref tdso; _loadedDSOs)
- {
- if (tdso._pdso == pdso)
- {
- _loadedDSOs.remove(i);
- break;
- }
- }
- }
-
- unsetDSOForHandle(pdso, pdso._handle);
- pdso._handle = null;
- }
- else
- {
- // static DSOs are unloaded in reverse order
- assert(pdso._tlsSize == _tlsRanges.back.length);
- _tlsRanges.popBack();
- assert(pdso == _loadedDSOs.back);
- _loadedDSOs.popBack();
- }
-
- freeDSO(pdso);
-
- if (_loadedDSOs.empty) finiLocks(); // last DSO
- }
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// 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;
- auto tlsRng = getTLSRange(pdso._tlsMod, pdso._tlsSize);
- _loadedDSOs.insertBack(ThreadDSO(pdso, refCnt, addCnt, tlsRng));
- pdso._moduleGroup.runTlsCtors();
- }
- }
-
- void decThreadRef(DSO* pdso, bool decAdd)
- {
- auto tdata = findThreadDSO(pdso);
- tdata !is null || assert(0);
- !decAdd || tdata._addCnt > 0 || assert(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();
- .free(pdso);
-}
-
-version (Shared)
-{
-@nogc nothrow:
- link_map* linkMapForHandle(void* handle) nothrow @nogc
- {
- link_map* map;
- dlinfo(handle, RTLD_DI_LINKMAP, &map) == 0 || assert(0);
- return map;
- }
-
- link_map* exeLinkMap(link_map* map) nothrow @nogc
- {
- assert(map);
- while (map.l_prev !is null)
- map = map.l_prev;
- return map;
- }
-
- DSO* dsoForHandle(void* handle) nothrow @nogc
- {
- 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) nothrow @nogc
- {
- !pthread_mutex_lock(&_handleToDSOMutex) || assert(0);
- assert(handle !in _handleToDSO);
- _handleToDSO[handle] = pdso;
- !pthread_mutex_unlock(&_handleToDSOMutex) || assert(0);
- }
-
- void unsetDSOForHandle(DSO* pdso, void* handle) nothrow @nogc
- {
- !pthread_mutex_lock(&_handleToDSOMutex) || assert(0);
- assert(_handleToDSO[handle] == pdso);
- _handleToDSO.remove(handle);
- !pthread_mutex_unlock(&_handleToDSOMutex) || assert(0);
- }
-
- void getDependencies(in ref dl_phdr_info info, ref Array!(DSO*) deps) nothrow @nogc
- {
- // get the entries of the .dynamic section
- ElfW!"Dyn"[] dyns;
- foreach (ref phdr; info.dlpi_phdr[0 .. info.dlpi_phnum])
- {
- if (phdr.p_type == PT_DYNAMIC)
- {
- auto p = cast(ElfW!"Dyn"*)(info.dlpi_addr + (phdr.p_vaddr & ~(size_t.sizeof - 1)));
- dyns = p[0 .. phdr.p_memsz / ElfW!"Dyn".sizeof];
- break;
- }
- }
- // find the string table which contains the sonames
- const(char)* strtab;
- foreach (dyn; dyns)
- {
- if (dyn.d_tag == DT_STRTAB)
- {
- version (linux)
- strtab = cast(const(char)*)dyn.d_un.d_ptr;
- else version (FreeBSD)
- strtab = cast(const(char)*)(info.dlpi_addr + dyn.d_un.d_ptr); // relocate
- else version (NetBSD)
- strtab = cast(const(char)*)(info.dlpi_addr + dyn.d_un.d_ptr); // relocate
- else
- static assert(0, "unimplemented");
- break;
- }
- }
- foreach (dyn; dyns)
- {
- immutable tag = dyn.d_tag;
- if (!(tag == DT_NEEDED || tag == DT_AUXILIARY || tag == DT_FILTER))
- continue;
-
- // soname of the dependency
- auto name = strtab + dyn.d_un.d_val;
- // get handle without loading the library
- auto handle = handleForName(name);
- // the runtime linker has already loaded all dependencies
- if (handle is null) assert(0);
- // if it's a D library
- if (auto pdso = dsoForHandle(handle))
- deps.insertBack(pdso); // append it to the dependencies
- }
- }
-
- void* handleForName(const char* name) nothrow @nogc
- {
- auto handle = .dlopen(name, RTLD_NOLOAD | RTLD_LAZY);
- if (handle !is null) .dlclose(handle); // drop reference count
- return handle;
- }
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Elf program header iteration
-///////////////////////////////////////////////////////////////////////////////
-
-/************
- * Scan segments in Linux dl_phdr_info struct and store
- * the TLS and writeable data segments in *pdso.
- */
-void scanSegments(in ref dl_phdr_info info, DSO* pdso) nothrow @nogc
-{
- foreach (ref phdr; info.dlpi_phdr[0 .. info.dlpi_phnum])
- {
- switch (phdr.p_type)
- {
- case PT_LOAD:
- if (phdr.p_flags & PF_W) // writeable data segment
- {
- auto beg = cast(void*)(info.dlpi_addr + (phdr.p_vaddr & ~(size_t.sizeof - 1)));
- pdso._gcRanges.insertBack(beg[0 .. phdr.p_memsz]);
- }
- version (Shared) if (phdr.p_flags & PF_X) // code segment
- {
- auto beg = cast(void*)(info.dlpi_addr + (phdr.p_vaddr & ~(size_t.sizeof - 1)));
- pdso._codeSegments.insertBack(beg[0 .. phdr.p_memsz]);
- }
- break;
-
- case PT_TLS: // TLS segment
- assert(!pdso._tlsSize); // is unique per DSO
- pdso._tlsMod = info.dlpi_tls_modid;
- pdso._tlsSize = phdr.p_memsz;
- break;
-
- default:
- break;
- }
- }
-}
-
-/**************************
- * Input:
- * result where the output is to be written; dl_phdr_info is a Linux struct
- * Returns:
- * true if found, and *result is filled in
- * References:
- * http://linux.die.net/man/3/dl_iterate_phdr
- */
-version (linux) bool findDSOInfoForAddr(in void* addr, dl_phdr_info* result=null) nothrow @nogc
-{
- static struct DG { const(void)* addr; dl_phdr_info* result; }
-
- extern(C) int callback(dl_phdr_info* info, size_t sz, void* arg) nothrow @nogc
- {
- auto p = cast(DG*)arg;
- if (findSegmentForAddr(*info, p.addr))
- {
- if (p.result !is null) *p.result = *info;
- return 1; // break;
- }
- return 0; // continue iteration
- }
-
- auto dg = DG(addr, result);
-
- /* Linux function that walks through the list of an application's shared objects and
- * calls 'callback' once for each object, until either all shared objects
- * have been processed or 'callback' returns a nonzero value.
- */
- return dl_iterate_phdr(&callback, &dg) != 0;
-}
-else version (FreeBSD) bool findDSOInfoForAddr(in void* addr, dl_phdr_info* result=null) nothrow @nogc
-{
- return !!_rtld_addr_phdr(addr, result);
-}
-else version (NetBSD) bool findDSOInfoForAddr(in void* addr, dl_phdr_info* result=null) nothrow @nogc
-{
- static struct DG { const(void)* addr; dl_phdr_info* result; }
-
- extern(C) int callback(dl_phdr_info* info, size_t sz, void* arg) nothrow @nogc
- {
- auto p = cast(DG*)arg;
- if (findSegmentForAddr(*info, p.addr))
- {
- if (p.result !is null) *p.result = *info;
- return 1; // break;
- }
- return 0; // continue iteration
- }
- auto dg = DG(addr, result);
- return dl_iterate_phdr(&callback, &dg) != 0;
-}
-
-/*********************************
- * Determine if 'addr' lies within shared object 'info'.
- * If so, return true and fill in 'result' with the corresponding ELF program header.
- */
-bool findSegmentForAddr(in ref dl_phdr_info info, in void* addr, ElfW!"Phdr"* result=null) nothrow @nogc
-{
- if (addr < cast(void*)info.dlpi_addr) // less than base address of object means quick reject
- return false;
-
- foreach (ref phdr; info.dlpi_phdr[0 .. info.dlpi_phnum])
- {
- auto beg = cast(void*)(info.dlpi_addr + phdr.p_vaddr);
- if (cast(size_t)(addr - beg) < phdr.p_memsz)
- {
- if (result !is null) *result = phdr;
- return true;
- }
- }
- return false;
-}
-
-version (linux) import core.sys.linux.errno : program_invocation_name;
-// should be in core.sys.freebsd.stdlib
-version (FreeBSD) extern(C) const(char)* getprogname() nothrow @nogc;
-version (NetBSD) extern(C) const(char)* getprogname() nothrow @nogc;
-
-@property const(char)* progname() nothrow @nogc
-{
- version (linux) return program_invocation_name;
- version (FreeBSD) return getprogname();
- version (NetBSD) return getprogname();
-}
-
-const(char)[] dsoName(const char* dlpi_name) nothrow @nogc
-{
- // the main executable doesn't have a name in its dlpi_name field
- const char* p = dlpi_name[0] != 0 ? dlpi_name : progname;
- return p[0 .. strlen(p)];
-}
-
-extern(C)
-{
- void* rt_get_bss_start() @nogc nothrow;
- void* rt_get_end() @nogc nothrow;
-}
-
-/// get the BSS section of the executable to check for copy relocations
-const(void)[] getCopyRelocSection() nothrow @nogc
-{
- auto bss_start = rt_get_bss_start();
- auto bss_end = rt_get_end();
- immutable bss_size = bss_end - bss_start;
-
- /**
- Check whether __bss_start/_end both lie within the executable DSO.same DSO.
-
- When a C host program dynamically loads druntime, i.e. it isn't linked
- against, __bss_start/_end might be defined in different DSOs, b/c the
- linker creates those symbols only when they are used.
- But as there are no copy relocations when dynamically loading a shared
- library, we can simply return a null bss range in that case.
- */
- if (bss_size <= 0)
- return null;
-
- version (linux)
- enum ElfW!"Addr" exeBaseAddr = 0;
- else version (FreeBSD)
- enum ElfW!"Addr" exeBaseAddr = 0;
- else version (NetBSD)
- enum ElfW!"Addr" exeBaseAddr = 0;
-
- dl_phdr_info info = void;
- findDSOInfoForAddr(bss_start, &info) || assert(0);
- if (info.dlpi_addr != exeBaseAddr)
- return null;
- findDSOInfoForAddr(bss_end - 1, &info) || assert(0);
- if (info.dlpi_addr != exeBaseAddr)
- return null;
-
- return bss_start[0 .. bss_size];
-}
-
-/**
- * Check for module collisions. A module in a shared library collides
- * with an existing module if it's ModuleInfo is interposed (search
- * symbol interposition) by another DSO. Therefor two modules with the
- * same name do not collide if their DSOs are in separate symbol resolution
- * chains.
- */
-void checkModuleCollisions(in ref dl_phdr_info info, in immutable(ModuleInfo)*[] modules,
- in void[] copyRelocSection) nothrow @nogc
-in { assert(modules.length); }
-body
-{
- immutable(ModuleInfo)* conflicting;
-
- foreach (m; modules)
- {
- auto addr = cast(const(void*))m;
- if (cast(size_t)(addr - copyRelocSection.ptr) < copyRelocSection.length)
- {
- // Module is in .bss of the exe because it was copy relocated
- }
- else if (!findSegmentForAddr(info, addr))
- {
- // Module is in another DSO
- conflicting = m;
- break;
- }
- }
-
- if (conflicting !is null)
- {
- dl_phdr_info other=void;
- findDSOInfoForAddr(conflicting, &other) || assert(0);
-
- auto modname = conflicting.name;
- auto loading = dsoName(info.dlpi_name);
- auto existing = dsoName(other.dlpi_name);
- fprintf(stderr, "Fatal Error while loading '%.*s':\n\tThe module '%.*s' is already defined in '%.*s'.\n",
- cast(int)loading.length, loading.ptr,
- cast(int)modname.length, modname.ptr,
- cast(int)existing.length, existing.ptr);
- import core.stdc.stdlib : _Exit;
- _Exit(1);
- }
-}
-
-/**************************
- * Input:
- * addr an internal address of a DSO
- * Returns:
- * the dlopen handle for that DSO or null if addr is not within a loaded DSO
- */
-version (Shared) void* handleForAddr(void* addr) nothrow @nogc
-{
- Dl_info info = void;
- if (dladdr(addr, &info) != 0)
- return handleForName(info.dli_fname);
- return null;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// TLS module helper
-///////////////////////////////////////////////////////////////////////////////
-
-
-/*
- * Returns: the TLS memory range for a given module and the calling
- * thread or null if that module has no TLS.
- *
- * Note: This will cause the TLS memory to be eagerly allocated.
- */
-struct tls_index
-{
- version (CRuntime_Glibc)
- {
- // For x86_64, fields are of type uint64_t, this is important for x32
- // where tls_index would otherwise have the wrong size.
- // See https://sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/x86_64/dl-tls.h
- version (X86_64)
- {
- ulong ti_module;
- ulong ti_offset;
- }
- else
- {
- c_ulong ti_module;
- c_ulong ti_offset;
- }
- }
- else
- {
- size_t ti_module;
- size_t ti_offset;
- }
-}
-
-extern(C) void* __tls_get_addr(tls_index* ti) nothrow @nogc;
-
-/* The dynamic thread vector (DTV) pointers may point 0x8000 past the start of
- * each TLS block. This is at least true for PowerPC and Mips platforms.
- * See: https://sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/powerpc/dl-tls.h;h=f7cf6f96ebfb505abfd2f02be0ad0e833107c0cd;hb=HEAD#l34
- * https://sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/mips/dl-tls.h;h=93a6dc050cb144b9f68b96fb3199c60f5b1fcd18;hb=HEAD#l32
- * https://sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/riscv/dl-tls.h;h=ab2d860314de94c18812bc894ff6b3f55368f20f;hb=HEAD#l32
- */
-version (X86)
- enum TLS_DTV_OFFSET = 0x0;
-else version (X86_64)
- enum TLS_DTV_OFFSET = 0x0;
-else version (ARM)
- enum TLS_DTV_OFFSET = 0x0;
-else version (AArch64)
- enum TLS_DTV_OFFSET = 0x0;
-else version (RISCV32)
- enum TLS_DTV_OFFSET = 0x800;
-else version (RISCV64)
- enum TLS_DTV_OFFSET = 0x800;
-else version (HPPA)
- enum TLS_DTV_OFFSET = 0x0;
-else version (SPARC)
- enum TLS_DTV_OFFSET = 0x0;
-else version (SPARC64)
- enum TLS_DTV_OFFSET = 0x0;
-else version (PPC)
- enum TLS_DTV_OFFSET = 0x8000;
-else version (PPC64)
- enum TLS_DTV_OFFSET = 0x8000;
-else version (MIPS32)
- enum TLS_DTV_OFFSET = 0x8000;
-else version (MIPS64)
- enum TLS_DTV_OFFSET = 0x8000;
-else
- static assert( false, "Platform not supported." );
-
-void[] getTLSRange(size_t mod, size_t sz) nothrow @nogc
-{
- if (mod == 0)
- return null;
-
- // base offset
- auto ti = tls_index(mod, 0);
- return (__tls_get_addr(&ti)-TLS_DTV_OFFSET)[0 .. sz];
-}
+++ /dev/null
-/**
- * Written in the D programming language.
- * This module provides OSX-specific support for sections.
- *
- * Copyright: Copyright Digital Mars 2008 - 2012.
- * License: Distributed under the
- * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0).
- * (See accompanying file LICENSE)
- * Authors: Walter Bright, Sean Kelly, Martin Nowak
- * Source: $(DRUNTIMESRC src/rt/_sections_osx.d)
- */
-
-module rt.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
- {
- return _moduleGroup.modules;
- }
-
- @property ref inout(ModuleGroup) moduleGroup() inout
- {
- return _moduleGroup;
- }
-
- @property inout(void[])[] gcRanges() inout
- {
- return _gcRanges[];
- }
-
- @property immutable(FuncTable)[] ehTables() const
- {
- 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()
-{
- 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()
-{
- _sections._gcRanges.reset();
- pthread_key_delete(_tlsKey);
- _isRuntimeInitialized = false;
-}
-
-void[]* initTLSRanges()
-{
- return &getTLSBlock();
-}
-
-void finiTLSRanges(void[]* rng)
-{
- .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()
-{
- 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;
-}
+++ /dev/null
-/**
- * Written in the D programming language.
- * This module provides Solaris-specific support for sections.
- *
- * Copyright: Copyright Martin Nowak 2012-2013.
- * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
- * Authors: Martin Nowak
- * Source: $(DRUNTIMESRC src/rt/_sections_solaris.d)
- */
-
-module rt.sections_solaris;
-
-version (Solaris):
-
-// debug = PRINTF;
-debug(PRINTF) import core.stdc.stdio;
-import core.stdc.stdlib : malloc, free;
-import rt.deh, rt.minfo;
-
-struct SectionGroup
-{
- static int opApply(scope int delegate(ref SectionGroup) dg)
- {
- return dg(_sections);
- }
-
- static int opApplyReverse(scope int delegate(ref SectionGroup) dg)
- {
- return dg(_sections);
- }
-
- @property immutable(ModuleInfo*)[] modules() const nothrow @nogc
- {
- return _moduleGroup.modules;
- }
-
- @property ref inout(ModuleGroup) moduleGroup() inout nothrow @nogc
- {
- return _moduleGroup;
- }
-
- @property immutable(FuncTable)[] ehTables() const nothrow @nogc
- {
- auto pbeg = cast(immutable(FuncTable)*)&__start_deh;
- auto pend = cast(immutable(FuncTable)*)&__stop_deh;
- return pbeg[0 .. pend - pbeg];
- }
-
- @property inout(void[])[] gcRanges() inout nothrow @nogc
- {
- return _gcRanges[];
- }
-
-private:
- ModuleGroup _moduleGroup;
- void[][1] _gcRanges;
-}
-
-void initSections() nothrow @nogc
-{
- auto mbeg = cast(immutable ModuleInfo**)&__start_minfo;
- auto mend = cast(immutable ModuleInfo**)&__stop_minfo;
- _sections.moduleGroup = ModuleGroup(mbeg[0 .. mend - mbeg]);
-
- auto pbeg = cast(void*)&__dso_handle;
- auto pend = cast(void*)&_end;
- _sections._gcRanges[0] = pbeg[0 .. pend - pbeg];
-}
-
-void finiSections() nothrow @nogc
-{
-}
-
-void[] initTLSRanges() nothrow @nogc
-{
- auto pbeg = cast(void*)&_tlsstart;
- auto pend = cast(void*)&_tlsend;
- return pbeg[0 .. pend - pbeg];
-}
-
-void finiTLSRanges(void[] rng) nothrow @nogc
-{
-}
-
-void scanTLSRanges(void[] rng, scope void delegate(void* pbeg, void* pend) nothrow dg) nothrow
-{
- dg(rng.ptr, rng.ptr + rng.length);
-}
-
-private:
-
-__gshared SectionGroup _sections;
-
-extern(C)
-{
- /* Symbols created by the compiler/linker and inserted into the
- * object file that 'bracket' sections.
- */
- extern __gshared
- {
- void* __start_deh;
- void* __stop_deh;
- void* __start_minfo;
- void* __stop_minfo;
- int __dso_handle;
- int _end;
- }
-
- extern
- {
- void* _tlsstart;
- void* _tlsend;
- }
-}
+++ /dev/null
-/**
- * Written in the D programming language.
- * This module provides Win32-specific support for sections.
- *
- * Copyright: Copyright Digital Mars 2008 - 2012.
- * License: Distributed under the
- * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0).
- * (See accompanying file LICENSE)
- * Authors: Walter Bright, Sean Kelly, Martin Nowak
- * Source: $(DRUNTIMESRC src/rt/_sections_win32.d)
- */
-
-module rt.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;
- }
-}
+++ /dev/null
-/**
- * Written in the D programming language.
- * This module provides Win32-specific support for sections.
- *
- * Copyright: Copyright Digital Mars 2008 - 2012.
- * License: Distributed under the
- * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0).
- * (See accompanying file LICENSE)
- * Authors: Walter Bright, Sean Kelly, Martin Nowak
- * Source: $(DRUNTIMESRC src/rt/_sections_win64.d)
- */
-
-module rt.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;
- 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;
-}