From 242442b3aa1d0e438a6893ba5d8b472c9634d293 Mon Sep 17 00:00:00 2001 From: David Chisnall Date: Sat, 23 Nov 2019 17:02:14 +0000 Subject: [PATCH] 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 --- eh_personality.c | 8 +--- objcxx_eh.cc | 110 +++++++++++++++++++++++++---------------------- objcxx_eh.h | 3 +- 3 files changed, 61 insertions(+), 60 deletions(-) diff --git a/eh_personality.c b/eh_personality.c index 410d177..82d2c6e 100644 --- a/eh_personality.c +++ b/eh_personality.c @@ -25,7 +25,6 @@ * 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 cxx_exception_class = EXCEPTION_CLASS('G','N','U','C','C','+','+','\0'); /** * 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); if (0 == ex->cxx_exception) { - id *newEx = __cxa_allocate_exception(sizeof(id)); - *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; + ex->cxx_exception = objc_init_cxx_exception(ex->object); } exceptionObject = ex->cxx_exception; exceptionClass = cxx_exception_class; diff --git a/objcxx_eh.cc b/objcxx_eh.cc index 275e48c..1bc8bf3 100644 --- a/objcxx_eh.cc +++ b/objcxx_eh.cc @@ -1,16 +1,24 @@ +typedef struct objc_object* id; #include #include #include "dwarf_eh.h" #include "objcxx_eh.h" +#include +#include #include "objc/runtime.h" + namespace __cxxabiv1 { 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 // conflicts with the libstdc++ headers, which expose only a subset of the @@ -56,6 +64,9 @@ namespace std using namespace std; +static std::atomic exception_object_offset; +static std::atomic exception_type_offset; + static BOOL isKindOfClass(Class thrown, Class type) { @@ -71,29 +82,6 @@ static BOOL isKindOfClass(Class thrown, Class type) 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, unsigned outer) const { - id thrown = (id)obj; + id thrown = nullptr; bool found = false; // Id throw matches any ObjC catch. This may be a silly idea! if (dynamic_cast(thrownType) @@ -192,42 +180,60 @@ extern "C" */ gnustep::libobjc::__objc_id_type_info __objc_id_type_info; -/** - * Exception cleanup function for C++ exceptions that wrap Objective-C - * exceptions. - */ -static void exception_cleanup(_Unwind_Reason_Code reason, - struct _Unwind_Exception *ex) +struct _Unwind_Exception *objc_init_cxx_exception(id obj) { - __cxa_exception *cxxex = (__cxa_exception*) ((char*)ex - offsetof(struct __cxa_exception, unwindHeader)); - if (cxxex->exceptionType != &__objc_id_type_info) + void *cxxexception = nullptr; + try { - delete cxxex->exceptionType; + id *exception_object = static_cast(__cxa_allocate_exception(sizeof(id))); + *exception_object = obj; + __cxa_throw(exception_object, &__objc_id_type_info, nullptr); } - __cxa_free_exception((void*)ex); -} - -struct _Unwind_Exception *objc_init_cxx_exception(void *thrown_exception) -{ - __cxa_exception *ex = ((__cxa_exception*)thrown_exception) - 1; - - std::type_info *tinfo = &__objc_id_type_info; - - ex->exceptionType = tinfo; + catch (...) + { + cxxexception = __cxa_current_primary_exception(); + } + assert(cxxexception); + uint64_t *ehcls = reinterpret_cast(cxxexception); + ehcls--; + int count = 1; + while (*ehcls != cxx_exception_class) + { + ehcls--; + count++; + assert((count < 8) && "Exception structure appears to be corrupt"); + } + ptrdiff_t displacement = reinterpret_cast(cxxexception) - reinterpret_cast(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'); - ex->unwindHeader.exception_cleanup = exception_cleanup; + std::type_info **ehtype = reinterpret_cast(ehcls); + ehtype--; + count = 1; + while (*ehtype != &__objc_id_type_info) + { + ehtype--; + count++; + assert((count < 32) && "Exception structure appears to be corrupt"); + } + displacement = reinterpret_cast(ehtype) - reinterpret_cast(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) { - __cxa_exception *ex = (__cxa_exception*) ((char*)thrown_exception - - offsetof(struct __cxa_exception, unwindHeader)); - const std::type_info *thrownType = ex->exceptionType; + ptrdiff_t type_offset = exception_type_offset; + if (type_offset == 0) + { + *isValid = 0; + return nullptr; + } + const std::type_info *thrownType = + *reinterpret_cast(reinterpret_cast(thrown_exception) + type_offset); if (!dynamic_cast(thrownType) && !dynamic_cast(thrownType)) { @@ -235,7 +241,7 @@ void* objc_object_for_cxx_exception(void *thrown_exception, int *isValid) return 0; } *isValid = 1; - return *(id*)(ex+1); + return *reinterpret_cast(reinterpret_cast(thrown_exception) + exception_object_offset); } /* diff --git a/objcxx_eh.h b/objcxx_eh.h index 024283a..1a117df 100644 --- a/objcxx_eh.h +++ b/objcxx_eh.h @@ -24,7 +24,7 @@ void *__cxa_allocate_exception(size_t thrown_size) CXA_ALLOCATE_EXCEPTION_SPECIF * the C++ personality function. */ __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 * libcxxrt (PathScale). @@ -51,6 +51,7 @@ void *objc_object_for_cxx_exception(void *thrown_exception, int *isValid); __attribute__((weak)) void print_type_info(void *thrown_exception); +static const uint64_t cxx_exception_class = EXCEPTION_CLASS('G','N','U','C','C','+','+','\0'); #ifdef __cplusplus }