From 52d71d2d0a85afaa4c4b5259142268d63711f0ec Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Fri, 5 Oct 2018 19:40:11 +0200 Subject: [PATCH] rt.sections_elf_shared: Add support for OSX This is almost exactly LDC's version, which uses this module for 32/64-bit OSX too, incl. support for Phobos as shared library, and doesn't use `rt.sections_osx_x86[_64]` at all. --- src/rt/sections_elf_shared.d | 223 ++++++++++++++++++++++++----------- 1 file changed, 155 insertions(+), 68 deletions(-) diff --git a/src/rt/sections_elf_shared.d b/src/rt/sections_elf_shared.d index f168ba60ff..8fafdcb615 100644 --- a/src/rt/sections_elf_shared.d +++ b/src/rt/sections_elf_shared.d @@ -17,7 +17,11 @@ 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): + +version (OSX) enum SharedDarwin = true; +else enum SharedDarwin = false; + +static if (SharedELF): // TODO: SharedDarwin too // debug = PRINTF; import core.memory; @@ -36,6 +40,15 @@ else version (FreeBSD) import core.sys.freebsd.sys.elf; import core.sys.freebsd.sys.link_elf; } +else version (OSX) +{ + import core.sys.osx.dlfcn; + import core.sys.osx.mach.dyld; + import core.sys.osx.mach.getsect; + + extern(C) intptr_t _dyld_get_image_slide(const mach_header*) nothrow @nogc; + extern(C) mach_header* _dyld_get_image_header_containing_address(const void *addr) nothrow @nogc; +} else version (NetBSD) { import core.sys.netbsd.dlfcn; @@ -94,6 +107,7 @@ struct DSO @property immutable(FuncTable)[] ehTables() const nothrow @nogc { + // TODO: possibly required for OSX return null; } @@ -108,13 +122,24 @@ private: { assert(_moduleGroup.modules.length); version (CRuntime_UClibc) {} else - assert(_tlsMod || !_tlsSize); + static if (SharedELF) + { + assert(_tlsMod || !_tlsSize); + } } + void** _slot; ModuleGroup _moduleGroup; Array!(void[]) _gcRanges; - size_t _tlsMod; - size_t _tlsSize; + static if (SharedELF) + { + size_t _tlsMod; + size_t _tlsSize; + } + else static if (SharedDarwin) + { + GetTLSAnchor _getTLSAnchor; + } version (Shared) { @@ -126,7 +151,15 @@ private: // get the TLS range for the executing thread void[] tlsRange() const nothrow @nogc { - return getTLSRange(_tlsMod, _tlsSize); + static if (SharedELF) + { + return getTLSRange(_tlsMod, _tlsSize); + } + else static if (SharedDarwin) + { + return getTLSRange(_getTLSAnchor()); + } + else static assert(0, "unimplemented"); } } @@ -198,7 +231,7 @@ version (Shared) 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); + .dlopen(nameForDSO(tdso._pdso), RTLD_LAZY) !is null || assert(0); (*res)[i]._addCnt = 1; // new array takes over the additional ref count } } @@ -319,17 +352,20 @@ version (Shared) bool _rtLoading; /* - * Hash table to map link_map* to corresponding DSO*. - * The hash table is protected by a Mutex. + * Hash table to map the native handle (as returned by dlopen) + * to the corresponding DSO*, 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; + static if (SharedELF) + { + /* + * Section in executable that contains copy relocations. + * Might be null when druntime is dynamically loaded by a C host. + */ + __gshared const(void)[] _copyRelocSection; + } } else { @@ -352,6 +388,12 @@ else // Compiler to runtime interface. /////////////////////////////////////////////////////////////////////////////// +version (OSX) + private alias ImageHeader = mach_header*; +else + private alias ImageHeader = dl_phdr_info; + +extern(C) alias GetTLSAnchor = void* function() nothrow @nogc; /* * This data structure is generated by the compiler, and then passed to @@ -362,6 +404,7 @@ 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 + static if (SharedDarwin) GetTLSAnchor _getTLSAnchor; } T[] toRange(T)(T* beg, T* end) { return beg[0 .. end - beg]; } @@ -384,20 +427,23 @@ extern(C) void _d_dso_registry(CompilerDSOData* data) DSO* pdso = cast(DSO*).calloc(1, DSO.sizeof); assert(typeid(DSO).initializer().ptr is null); + pdso._slot = data._slot; *data._slot = pdso; // store backlink in library record pdso._moduleGroup = ModuleGroup(toRange(data._minfo_beg, data._minfo_end)); - dl_phdr_info info = void; - findDSOInfoForAddr(data._slot, &info) || assert(0); + static if (SharedDarwin) pdso._getTLSAnchor = data._getTLSAnchor; - scanSegments(info, pdso); + ImageHeader header = void; + findImageHeaderForAddr(data._slot, &header) || assert(0); + + scanSegments(header, pdso); version (Shared) { auto handle = handleForAddr(data._slot); - getDependencies(info, pdso._deps); + getDependencies(header, pdso._deps); pdso._handle = handle; setDSOForHandle(pdso, pdso._handle); @@ -627,21 +673,13 @@ void freeDSO(DSO* pdso) nothrow @nogc version (Shared) { @nogc nothrow: - link_map* linkMapForHandle(void* handle) + const(char)* nameForDSO(in DSO* pdso) { - link_map* map; - dlinfo(handle, RTLD_DI_LINKMAP, &map) == 0 || assert(0); - return map; + Dl_info info = void; + dladdr(pdso._slot, &info) || assert(0); + return info.dli_fname; } - link_map* exeLinkMap(link_map* map) - { - assert(map); - while (map.l_prev !is null) - map = map.l_prev; - return map; - } - DSO* dsoForHandle(void* handle) { DSO* pdso; @@ -668,7 +706,7 @@ version (Shared) !pthread_mutex_unlock(&_handleToDSOMutex) || assert(0); } - void getDependencies(in ref dl_phdr_info info, ref Array!(DSO*) deps) + static if (SharedELF) void getDependencies(in ref dl_phdr_info info, ref Array!(DSO*) deps) { // get the entries of the .dynamic section ElfW!"Dyn"[] dyns; @@ -719,6 +757,10 @@ version (Shared) deps.insertBack(pdso); // append it to the dependencies } } + else static if (SharedDarwin) void getDependencies(in ImageHeader info, ref Array!(DSO*) deps) + { + // FIXME: Not implemented yet. + } void* handleForName(const char* name) { @@ -733,10 +775,10 @@ version (Shared) /////////////////////////////////////////////////////////////////////////////// /************ - * Scan segments in Linux dl_phdr_info struct and store + * Scan segments in the image header and store * the TLS and writeable data segments in *pdso. */ -void scanSegments(in ref dl_phdr_info info, DSO* pdso) nothrow @nogc +static if (SharedELF) void scanSegments(in ref dl_phdr_info info, DSO* pdso) nothrow @nogc { foreach (ref phdr; info.dlpi_phdr[0 .. info.dlpi_phnum]) { @@ -771,6 +813,27 @@ void scanSegments(in ref dl_phdr_info info, DSO* pdso) nothrow @nogc } } } +else static if (SharedDarwin) void scanSegments(mach_header* info, DSO* pdso) +{ + import rt.mach_utils; + + immutable slide = _dyld_get_image_slide(info); + foreach (e; dataSections) + { + auto sect = getSection(info, slide, e.seg, e.sect); + if (sect != null) + pdso._gcRanges.insertBack((cast(void*)sect.ptr)[0 .. sect.length]); + } + + version (Shared) + { + auto text = getSection(info, slide, "__TEXT", "__text"); + if (!text) { + assert(0, "Failed to get text section."); + } + pdso._codeSegments.insertBack(cast(void[])text); + } +} /************************** * Input: @@ -780,7 +843,7 @@ void scanSegments(in ref dl_phdr_info info, DSO* pdso) nothrow @nogc * References: * http://linux.die.net/man/3/dl_iterate_phdr */ -bool findDSOInfoForAddr(in void* addr, dl_phdr_info* result=null) nothrow @nogc +bool findImageHeaderForAddr(in void* addr, ImageHeader* result=null) nothrow @nogc { version (linux) enum IterateManually = true; else version (NetBSD) enum IterateManually = true; @@ -809,6 +872,12 @@ bool findDSOInfoForAddr(in void* addr, dl_phdr_info* result=null) nothrow @nogc */ return dl_iterate_phdr(&callback, &dg) != 0; } + else version (OSX) + { + auto header = _dyld_get_image_header_containing_address(addr); + if (result) *result = header; + return !!header; + } else version (FreeBSD) { return !!_rtld_addr_phdr(addr, result); @@ -825,7 +894,7 @@ bool findDSOInfoForAddr(in void* addr, dl_phdr_info* result=null) nothrow @nogc * 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 +static if (SharedELF) 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; @@ -845,6 +914,7 @@ bool findSegmentForAddr(in ref dl_phdr_info info, in void* addr, ElfW!"Phdr"* re 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 (OSX) 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; @@ -852,6 +922,7 @@ version (NetBSD) extern(C) const(char)* getprogname() nothrow @nogc; { version (linux) return program_invocation_name; version (FreeBSD) return getprogname(); + version (OSX) return getprogname(); version (DragonFlyBSD) return getprogname(); version (NetBSD) return getprogname(); } @@ -894,42 +965,58 @@ struct tls_index size_t ti_offset; } -extern(C) void* __tls_get_addr(tls_index* ti) nothrow @nogc; +version (OSX) +{ + extern(C) void _d_dyld_getTLSRange(void*, void**, size_t*) 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 - */ -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 (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; + void[] getTLSRange(void *tlsSymbol) nothrow @nogc + { + void* start = null; + size_t size = 0; + _d_dyld_getTLSRange(tlsSymbol, &start, &size); + assert(start && size, "Could not determine TLS range."); + return start[0 .. size]; + } +} else - static assert( false, "Platform not supported." ); - -void[] getTLSRange(size_t mod, size_t sz) nothrow @nogc { - if (mod == 0) - return null; + extern(C) void* __tls_get_addr(tls_index* ti) nothrow @nogc; - // base offset - auto ti = tls_index(mod, 0); - return (__tls_get_addr(&ti)-TLS_DTV_OFFSET)[0 .. sz]; + /* 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 + */ + 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 (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]; + } }