From ec5c0bcae264987873a4cfb9a300affaf2985897 Mon Sep 17 00:00:00 2001 From: David Chisnall Date: Sat, 2 May 2020 10:50:17 +0100 Subject: [PATCH] More C++ exception fixes. * Make sure the unwind state is synchronised between the Objective-C and C++ exception objects. * Reintroduce the is-pointer callback so that `__cxa_begin_catch` adjusts the pointer correctly. Objective-C++ code uses `__cxa_begin_catch` without the `objc_begin_catch` wrapper and the runtime does not call the `__do_catch` method on the type info if the type info is an exact match, so the caught object ended up being a pointer to the object. This also meant that we needed to remove the double dereference in the `__do_catch` methods. * Introduce a subclass of `std::type_info` for all Objective-C types and move the `virtual` functions there. This should simplify supporting libc++abi. --- eh_personality.c | 13 +++++--- objcxx_eh.cc | 87 ++++++++++++++++++++++++++++++++++++++---------- 2 files changed, 78 insertions(+), 22 deletions(-) diff --git a/eh_personality.c b/eh_personality.c index b74ad76..eaf0060 100644 --- a/eh_personality.c +++ b/eh_personality.c @@ -503,11 +503,17 @@ BEGIN_PERSONALITY_FUNCTION(__gnustep_objcxx_personality_v0) if (0 == ex->cxx_exception) { ex->cxx_exception = objc_init_cxx_exception(ex->object); - ex->cxx_exception->private_1 = exceptionObject->private_1; - ex->cxx_exception->private_2 = exceptionObject->private_2; } + // We now have two copies of the _Unwind_Exception object (which stores + // state for the unwinder) in flight. Make sure that they're in sync. + ex->cxx_exception->private_1 = exceptionObject->private_1; + ex->cxx_exception->private_2 = exceptionObject->private_2; exceptionObject = ex->cxx_exception; exceptionClass = cxx_exception_class; + int ret = CALL_PERSONALITY_FUNCTION(__gxx_personality_v0); + exceptionObject->private_1 = ex->cxx_exception->private_1; + exceptionObject->private_2 = ex->cxx_exception->private_2; + return ret; } return CALL_PERSONALITY_FUNCTION(__gxx_personality_v0); } @@ -588,8 +594,7 @@ id objc_begin_catch(struct _Unwind_Exception *exceptionObject) { DEBUG_LOG("c++ catch\n"); td->current_exception_type = CXX; - id *obj = __cxa_begin_catch(exceptionObject); - return obj ? *obj : nil; + return __cxa_begin_catch(exceptionObject); } DEBUG_LOG("foreign exception catch\n"); // Box if we have a boxing function. diff --git a/objcxx_eh.cc b/objcxx_eh.cc index 25466ab..a60c8ce 100644 --- a/objcxx_eh.cc +++ b/objcxx_eh.cc @@ -75,9 +75,12 @@ typedef void (*terminate_handler)(); namespace std { /** - * std::type_info defined with the GCC ABI. This may not be exposed in - * public headers, but is required for correctly implementing the unified - * exception model. + * std::type_info, containing the minimum requirements for the ABI. + * Public headers on some implementations also expose some implementation + * details. The layout of our subclasses must respect the layout of the + * C++ runtime library, but also needs to be portable across multiple + * implementations and so should not depend on internal symbols from those + * libraries. */ class type_info { @@ -95,14 +98,6 @@ namespace std type_info(const char *name): __type_name(name) { } public: const char* name() const { return __type_name; } - virtual bool __is_pointer_p() const; - virtual bool __is_function_p() const; - virtual bool __do_catch(const type_info *thrown_type, - void **thrown_object, - unsigned outer) const; - virtual bool __do_upcast( - const __class_type_info *target, - void **thrown_object) const; }; } @@ -226,15 +221,71 @@ namespace gnustep { namespace libobjc { - struct __objc_id_type_info : std::type_info + /** + * Superclass for the type info for Objective-C exceptions. + */ + struct __objc_type_info : std::type_info + { + /** + * Constructor that sets the name. + */ + __objc_type_info(const char *name) : type_info(name) {} + /** + * Helper function used by libsupc++ and libcxxrt to determine if + * this is a pointer type. If so, catches automatically + * dereference the pointer to the thrown pointer in + * `__cxa_begin_catch`. + */ + virtual bool __is_pointer_p() const { return true; } + /** + * Helper function used by libsupc++ and libcxxrt to determine if + * this is a function pointer type. Irrelevant for our purposes. + */ + virtual bool __is_function_p() const { return false; } + /** + * Catch handler. This is used in the C++ personality function. + * `thrown_type` is the type info of the thrown object, `this` is + * the type info at the catch site. `thrown_object` is a pointer + * to a pointer to the thrown object and may be adjusted by this + * function. + */ + virtual bool __do_catch(const type_info *thrown_type, + void **thrown_object, + unsigned) const + { + assert(0); + return false; + }; + /** + * Function used for `dynamic_cast` between two C++ class types in + * libsupc++ and libcxxrt. + * + * This should never be called on Objective-C types. + */ + virtual bool __do_upcast( + const __class_type_info *target, + void **thrown_object) const + { + assert(0); + return false; + }; + }; + /** + * Singleton type info for the `id` type. + */ + struct __objc_id_type_info : __objc_type_info { - __objc_id_type_info() : type_info("@id") {}; + /** + * The `id` type is mangled to `@id`, which is not a valid mangling + * of anything else. + */ + __objc_id_type_info() : __objc_type_info("@id") {}; virtual ~__objc_id_type_info(); virtual bool __do_catch(const type_info *thrownType, void **obj, unsigned outer) const; }; - struct __objc_class_type_info : std::type_info + struct __objc_class_type_info : __objc_type_info { virtual ~__objc_class_type_info(); virtual bool __do_catch(const type_info *thrownType, @@ -266,7 +317,7 @@ bool gnustep::libobjc::__objc_class_type_info::__do_catch(const type_info *throw || (AppleCompatibleMode && dynamic_cast(thrownType))) { - thrown = **(id**)obj; + thrown = *(id*)obj; // nil only matches id catch handlers in Apple-compatible mode, or when thrown as an id if (0 == thrown) { @@ -278,7 +329,7 @@ bool gnustep::libobjc::__objc_class_type_info::__do_catch(const type_info *throw } else if (dynamic_cast(thrownType)) { - thrown = **(id**)obj; + thrown = *(id*)obj; found = isKindOfClass((Class)objc_getClass(thrownType->name()), (Class)objc_getClass(name())); } @@ -296,12 +347,12 @@ bool gnustep::libobjc::__objc_id_type_info::__do_catch(const type_info *thrownTy // Id catch matches any ObjC throw if (dynamic_cast(thrownType)) { - *obj = **(id**)obj; + *obj = *(id*)obj; return true; } if (dynamic_cast(thrownType)) { - *obj = **(id**)obj; + *obj = *(id*)obj; return true; } return false;