-
-
Notifications
You must be signed in to change notification settings - Fork 411
Add support for native TLS on OS X #1461
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 |
|---|---|---|
| @@ -0,0 +1,51 @@ | ||
| /** | ||
| * Helpers for determining TLS memory ranges on OS X. | ||
| * | ||
| * This unfortunately cannot be entirely done in D, as the OS X API uses | ||
| * the Apple-specific blocks C extension. | ||
| * | ||
| * Copyright: David Nadlinger, 2012. | ||
| * License: <a href="http://www.boost.org/LICENSE_1_0.txt">Boost License 1.0</a>. | ||
| * Authors: David Nadlinger | ||
| */ | ||
|
|
||
| #ifndef __APPLE__ | ||
| #ifndef __BLOCKS__ | ||
| #error "Need a C compiler with Apple Blocks support – not building on OS X with Clang?" | ||
| #endif | ||
| #endif | ||
|
|
||
| #include <assert.h> | ||
| #include <stddef.h> | ||
| #include <stdio.h> | ||
|
|
||
| /* | ||
| * Declarations from dyld_priv.h, available on 10.7+. | ||
| */ | ||
| enum dyld_tlv_states { | ||
| dyld_tlv_state_allocated = 10, | ||
| dyld_tlv_state_deallocated = 20 | ||
| }; | ||
| typedef struct { | ||
| size_t info_size; | ||
| void * tlv_addr; | ||
| size_t tlv_size; | ||
| } dyld_tlv_info; | ||
| typedef void (^dyld_tlv_state_change_handler)(enum dyld_tlv_states state, const dyld_tlv_info *info); | ||
| extern void dyld_register_tlv_state_change_handler(enum dyld_tlv_states state, dyld_tlv_state_change_handler handler); | ||
| extern void dyld_enumerate_tlv_storage(dyld_tlv_state_change_handler handler); | ||
|
|
||
|
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. A dummy symbol will only get you a single section, try to connect this to __tls_data below.
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. I'm not sure I understand this comment. Is it still relevant when
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. There should be only a single section given that shared libraries are not supported on OS X yet. Or am I missing something?
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. How does this work for LDC? It does support dynamic libraries?
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. We don't really do yet (except for Linux).
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. You should've already figured out how generic native tls accesses work (for the dmd part), so why introducing sth. that's incompatible with shared libraries? |
||
| void _d_dyld_getTLSRange(void* arbitraryTLSSymbol, void** start, size_t* size) { | ||
| dyld_enumerate_tlv_storage( | ||
| ^(enum dyld_tlv_states state, const dyld_tlv_info *info) { | ||
|
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. Please don't use weird vendor extensions, for sure you can do it with a plain callback or a struct.
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 API is only offered using a blocks-based callback. The need to do that is in fact the only reason why the code exists in an extra |
||
| assert(state == dyld_tlv_state_allocated); | ||
| if (info->tlv_addr <= arbitraryTLSSymbol && | ||
| arbitraryTLSSymbol < (info->tlv_addr + info->tlv_size) | ||
| ) { | ||
| // Found the range. | ||
| *start = info->tlv_addr; | ||
| *size = info->tlv_size; | ||
| } | ||
| } | ||
| ); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,186 @@ | ||
| /** | ||
| * Written in the D programming language. | ||
| * This module provides OS X x86-64 specific support for sections. | ||
| * | ||
| * Copyright: Copyright Digital Mars 2016. | ||
| * License: Distributed under the | ||
| * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0). | ||
| * (See accompanying file LICENSE) | ||
| * Authors: Jacob Carlborg | ||
| * Source: $(DRUNTIMESRC src/rt/_sections_osx_x86_64.d) | ||
| */ | ||
| module rt.sections_osx_x86_64; | ||
|
|
||
| version(OSX): | ||
| version(X86_64): | ||
|
|
||
| // debug = PRINTF; | ||
| import core.stdc.stdio; | ||
| import core.stdc.string, core.stdc.stdlib; | ||
| import core.sys.posix.pthread; | ||
| import core.sys.osx.mach.dyld; | ||
| import core.sys.osx.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() | ||
| { | ||
| _dyld_register_func_for_add_image(§ions_osx_onAddImage); | ||
|
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. Well unless something changed this function is still a dead-end because you cannot deregister the callback, meaning that this change makes D shared libraries (a VST plugin) ununloadable. 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. I have a fix on the way to add linux ctors/dtors to all modules and replace this callback usage.
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.
@MartinNowak this PR won't fix dynamic libraries. It implements native TLS, nothing else. But native TLS is a prerequisite to fix dynamic libraries.
I don't see how this PR would affect the way dynamic libraries work. The state of dynamic libraries will not be any worse than they are now.
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. You can use D shared libraries as plugins for C hosts (statically linking against phobos), this change breaks the use-case. So please finish your work on ctors/dtors first, or come up w/ a different solution.
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. Master is using
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. And if you want to support shared libraries, theres should be a test for that.
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. Sorry, didn't remember that we've always used those callbacks, would still be great to replace them soon.
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.
No worries 😃.
Absolutely. But let's take this one step at the time. 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.
Indeed, I would like to go at the ctor problem knowing we've got a clean slate(all tests passing) |
||
| _isRuntimeInitialized = true; | ||
| } | ||
|
|
||
| /*** | ||
| * Gets called on program shutdown just after GC is terminated. | ||
| */ | ||
| void finiSections() | ||
| { | ||
| _sections._gcRanges.reset(); | ||
| _isRuntimeInitialized = false; | ||
| } | ||
|
|
||
| void[] initTLSRanges() | ||
| { | ||
| void* start = null; | ||
| size_t size = 0; | ||
| _d_dyld_getTLSRange(&dummyTlsSymbol, &start, &size); | ||
| assert(start && size, "Could not determine TLS range."); | ||
| return start[0 .. size]; | ||
| } | ||
|
|
||
| void finiTLSRanges(void[] rng) | ||
| { | ||
|
|
||
| } | ||
|
|
||
| void scanTLSRanges(void[] rng, scope void delegate(void* pbeg, void* pend) nothrow dg) nothrow | ||
| { | ||
| dg(rng.ptr, rng.ptr + rng.length); | ||
| } | ||
|
|
||
| private: | ||
|
|
||
| extern(C) void _d_dyld_getTLSRange(void*, void**, size_t*); | ||
|
|
||
| __gshared SectionGroup _sections; | ||
| ubyte dummyTlsSymbol; | ||
|
|
||
| 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 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) | ||
| { | ||
| assert(header.magic == MH_MAGIC_64); | ||
| auto sect = getsectbynamefromheader_64(cast(mach_header_64*)header, | ||
| segmentName, | ||
| sectionName); | ||
|
|
||
| if (sect !is null && sect.size > 0) | ||
| return (cast(ubyte*)sect.addr + slide)[0 .. cast(size_t)sect.size]; | ||
| return null; | ||
| } | ||
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.
Check for OSX, not for lambda support.
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.
There's a theoretical possibility that someone will try to compile this with GCC. So perhaps check for both OS X and blocks? @klickverbot will it be possible for LDC to use this functionality? In that case what, preprocessor constant can I use to cover all Apple platforms?
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.
Huh? The file is from LDC's druntime, so of course we use it already. If your question is about using Blocks from D, then I don't think anybody is planning to add this to the ObjC interop implementation just yet.
In either case, the file is supposed to be excluded from the build on all other platforms anyway, so strictly speaking, the
#ifndefis completely unnecessary. However, since the error messages produced by tools that don't support Blocks can be quite puzzling (especially if you are not aware that Blocks even exist), I added the extra check to get a clean preprocessor error in such cases, which is why I used__BLOCKS__.Feel free to change it so that it refers to another version or remove it entirely, though. I don't think a strong argument is to be made either way. You'd need to look into what other preprocessor constant to use yourself, though.
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.
@MartinNowak checking for both OS X and blocks now.