Rework Objective-C++ exceptions.

The new version now does not depend on the layout of the C++ exception
structure and instead finds the two offsets that it cares about.

Fixes #108
main
David Chisnall 6 years ago committed by David Chisnall
parent 8e26f54e3e
commit 242442b3aa

@ -25,7 +25,6 @@
* Class of exceptions to distinguish between this and other exception types. * Class of exceptions to distinguish between this and other exception types.
*/ */
static const uint64_t objc_exception_class = EXCEPTION_CLASS('G','N','U','C','O','B','J','C'); static const uint64_t objc_exception_class = EXCEPTION_CLASS('G','N','U','C','O','B','J','C');
static const uint64_t cxx_exception_class = EXCEPTION_CLASS('G','N','U','C','C','+','+','\0');
/** /**
* Structure used as a header on thrown exceptions. * Structure used as a header on thrown exceptions.
@ -492,12 +491,7 @@ BEGIN_PERSONALITY_FUNCTION(__gnustep_objcxx_personality_v0)
struct objc_exception *ex = objc_exception_from_header(exceptionObject); struct objc_exception *ex = objc_exception_from_header(exceptionObject);
if (0 == ex->cxx_exception) if (0 == ex->cxx_exception)
{ {
id *newEx = __cxa_allocate_exception(sizeof(id)); ex->cxx_exception = objc_init_cxx_exception(ex->object);
*newEx = ex->object;
ex->cxx_exception = objc_init_cxx_exception(newEx);
memcpy(ex->cxx_exception, exceptionObject, sizeof(struct _Unwind_Exception));
ex->cxx_exception->exception_class = cxx_exception_class;
ex->cxx_exception->exception_cleanup = cleanup;
} }
exceptionObject = ex->cxx_exception; exceptionObject = ex->cxx_exception;
exceptionClass = cxx_exception_class; exceptionClass = cxx_exception_class;

@ -1,16 +1,24 @@
typedef struct objc_object* id;
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
#include "dwarf_eh.h" #include "dwarf_eh.h"
#include "objcxx_eh.h" #include "objcxx_eh.h"
#include <cxxabi.h>
#include <atomic>
#include "objc/runtime.h" #include "objc/runtime.h"
namespace __cxxabiv1 namespace __cxxabiv1
{ {
struct __class_type_info; struct __class_type_info;
} }
using __cxxabiv1::__class_type_info; extern "C" void
__cxa_throw(void *thrown_exception, std::type_info *tinfo,
void (*dest)(void *));
using namespace __cxxabiv1;
// Define some C++ ABI types here, rather than including them. This prevents // Define some C++ ABI types here, rather than including them. This prevents
// conflicts with the libstdc++ headers, which expose only a subset of the // conflicts with the libstdc++ headers, which expose only a subset of the
@ -56,6 +64,9 @@ namespace std
using namespace std; using namespace std;
static std::atomic<ptrdiff_t> exception_object_offset;
static std::atomic<ptrdiff_t> exception_type_offset;
static BOOL isKindOfClass(Class thrown, Class type) static BOOL isKindOfClass(Class thrown, Class type)
{ {
@ -71,29 +82,6 @@ static BOOL isKindOfClass(Class thrown, Class type)
return NO; return NO;
} }
/**
* C++ Exception structure. From the Itanium ABI spec
*/
struct __cxa_exception
{
std::type_info *exceptionType;
void (*exceptionDestructor) (void *);
unexpected_handler unexpectedHandler;
terminate_handler terminateHandler;
__cxa_exception *nextException;
int handlerCount;
#if defined(__arm__) && !defined(__ARM_DWARF_EH__)
_Unwind_Exception *nextCleanup;
int cleanupCount;
#endif
int handlerSwitchValue;
const char *actionRecord;
const char *languageSpecificData;
void *catchTemp;
void *adjustedPtr;
_Unwind_Exception unwindHeader;
};
@ -134,7 +122,7 @@ bool gnustep::libobjc::__objc_class_type_info::__do_catch(const type_info *throw
void **obj, void **obj,
unsigned outer) const unsigned outer) const
{ {
id thrown = (id)obj; id thrown = nullptr;
bool found = false; bool found = false;
// Id throw matches any ObjC catch. This may be a silly idea! // Id throw matches any ObjC catch. This may be a silly idea!
if (dynamic_cast<const __objc_id_type_info*>(thrownType) if (dynamic_cast<const __objc_id_type_info*>(thrownType)
@ -192,42 +180,60 @@ extern "C"
*/ */
gnustep::libobjc::__objc_id_type_info __objc_id_type_info; gnustep::libobjc::__objc_id_type_info __objc_id_type_info;
/** struct _Unwind_Exception *objc_init_cxx_exception(id obj)
* Exception cleanup function for C++ exceptions that wrap Objective-C
* exceptions.
*/
static void exception_cleanup(_Unwind_Reason_Code reason,
struct _Unwind_Exception *ex)
{ {
__cxa_exception *cxxex = (__cxa_exception*) ((char*)ex - offsetof(struct __cxa_exception, unwindHeader)); void *cxxexception = nullptr;
if (cxxex->exceptionType != &__objc_id_type_info) try
{ {
delete cxxex->exceptionType; id *exception_object = static_cast<id*>(__cxa_allocate_exception(sizeof(id)));
*exception_object = obj;
__cxa_throw(exception_object, &__objc_id_type_info, nullptr);
} }
__cxa_free_exception((void*)ex); catch (...)
} {
cxxexception = __cxa_current_primary_exception();
struct _Unwind_Exception *objc_init_cxx_exception(void *thrown_exception) }
{ assert(cxxexception);
__cxa_exception *ex = ((__cxa_exception*)thrown_exception) - 1; uint64_t *ehcls = reinterpret_cast<uint64_t*>(cxxexception);
ehcls--;
std::type_info *tinfo = &__objc_id_type_info; int count = 1;
while (*ehcls != cxx_exception_class)
ex->exceptionType = tinfo; {
ehcls--;
count++;
assert((count < 8) && "Exception structure appears to be corrupt");
}
ptrdiff_t displacement = reinterpret_cast<const char*>(cxxexception) - reinterpret_cast<const char*>(ehcls);
assert((exception_object_offset == 0) || (exception_object_offset == displacement));
ex->exceptionDestructor = 0; exception_object_offset = displacement;
ex->unwindHeader.exception_class = EXCEPTION_CLASS('G','N','U','C','C','+','+','\0'); std::type_info **ehtype = reinterpret_cast<std::type_info**>(ehcls);
ex->unwindHeader.exception_cleanup = exception_cleanup; ehtype--;
count = 1;
while (*ehtype != &__objc_id_type_info)
{
ehtype--;
count++;
assert((count < 32) && "Exception structure appears to be corrupt");
}
displacement = reinterpret_cast<const char*>(ehtype) - reinterpret_cast<const char*>(ehcls);
assert((exception_type_offset == 0) || (exception_type_offset == displacement));
return &ex->unwindHeader; exception_type_offset = displacement;
return reinterpret_cast<_Unwind_Exception*>(ehcls);
} }
void* objc_object_for_cxx_exception(void *thrown_exception, int *isValid) void* objc_object_for_cxx_exception(void *thrown_exception, int *isValid)
{ {
__cxa_exception *ex = (__cxa_exception*) ((char*)thrown_exception - ptrdiff_t type_offset = exception_type_offset;
offsetof(struct __cxa_exception, unwindHeader)); if (type_offset == 0)
const std::type_info *thrownType = ex->exceptionType; {
*isValid = 0;
return nullptr;
}
const std::type_info *thrownType =
*reinterpret_cast<const std::type_info**>(reinterpret_cast<char*>(thrown_exception) + type_offset);
if (!dynamic_cast<const gnustep::libobjc::__objc_id_type_info*>(thrownType) && if (!dynamic_cast<const gnustep::libobjc::__objc_id_type_info*>(thrownType) &&
!dynamic_cast<const gnustep::libobjc::__objc_class_type_info*>(thrownType)) !dynamic_cast<const gnustep::libobjc::__objc_class_type_info*>(thrownType))
{ {
@ -235,7 +241,7 @@ void* objc_object_for_cxx_exception(void *thrown_exception, int *isValid)
return 0; return 0;
} }
*isValid = 1; *isValid = 1;
return *(id*)(ex+1); return *reinterpret_cast<id*>(reinterpret_cast<char*>(thrown_exception) + exception_object_offset);
} }
/* /*

@ -24,7 +24,7 @@ void *__cxa_allocate_exception(size_t thrown_size) CXA_ALLOCATE_EXCEPTION_SPECIF
* the C++ personality function. * the C++ personality function.
*/ */
__attribute__((weak)) __attribute__((weak))
struct _Unwind_Exception *objc_init_cxx_exception(void *thrown_exception); struct _Unwind_Exception *objc_init_cxx_exception(id thrown_exception);
/** /**
* The GNU C++ exception personality function, provided by libsupc++ (GNU) or * The GNU C++ exception personality function, provided by libsupc++ (GNU) or
* libcxxrt (PathScale). * libcxxrt (PathScale).
@ -51,6 +51,7 @@ void *objc_object_for_cxx_exception(void *thrown_exception, int *isValid);
__attribute__((weak)) __attribute__((weak))
void print_type_info(void *thrown_exception); void print_type_info(void *thrown_exception);
static const uint64_t cxx_exception_class = EXCEPTION_CLASS('G','N','U','C','C','+','+','\0');
#ifdef __cplusplus #ifdef __cplusplus
} }

Loading…
Cancel
Save