-
-
Notifications
You must be signed in to change notification settings - Fork 411
[WIP] rt.sections_elf_shared: Add support for OSX (prepare for shared Phobos lib) #2322
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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*; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Will this work for both 32bit and 64bit? There's a
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It does work for LDC, apparently for the reason you mentioned (used for |
||
| 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; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Without knowing this existed, I independently came up with this implementation for findDSOInfoForAddr: Probably could align ourselves up, mixing what you have here with I think:
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. On darwin9, and older versions, will have to use
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This PR is more than 2 years old - this now looks like this: https://github.com/ldc-developers/druntime/blob/a261a84e4c6b208c65ae39da0ae844ae3dd06744/src/rt/sections_elf_shared.d#L903-L907
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The slide can be calculated like this [1]. Also note this [2]: "The following functions allow you to iterate through all loaded images. This is not a thread safe operation. Another thread can add or remove an image during the iteration. Many uses of these routines can be replace by a call to dladdr() which will return the mach_header and name of an image, given an address in the image. dladdr() is thread safe." [2] https://opensource.apple.com/source/dyld/dyld-750.6/include/mach-o/dyld.h.auto.html
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @jacob-carlborg good to know, though you're still using _dyld_image_count and get_image_header
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, IIRC I couldn't get that to work with |
||
| } | ||
| 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,13 +914,15 @@ 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; | ||
|
|
||
| @property const(char)* progname() 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."); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This check will be removed if asserts are removed, is that intentional?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Most likely not => #2324. |
||
| 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]; | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure I understand when you're using
SharedDarwinand when you're usingOSX.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah I noticed the inconsistency too. It's not my code, and as you know, I have no interest in & knowledge of the different Mac OS flavours (desktop, iOS, watchOS, whatever).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is your code, you added
SharedDarwin.