Skip to content

Instantly share code, notes, and snippets.

@dnadlinger
Last active December 26, 2015 08:39
Show Gist options
  • Select an option

  • Save dnadlinger/7124007 to your computer and use it in GitHub Desktop.

Select an option

Save dnadlinger/7124007 to your computer and use it in GitHub Desktop.
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