Skip to content

Commit

Permalink
Implement ARM EH support, fix objc_msgSend() to work on ARM.
Browse files Browse the repository at this point in the history
  • Loading branch information
theraven committed Nov 9, 2011
1 parent 2a3a93e commit 9490b1b
Show file tree
Hide file tree
Showing 8 changed files with 489 additions and 197 deletions.
2 changes: 1 addition & 1 deletion GNUmakefile
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ LIBOBJC = libobjc
LIBOBJCLIBNAME = objc
LIBOBJCXX = libobjcxx

LIBRARY_NAME = ${LIBOBJC} ${LIBOBJCXX}
LIBRARY_NAME = ${LIBOBJC}

${LIBOBJC}_OBJC_FILES = \
NSBlocks.m\
Expand Down
5 changes: 4 additions & 1 deletion Test/objc_msgSend.m
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
#include <class.h>
#include <stdarg.h>

//#define assert(x) if (!(x)) { printf("Failed %d\n", __LINE__); }

id objc_msgSend(id, SEL, ...);

typedef struct { int a,b,c,d,e; } s;
Expand Down Expand Up @@ -48,6 +50,7 @@ + (void)printf: (const char*)str, ...

vasprintf(&s, str, ap);
va_end(ap);
//fprintf(stderr, "String: '%s'\n", s);
assert(strcmp(s, "Format string 42 42.000000\n") ==0);
}
+ (void)initialize
Expand All @@ -68,7 +71,7 @@ int main(void)
assert((TestCls == e) && "Exceptions propagate out of +initialize");
exceptionThrown = 1;
}
assert(exceptionThrown);
assert(exceptionThrown && "An exception was thrown");
assert((id)0x42 == objc_msgSend(TestCls, @selector(foo)));
objc_msgSend(TestCls, @selector(nothing));
objc_msgSend(TestCls, @selector(missing));
Expand Down
125 changes: 99 additions & 26 deletions eh_personality.c
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,68 @@ typedef enum
handler_class
} handler_type;

/**
* Saves the result of the landing pad that we have found. For ARM, this is
* stored in the generic unwind structure, while on other platforms it is
* stored in the Objective-C exception.
*/
static void saveLandingPad(struct _Unwind_Context *context,
struct _Unwind_Exception *ucb,
struct objc_exception *ex,
int selector,
dw_eh_ptr_t landingPad)
{
#ifdef __arm__
// On ARM, we store the saved exception in the generic part of the structure
ucb->barrier_cache.sp = _Unwind_GetGR(context, 13);
ucb->barrier_cache.bitpattern[1] = (uint32_t)selector;
ucb->barrier_cache.bitpattern[3] = (uint32_t)landingPad;
#else
// Cache the results for the phase 2 unwind, if we found a handler
// and this is not a foreign exception. We can't cache foreign exceptions
// because we don't know their structure (although we could cache C++
// exceptions...)
if (ex)
{
ex->handlerSwitchValue = selector;
ex->landingPad = landingPad;
}
#endif
}

/**
* Loads the saved landing pad. Returns 1 on success, 0 on failure.
*/
static int loadLandingPad(struct _Unwind_Context *context,
struct _Unwind_Exception *ucb,
struct objc_exception *ex,
unsigned long *selector,
dw_eh_ptr_t *landingPad)
{
#ifdef __arm__
*selector = ucb->barrier_cache.bitpattern[1];
*landingPad = (dw_eh_ptr_t)ucb->barrier_cache.bitpattern[3];
return 1;
#else
if (ex)
{
*selector = ex->handlerSwitchValue;
*landingPad = ex->landingPad;
return 0;
}
return 0;
#endif
}

static inline _Unwind_Reason_Code continueUnwinding(struct _Unwind_Exception *ex,
struct _Unwind_Context *context)
{
#ifdef __arm__
if (__gnu_unwind_frame(ex, context) != _URC_OK) { return _URC_FAILURE; }
#endif
return _URC_CONTINUE_UNWIND;
}

static void cleanup(_Unwind_Reason_Code reason, struct _Unwind_Exception *e)
{
/*
Expand Down Expand Up @@ -92,6 +154,7 @@ void objc_exception_throw(id object)
{
_objc_unexpected_exception(object);
}
fprintf(stderr, "Throw returned %d\n",(int) err);
abort();
}

Expand Down Expand Up @@ -199,15 +262,33 @@ static handler_type check_action_record(struct _Unwind_Context *context,
return handler_none;
}

_Unwind_Reason_Code
__gnu_objc_personality_v01(_Unwind_State state,
struct _Unwind_Exception *ue_header,
struct _Unwind_Context *context)
{
fprintf(stderr, "LSDA: %p\n", (void*)_Unwind_GetLanguageSpecificData(context));
unsigned char *lsda_addr = (void*)_Unwind_GetLanguageSpecificData(context);
fprintf(stderr, "Encoding: %x\n", (int)*lsda_addr);
return 0;

}
typedef uint32_t _uw;

/**
* The Objective-C exception personality function.
*/
_Unwind_Reason_Code __gnu_objc_personality_v0(int version,
_Unwind_Action actions,
uint64_t exceptionClass,
struct _Unwind_Exception *exceptionObject,
struct _Unwind_Context *context)
{
BEGIN_PERSONALITY_FUNCTION(__gnu_objc_personality_v0)
fprintf(stderr, "Personality function called\n");

_uw *ptr = (_uw *) exceptionObject->pr_cache.ehtp;
/* Skip the personality routine address. */
ptr++;
/* Skip the unwind opcodes. */
ptr += (((*ptr) >> 24) & 0xff) + 1;
fprintf(stderr, "Maybe this is the LSDA? 0x%x\n", ptr);


// This personality function is for version 1 of the ABI. If you use it
// with a future version of the ABI, it won't know what to do, so it
// reports a fatal error and give up before it breaks anything.
Expand All @@ -216,8 +297,9 @@ _Unwind_Reason_Code __gnu_objc_personality_v0(int version,
return _URC_FATAL_PHASE1_ERROR;
}
struct objc_exception *ex = 0;

//char *cls = (char*)&exceptionClass;
#ifndef fprintf
char *cls = (char*)&exceptionClass;
#endif
fprintf(stderr, "Class: %c%c%c%c%c%c%c%c\n", cls[7], cls[6], cls[5], cls[4], cls[3], cls[2], cls[1], cls[0]);

// Check if this is a foreign exception. If it is a C++ exception, then we
Expand Down Expand Up @@ -266,6 +348,7 @@ _Unwind_Reason_Code __gnu_objc_personality_v0(int version,
fprintf(stderr, "Foreign class: %p\n", thrown_class);
}
unsigned char *lsda_addr = (void*)_Unwind_GetLanguageSpecificData(context);
fprintf(stderr, "LSDA: %p\n", lsda_addr);

// No LSDA implies no landing pads - try the next frame
if (0 == lsda_addr) { return _URC_CONTINUE_UNWIND; }
Expand All @@ -276,6 +359,7 @@ _Unwind_Reason_Code __gnu_objc_personality_v0(int version,

if (actions & _UA_SEARCH_PHASE)
{
fprintf(stderr, "Search phase...\n");
struct dwarf_eh_lsda lsda = parse_lsda(context, lsda_addr);
action = dwarf_eh_find_callsite(context, &lsda);
handler_type handler = check_action_record(context, foreignException,
Expand All @@ -286,13 +370,7 @@ _Unwind_Reason_Code __gnu_objc_personality_v0(int version,
((handler == handler_catchall_id) && !foreignException) ||
(handler == handler_catchall))
{
// Cache the results for the phase 2 unwind, if we found a handler
// and this is not a foreign exception.
if (ex)
{
ex->handlerSwitchValue = selector;
ex->landingPad = action.landing_pad;
}
saveLandingPad(context, exceptionObject, ex, selector, action.landing_pad);
fprintf(stderr, "Found handler! %d\n", handler);
return _URC_HANDLER_FOUND;
}
Expand Down Expand Up @@ -353,8 +431,7 @@ _Unwind_Reason_Code __gnu_objc_personality_v0(int version,
else
{
// Restore the saved info if we saved some last time.
action.landing_pad = ex->landingPad;
selector = ex->handlerSwitchValue;
loadLandingPad(context, exceptionObject, ex, &selector, &action.landing_pad);
object = ex->object;
free(ex);
}
Expand All @@ -367,12 +444,9 @@ _Unwind_Reason_Code __gnu_objc_personality_v0(int version,
return _URC_INSTALL_CONTEXT;
}

_Unwind_Reason_Code __gnustep_objcxx_personality_v0(int version,
_Unwind_Action actions,
uint64_t exceptionClass,
struct _Unwind_Exception *exceptionObject,
struct _Unwind_Context *context)
{
// FIXME!
#ifndef __arm__
BEGIN_PERSONALITY_FUNCTION(__gnustep_objcxx_personality_v0)
if (exceptionClass == objc_exception_class)
{
struct objc_exception *ex = (struct objc_exception*)
Expand All @@ -391,7 +465,6 @@ _Unwind_Reason_Code __gnustep_objcxx_personality_v0(int version,
exceptionObject = ex->cxx_exception;
exceptionClass = cxx_exception_class;
}
return __gxx_personality_v0(version, actions, exceptionClass,
exceptionObject, context);
return CALL_PERSONALITY_FUNCTION(__gxx_personality_v0);
}

#endif
23 changes: 13 additions & 10 deletions objc_msgSend.arm.S
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#define DATA_OFFSET 12
#define SLOT_OFFSET 16
.syntax unified
.fpu neon

// Macro for testing: logs a register value to standard error
.macro LOG reg
Expand All @@ -14,14 +15,15 @@
.endm

.macro MSGSEND receiver, sel
.cfi_startproc
.fnstart
teq \receiver, 0
beq 4f // Skip everything if the receiver is nil
push {r4-r6} // We're going to use these three as
.cfi_adjust_cfa_offset 12 // scratch registers, so save them now.
.cfi_rel_offset r4, 0 // These are callee-save, so the unwind library
.cfi_rel_offset r5, 4 // must be able to restore them, so we need CFI
.cfi_rel_offset r6, 8 // directives for them, but not for any other pushes
.save {r4-r6}
// scratch registers, so save them now.
// These are callee-save, so the unwind library
// must be able to restore them, so we need CFI
// directives for them, but not for any other pushes
tst \receiver, SMALLOBJ_MASK // Sets Z if this is not a small int


Expand Down Expand Up @@ -73,21 +75,22 @@
mov r1, 0
mov pc, lr
5: // Slow lookup
push {r0-r3, lr} // Save anything that will be clobbered by the call
push {r0-r4, lr} // Save anything that will be clobbered by the call
.save {r0-r4, lr}


push {\receiver} // &self, _cmd in arguments
mov r0, sp
.save {\receiver}
mov r1, \sel

.cfi_adjust_cfa_offset 24 // pushed 6 more 4-byte words onto the stack.
bl slowMsgLookup(PLT) // This is the only place where the CFI directives have to be accurate...
mov ip, r0 // IMP -> ip

pop {r5} // restore (modified) self to r5
pop {r0-r3, lr} // Load clobbered registers
pop {r0-r4, lr} // Load clobbered registers
mov \receiver, r5
b 3b
.cfi_endproc
.fnend
.endm

.globl objc_msgSend
Expand Down
7 changes: 1 addition & 6 deletions objcxx_eh.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,7 @@ struct _Unwind_Exception *objc_init_cxx_exception(void *thrown_exception);
* The GNU C++ exception personality function, provided by libsupc++ (GNU) or
* libcxxrt (PathScale).
*/
__attribute__((weak))
_Unwind_Reason_Code __gxx_personality_v0(int version,
_Unwind_Action actions,
uint64_t exceptionClass,
struct _Unwind_Exception *exceptionObject,
struct _Unwind_Context *context);
__attribute__((weak)) DECLARE_PERSONALITY_FUNCTION(__gxx_personality_v0);
/**
* Frees an exception object allocated by __cxa_allocate_exception(). Part of
* the Itanium C++ ABI.
Expand Down
Loading

0 comments on commit 9490b1b

Please sign in to comment.