Last active
December 26, 2015 08:39
-
-
Save dnadlinger/7124007 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| diff --git a/src/ldc/arm_unwind.c b/src/ldc/arm_unwind.c | |
| index f1ab5db..a847e2a 100644 | |
| --- a/src/ldc/arm_unwind.c | |
| +++ b/src/ldc/arm_unwind.c | |
| @@ -24,6 +24,11 @@ void _d_eh_SetIP(_Unwind_Context *context, _Unwind_Word new_value) | |
| _Unwind_SetIP(context, new_value); | |
| } | |
| +_Unwind_Word _d_eh_GetGR(_Unwind_Context *context, int index) | |
| +{ | |
| + return _Unwind_GetGR(context, index); | |
| +} | |
| + | |
| void _d_eh_SetGR(_Unwind_Context *context, int index, _Unwind_Word new_value) | |
| { | |
| _Unwind_SetGR(context, index, new_value); | |
| diff --git a/src/ldc/eh.d b/src/ldc/eh.d | |
| index f6d9730..194e007 100644 | |
| --- a/src/ldc/eh.d | |
| +++ b/src/ldc/eh.d | |
| @@ -9,8 +9,8 @@ private import core.stdc.stdlib; | |
| private import rt.util.console; | |
| private import core.stdc.stdarg; | |
| -// debug = EH_personality; | |
| -// debug = EH_personality_verbose; | |
| +debug = EH_personality; | |
| +debug = EH_personality_verbose; | |
| // current EH implementation works on x86 | |
| // if it has a working unwind runtime | |
| @@ -60,9 +60,10 @@ extern(C) | |
| // libunwind headers | |
| extern(C) | |
| { | |
| + // FIXME: Some of these do not actually exist on ARM. | |
| enum _Unwind_Reason_Code : int | |
| { | |
| - NO_REASON = 0, | |
| + NO_REASON = 0, // "OK" on ARM | |
| FOREIGN_EXCEPTION_CAUGHT = 1, | |
| FATAL_PHASE2_ERROR = 2, | |
| FATAL_PHASE1_ERROR = 3, | |
| @@ -70,7 +71,8 @@ extern(C) | |
| END_OF_STACK = 5, | |
| HANDLER_FOUND = 6, | |
| INSTALL_CONTEXT = 7, | |
| - CONTINUE_UNWIND = 8 | |
| + CONTINUE_UNWIND = 8, | |
| + FAILURE = 9 // ARM only | |
| } | |
| enum _Unwind_Action : int | |
| @@ -145,11 +147,12 @@ version(HP_LIBUNWIND) | |
| } | |
| else version(GCC_UNWIND) | |
| { | |
| - void _Unwind_Resume(_Unwind_Exception*); | |
| - _Unwind_Reason_Code _Unwind_RaiseException(_Unwind_Exception*); | |
| ptrdiff_t _Unwind_GetLanguageSpecificData(_Unwind_Context_Ptr context); | |
| version (ARM) | |
| { | |
| + _Unwind_Reason_Code _Unwind_RaiseException(_Unwind_Control_Block*); | |
| + void _Unwind_Resume(_Unwind_Control_Block*); | |
| + | |
| // On ARM, these are macros resp. not visible (static inline). To avoid | |
| // an unmaintainable amount of dependencies on implementation details, | |
| // just use a C shim. | |
| @@ -159,11 +162,16 @@ else version(GCC_UNWIND) | |
| void _d_eh_SetIP(_Unwind_Context_Ptr context, ptrdiff_t new_value); | |
| alias _Unwind_SetIP = _d_eh_SetIP; | |
| + ptrdiff_t _d_eh_GetGR(_Unwind_Context_Ptr context, int index); | |
| + alias _Unwind_GetGR = _d_eh_GetGR; | |
| + | |
| void _d_eh_SetGR(_Unwind_Context_Ptr context, int index, ptrdiff_t new_value); | |
| alias _Unwind_SetGR = _d_eh_SetGR; | |
| } | |
| else | |
| { | |
| + _Unwind_Reason_Code _Unwind_RaiseException(_Unwind_Exception*); | |
| + void _Unwind_Resume(_Unwind_Exception*); | |
| ptrdiff_t _Unwind_GetIP(_Unwind_Context_Ptr context); | |
| void _Unwind_SetIP(_Unwind_Context_Ptr context, ptrdiff_t new_value); | |
| void _Unwind_SetGR(_Unwind_Context_Ptr context, int index, | |
| @@ -354,6 +362,30 @@ Lerr: | |
| return addr; | |
| } | |
| +ptrdiff_t get_base_of_encoded_value(ubyte encoding, _Unwind_Context_Ptr context) | |
| +{ | |
| + if (encoding == _DW_EH_Format.DW_EH_PE_omit) | |
| + return 0; | |
| + | |
| + with (_DW_EH_Format) switch (encoding & 0x70) { | |
| + case DW_EH_PE_absptr: | |
| + case DW_EH_PE_pcrel: | |
| + case DW_EH_PE_aligned: | |
| + return 0; | |
| + | |
| + case DW_EH_PE_textrel: | |
| + return _Unwind_GetTextRelBase (context); | |
| + case DW_EH_PE_datarel: | |
| + return _Unwind_GetDataRelBase (context); | |
| + case DW_EH_PE_funcrel: | |
| + return _Unwind_GetRegionStart (context); | |
| + | |
| + default: | |
| + fatalerror("Unsupported encoding type to get base from."); | |
| + assert(0); | |
| + } | |
| +} | |
| + | |
| // exception struct used by the runtime. | |
| // _d_throw allocates a new instance and passes the address of its | |
| @@ -366,7 +398,14 @@ Lerr: | |
| struct _d_exception | |
| { | |
| Object exception_object; | |
| - _Unwind_Exception unwind_info; | |
| + version (ARM) | |
| + { | |
| + _Unwind_Control_Block unwind_info; | |
| + } | |
| + else | |
| + { | |
| + _Unwind_Exception unwind_info; | |
| + } | |
| } | |
| // the 8-byte string identifying the type of exception | |
| @@ -382,18 +421,111 @@ __gshared char[8] _d_exception_class = "LLDCD2\0\0"; | |
| version(GCC_UNWIND) | |
| { | |
| +version(ARM) | |
| +{ | |
| + | |
| +enum _Unwind_State | |
| +{ | |
| + VIRTUAL_UNWIND_FRAME = 0, | |
| + UNWIND_FRAME_STARTING = 1, | |
| + UNWIND_FRAME_RESUME = 2, | |
| + ACTION_MASK = 3, | |
| + FORCE_UNWIND = 8, | |
| + END_OF_STACK = 16 | |
| +} | |
| + | |
| +alias _uw = ptrdiff_t; | |
| + | |
| +struct _Unwind_Control_Block | |
| +{ | |
| + char[8] exception_class; | |
| + void function(_Unwind_Reason_Code, _Unwind_Control_Block *) exception_cleanup; | |
| + | |
| + struct unwinder_cache_t | |
| + { | |
| + _uw reserved1; | |
| + _uw reserved2; | |
| + _uw reserved3; | |
| + _uw reserved4; | |
| + _uw reserved5; | |
| + } | |
| + unwinder_cache_t unwinder_cache; | |
| + | |
| + struct barrier_cache_t | |
| + { | |
| + _uw sp; | |
| + _uw[5] bitpattern; | |
| + } | |
| + barrier_cache_t barrier_cache; | |
| + | |
| + struct cleanup_cache_t | |
| + { | |
| + _uw[4] bitpattern; | |
| + } | |
| + cleanup_cache_t cleanup_cache; | |
| + | |
| + struct pr_cache_t | |
| + { | |
| + _uw fnstart; | |
| + _uw *ehtp; | |
| + _uw additional; | |
| + _uw reserved1; | |
| + } | |
| + pr_cache_t pr_cache; | |
| +} | |
| + | |
| +extern(C) _Unwind_Reason_Code __gnu_unwind_frame(_Unwind_Control_Block *, _Unwind_Context_Ptr); | |
| + | |
| +private _Unwind_Reason_Code continueUnwind(_Unwind_Control_Block* ucb, _Unwind_Context_Ptr context) { | |
| + if (__gnu_unwind_frame(ucb, context) != _Unwind_Reason_Code.NO_REASON) | |
| + return _Unwind_Reason_Code.FAILURE; | |
| + return _Unwind_Reason_Code.CONTINUE_UNWIND; | |
| +} | |
| + | |
| +// Defined in unwind-arm.h. | |
| +enum UNWIND_STACK_REG = 13; | |
| +enum UNWIND_POINTER_REG = 12; | |
| + | |
| +auto toDException(_Unwind_Control_Block* ucb) { | |
| + return cast(_d_exception*)(cast(ubyte*)ucb - _d_exception.unwind_info.offsetof); | |
| +} | |
| + | |
| // the personality routine gets called by the unwind handler and is responsible for | |
| // reading the EH tables and deciding what to do | |
| -extern(C) _Unwind_Reason_Code _d_eh_personality(int ver, _Unwind_Action actions, ulong exception_class, _Unwind_Exception* exception_info, _Unwind_Context_Ptr context) | |
| +extern(C) _Unwind_Reason_Code _d_eh_personality(_Unwind_State state, _Unwind_Control_Block* ucb, _Unwind_Context_Ptr context) | |
| { | |
| - debug(EH_personality_verbose) printf("entering personality function. context: %p\n", context); | |
| - // check ver: the C++ Itanium ABI only allows ver == 1 | |
| - if (ver != 1) | |
| - return _Unwind_Reason_Code.FATAL_PHASE1_ERROR; | |
| + debug(EH_personality_verbose) printf(" - entering personality function. state: %d; ucb: %p, context: %p\n", state, ucb, context); | |
| + | |
| + _Unwind_Action actions; | |
| + with (_Unwind_State) with (_Unwind_Action) { | |
| + switch (state & _Unwind_State.ACTION_MASK) { | |
| + case _Unwind_State.VIRTUAL_UNWIND_FRAME: | |
| + actions = _Unwind_Action.SEARCH_PHASE; | |
| + break; | |
| + case _Unwind_State.UNWIND_FRAME_STARTING: | |
| + actions = _Unwind_Action.CLEANUP_PHASE; | |
| + if (!(state & _Unwind_State.FORCE_UNWIND) && | |
| + ucb.barrier_cache.sp == _Unwind_GetGR(context, UNWIND_STACK_REG)) { | |
| + actions |= _Unwind_Action.HANDLER_FRAME; | |
| + } | |
| + break; | |
| + case _Unwind_State.UNWIND_FRAME_RESUME: | |
| + return continueUnwind(ucb, context); | |
| + default: | |
| + fatalerror("Unhandled ARM EABI unwind state."); | |
| + } | |
| + actions |= state & _Unwind_State.FORCE_UNWIND; | |
| + } | |
| + | |
| + // The dwarf unwinder assumes the context structure holds things like the | |
| + // function and LSDA pointers. The ARM implementation caches these in | |
| + // the exception header (UCB). To avoid rewriting everything we make a | |
| + // virtual scratch register point at the UCB. | |
| + _Unwind_SetGR(context, UNWIND_POINTER_REG, cast(ptrdiff_t)ucb); | |
| // check exceptionClass | |
| //TODO: Treat foreign exceptions with more respect | |
| - if ((cast(char*)&exception_class)[0..8] != _d_exception_class) | |
| + if (ucb.exception_class != _d_exception_class) | |
| return _Unwind_Reason_Code.FATAL_PHASE1_ERROR; | |
| // find call site table, action table and classinfo table | |
| @@ -406,7 +538,7 @@ extern(C) _Unwind_Reason_Code _d_eh_personality(int ver, _Unwind_Action actions, | |
| ubyte classinfo_table_encoding; | |
| _d_getLanguageSpecificTables(context, callsite_table, action_table, classinfo_table, classinfo_table_encoding); | |
| if (callsite_table is null) | |
| - return _Unwind_Reason_Code.CONTINUE_UNWIND; | |
| + return continueUnwind(ucb, context); | |
| /* | |
| find landing pad and action table index belonging to ip by walking | |
| @@ -431,7 +563,7 @@ extern(C) _Unwind_Reason_Code _d_eh_personality(int ver, _Unwind_Action actions, | |
| { | |
| // if we've gone through the list and found nothing... | |
| if (callsite_walker >= action_table) | |
| - return _Unwind_Reason_Code.CONTINUE_UNWIND; | |
| + return continueUnwind(ucb, context); | |
| block_start_offset = *cast(uint*)callsite_walker; | |
| block_size = *(cast(uint*)callsite_walker + 1); | |
| @@ -440,32 +572,32 @@ extern(C) _Unwind_Reason_Code _d_eh_personality(int ver, _Unwind_Action actions, | |
| landing_pad += region_start; | |
| callsite_walker = get_uleb128(callsite_walker + 3*uint.sizeof, action_offset); | |
| - debug(EH_personality_verbose) printf("ip=%llx %d %d %llx\n", ip, block_start_offset, block_size, landing_pad); | |
| + debug(EH_personality_verbose) printf(" -- ip=%tx %d %d %tx\n", ip, block_start_offset, block_size, landing_pad); | |
| // since the list is sorted, as soon as we're past the ip | |
| // there's no handler to be found | |
| if (ip < region_start + block_start_offset) | |
| - return _Unwind_Reason_Code.CONTINUE_UNWIND; | |
| + return continueUnwind(ucb, context); | |
| // if we've found our block, exit | |
| if (ip < region_start + block_start_offset + block_size) | |
| break; | |
| } | |
| - debug(EH_personality) printf("Found correct landing pad and actionOffset %d\n", action_offset); | |
| + debug(EH_personality) printf(" -- Found correct landing pad and actionOffset %d\n", action_offset); | |
| // now we need the exception's classinfo to find a handler | |
| // the exception_info is actually a member of a larger _d_exception struct | |
| // the runtime allocated. get that now | |
| - _d_exception* exception_struct = cast(_d_exception*)(cast(ubyte*)exception_info - _d_exception.unwind_info.offsetof); | |
| + _d_exception* exception_struct = ucb.toDException(); | |
| // if there's no action offset and no landing pad, continue unwinding | |
| if (!action_offset && !landing_pad) | |
| - return _Unwind_Reason_Code.CONTINUE_UNWIND; | |
| + return continueUnwind(ucb, context); | |
| // if there's no action offset but a landing pad, this is a cleanup handler | |
| else if(!action_offset && landing_pad) | |
| - return _d_eh_install_finally_context(actions, landing_pad, exception_struct, context); | |
| + return _d_eh_install_finally_context(actions, landing_pad, ucb, context); | |
| /* | |
| walk action table chain, comparing classinfos using _d_isbaseof | |
| @@ -473,11 +605,16 @@ extern(C) _Unwind_Reason_Code _d_eh_personality(int ver, _Unwind_Action actions, | |
| ubyte* action_walker = action_table + action_offset - 1; | |
| size_t ci_size = get_size_of_encoded_value(classinfo_table_encoding); | |
| + debug(EH_personality) printf(" -- ci_size: %td, ci_encoding: %d\n", ci_size, classinfo_table_encoding); | |
| + | |
| + size_t ttype_base = get_base_of_encoded_value(classinfo_table_encoding, context); | |
| + debug(EH_personality) printf(" -- ttype_base: 0x%tx\n", ttype_base); | |
| ptrdiff_t ti_offset, next_action_offset; | |
| while (true) | |
| { | |
| action_walker = get_sleb128(action_walker, ti_offset); | |
| + debug(EH_personality) printf(" -- ti_offset: %tx\n", ti_offset); | |
| // it is intentional that we not modify action_walker here | |
| // next_action_offset is from current action_walker position | |
| get_sleb128(action_walker, next_action_offset); | |
| @@ -491,26 +628,41 @@ extern(C) _Unwind_Reason_Code _d_eh_personality(int ver, _Unwind_Action actions, | |
| { | |
| if (!(next_action_offset == 0)) | |
| fatalerror("Cleanup action must be last in chain"); | |
| - return _d_eh_install_finally_context(actions, landing_pad, exception_struct, context); | |
| + return _d_eh_install_finally_context(actions, landing_pad, ucb, context); | |
| } | |
| // get classinfo for action and check if the one in the | |
| // exception structure is a base | |
| size_t catch_ci_ptr; | |
| get_encoded_value(classinfo_table - ti_offset * ci_size, catch_ci_ptr, classinfo_table_encoding, context); | |
| + debug(EH_personality) printf(" -- catch_ci_ptr: %p\n", catch_ci_ptr); | |
| ClassInfo catch_ci = cast(ClassInfo)cast(void*)catch_ci_ptr; | |
| - debug(EH_personality) printf("Comparing catch %s to exception %s\n", catch_ci.name.ptr, exception_struct.exception_object.classinfo.name.ptr); | |
| + auto ci = exception_struct.exception_object.classinfo; | |
| + debug(EH_personality) printf(" -- Comparing catch %p to exception %p (%s)\n", cast(void*)catch_ci, cast(void*)ci, ci.name.ptr); | |
| if (_d_isbaseof(exception_struct.exception_object.classinfo, catch_ci)) | |
| return _d_eh_install_catch_context(actions, ti_offset, landing_pad, exception_struct, context); | |
| + debug(EH_personality) printf(" -- Type mismatch, next action offset: %tx\n", next_action_offset); | |
| // we've walked through all actions and found nothing... | |
| if (next_action_offset == 0) | |
| - return _Unwind_Reason_Code.CONTINUE_UNWIND; | |
| + { | |
| + return continueUnwind(ucb, context); | |
| + } | |
| else | |
| + { | |
| action_walker += next_action_offset; | |
| + } | |
| } | |
| } | |
| +} | |
| +else | |
| +{ | |
| + // FIXME | |
| + static assert(0, "Need to merge ARM changes with x86 implementation."); | |
| +} | |
| + | |
| + | |
| // These are the register numbers for SetGR that | |
| // llvm's eh.exception and eh.selector intrinsics | |
| // will pick up. | |
| @@ -542,6 +694,11 @@ else version (PPC) | |
| private enum eh_exception_regno = 3; | |
| private enum eh_selector_regno = 4; | |
| } | |
| +else version (ARM) | |
| +{ | |
| + private enum eh_exception_regno = 0; | |
| + private enum eh_selector_regno = 1; | |
| +} | |
| else version (AArch64) | |
| { | |
| private enum eh_exception_regno = 0; | |
| @@ -573,31 +730,32 @@ private _Unwind_Reason_Code _d_eh_install_catch_context(_Unwind_Action actions, | |
| return _Unwind_Reason_Code.FATAL_PHASE2_ERROR; | |
| } | |
| -private _Unwind_Reason_Code _d_eh_install_finally_context(_Unwind_Action actions, ptrdiff_t landing_pad, _d_exception* exception_struct, _Unwind_Context_Ptr context) | |
| +private _Unwind_Reason_Code _d_eh_install_finally_context(_Unwind_Action actions, ptrdiff_t landing_pad, _Unwind_Control_Block* ucb, _Unwind_Context_Ptr context) | |
| { | |
| // if we're merely in search phase, continue | |
| if (actions & _Unwind_Action.SEARCH_PHASE) | |
| - return _Unwind_Reason_Code.CONTINUE_UNWIND; | |
| + return continueUnwind(ucb, context); | |
| debug(EH_personality) printf("Calling cleanup routine...\n"); | |
| - _Unwind_SetGR(context, eh_exception_regno, cast(ptrdiff_t)exception_struct); | |
| + _Unwind_SetGR(context, eh_exception_regno, cast(ptrdiff_t)ucb.toDException()); | |
| _Unwind_SetGR(context, eh_selector_regno, 0); | |
| _Unwind_SetIP(context, landing_pad); | |
| return _Unwind_Reason_Code.INSTALL_CONTEXT; | |
| } | |
| -private void _d_getLanguageSpecificTables(_Unwind_Context_Ptr context, ref ubyte* callsite, ref ubyte* action, ref ubyte* ci, ref ubyte ciEncoding) | |
| +private void _d_getLanguageSpecificTables(_Unwind_Context_Ptr context, ref ubyte* callsite, ref ubyte* action, ref ubyte* classinfo_table, ref ubyte ciEncoding) | |
| { | |
| ubyte* data = cast(ubyte*)_Unwind_GetLanguageSpecificData(context); | |
| if (data is null) | |
| { | |
| - //printf("language specific data was null\n"); | |
| + debug(EH_personality) printf("language specific data was null\n"); | |
| callsite = null; | |
| action = null; | |
| - ci = null; | |
| + classinfo_table = null; | |
| return; | |
| } | |
| + debug(EH_personality) printf(" -- LSDA: %p\n", data); | |
| //TODO: Do proper DWARF reading here | |
| if (*data++ != _DW_EH_Format.DW_EH_PE_omit) | |
| @@ -606,10 +764,15 @@ private void _d_getLanguageSpecificTables(_Unwind_Context_Ptr context, ref ubyte | |
| ciEncoding = *data++; | |
| if (ciEncoding == _DW_EH_Format.DW_EH_PE_omit) | |
| fatalerror("Language Specific Data does not contain Types Table"); | |
| + version (ARM) version (linux) { | |
| + with (_DW_EH_Format) { | |
| + ciEncoding = DW_EH_PE_pcrel | DW_EH_PE_indirect; | |
| + } | |
| + } | |
| size_t cioffset; | |
| data = get_uleb128(data, cioffset); | |
| - ci = data + cioffset; | |
| + classinfo_table = data + cioffset; | |
| if (*data++ != _DW_EH_Format.DW_EH_PE_udata4) | |
| fatalerror("DWARF header has unexpected format 2"); | |
| @@ -618,6 +781,8 @@ private void _d_getLanguageSpecificTables(_Unwind_Context_Ptr context, ref ubyte | |
| action = data + callsitelength; | |
| callsite = data; | |
| + | |
| + debug(EH_personality) printf(" -- callsite: %p, action: %p, classinfo_table: %p, ciEncoding: %d\n", callsite, action, classinfo_table, ciEncoding); | |
| } | |
| } // end of x86 Linux specific implementation | |
| @@ -628,7 +793,14 @@ extern(C) void _d_throw_exception(Object e) | |
| if (e !is null) | |
| { | |
| _d_exception* exc_struct = new _d_exception; | |
| - exc_struct.unwind_info.exception_class = *cast(ulong*)_d_exception_class.ptr; | |
| + version (ARM) | |
| + { | |
| + exc_struct.unwind_info.exception_class = _d_exception_class; | |
| + } | |
| + else | |
| + { | |
| + exc_struct.unwind_info.exception_class = *cast(ulong*)_d_exception_class.ptr; | |
| + } | |
| exc_struct.exception_object = e; | |
| debug(EH_personality) printf("throw exception %p\n", e); | |
| _Unwind_Reason_Code ret = _Unwind_RaiseException(&exc_struct.unwind_info); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment