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