MinGW: Use _Unwind_RaiseException to throw exceptions (#278)

The current implementation uses Vectored Exception Handlers. This implementation is too greedy, and invokes _objc_unexpected_exception for (certain) exceptions which would be handled by the application itself.
main
Frederik Carlier 2 years ago committed by GitHub
parent f983cdbf68
commit 6bd3db5866
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -124,9 +124,7 @@ set(libobjc_CXX_SRCS
# Windows does not use DWARF EH, except when using the GNU ABI (MinGW)
if (WIN32 AND NOT MINGW)
list(APPEND libobjc_CXX_SRCS eh_win32_msvc.cc)
elseif (MINGW)
list(APPEND libobjc_CXX_SRCS eh_win32_mingw.m)
else ()
elseif (NOT MINGW)
list(APPEND libobjc_C_SRCS eh_personality.c)
endif ()
@ -230,7 +228,7 @@ if (WIN32 AND NOT MINGW)
message(STATUS "Using MSVC-compatible exception model")
elseif (MINGW)
message(STATUS "Using MinGW-compatible exception model")
list(APPEND libobjc_CXX_SRCS objcxx_eh.cc)
list(APPEND libobjc_CXX_SRCS objcxx_eh.cc objcxx_eh_mingw.cc)
else ()
separate_arguments(EH_PERSONALITY_FLAGS NATIVE_COMMAND ${CMAKE_CXX_FLAGS})
if (CMAKE_CXX_COMPILER_TARGET)

@ -1,59 +0,0 @@
#include "objc/runtime.h"
#include "objc/objc-exception.h"
#include "objc/hooks.h"
#include <stdio.h>
#include <windows.h>
#define STATUS_GCC_THROW 0x20474343
extern void *__cxa_current_exception_type(void);
extern void __cxa_rethrow();
BOOL handler_installed = NO;
_Thread_local BOOL in_handler = NO;
// This vectored exception handler is the last handler to get invoke for every exception (Objective C or foreign).
// It calls _objc_unexpected_exception only when the exception is a C++ exception (ex->ExceptionCode == STATUS_GCC_THROW)
// and the exception is an Objective C exception.
// It always returns EXCEPTION_CONTINUE_SEARCH, so Windows will continue handling the exception.
static LONG CALLBACK _objc_vectored_exception_handler(EXCEPTION_POINTERS* exceptionInfo)
{
const EXCEPTION_RECORD* ex = exceptionInfo->ExceptionRecord;
if (_objc_unexpected_exception != 0
&& ex->ExceptionCode == STATUS_GCC_THROW
&& !in_handler)
{
// Rethrow the current exception and use the @catch clauses to determine whether it's an Objective C exception
// or a foreign exception.
if (__cxa_current_exception_type()) {
in_handler = YES;
@try {
__cxa_rethrow();
} @catch (id e) {
// Invoke _objc_unexpected_exception for Objective C exceptions
(*_objc_unexpected_exception)((id)e);
} @catch (...) {
// Ignore foreign exceptions.
}
in_handler = NO;
}
}
// EXCEPTION_CONTINUE_SEARCH instructs the exception handler to continue searching for appropriate exception handlers.
return EXCEPTION_CONTINUE_SEARCH;
}
OBJC_PUBLIC extern objc_uncaught_exception_handler objc_setUncaughtExceptionHandler(objc_uncaught_exception_handler handler)
{
objc_uncaught_exception_handler previousHandler = __atomic_exchange_n(&_objc_unexpected_exception, handler, __ATOMIC_SEQ_CST);
// Add a vectored exception handler to support the hook. We only need to do this once.
if (!handler_installed) {
AddVectoredExceptionHandler(0 /* The handler is the last handler to be called */ , _objc_vectored_exception_handler);
handler_installed = YES;
}
return previousHandler;
}

@ -1,19 +1,11 @@
typedef struct objc_object* id;
#include <atomic>
#include <stdlib.h>
#include <stdio.h>
#include "dwarf_eh.h"
#include "objcxx_eh_private.h"
#include "objcxx_eh.h"
#include "visibility.h"
#include "objc/runtime.h"
#include "objc/objc-arc.h"
#ifndef DEBUG_EXCEPTIONS
#define DEBUG_LOG(...)
#else
#define DEBUG_LOG(str, ...) fprintf(stderr, str, ## __VA_ARGS__)
#endif
/**
* Helper function that has a custom personality function.
* This calls `cxx_throw` and has a destructor that must be run. We intercept
@ -23,92 +15,8 @@ int eh_trampoline();
uint64_t cxx_exception_class;
/**
* Our own definitions of C++ ABI functions and types. These are provided
* because this file must not include cxxabi.h. We need to handle subtly
* different variations of the ABI and including one specific implementation
* would make that very difficult.
*/
namespace __cxxabiv1
{
/**
* Type info for classes. Forward declared because the GNU ABI provides a
* method on all type_info objects that the dynamic the dynamic cast header
* needs.
*/
struct __class_type_info;
/**
* The C++ in-flight exception object. We will derive the offset of fields
* in this, so we do not ever actually see a concrete definition of it.
*/
struct __cxa_exception;
/**
* The public ABI structure for current exception state.
*/
struct __cxa_eh_globals
{
/**
* The current exception that has been caught.
*/
__cxa_exception *caughtExceptions;
/**
* The number of uncaught exceptions still in flight.
*/
unsigned int uncaughtExceptions;
};
/**
* Retrieve the above structure.
*/
extern "C" __cxa_eh_globals *__cxa_get_globals();
}
namespace std
{
struct type_info;
}
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
// type_info class (the part required for standards compliance, not the
// implementation details).
typedef void (*unexpected_handler)();
typedef void (*terminate_handler)();
namespace std
{
/**
* 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
{
public:
virtual ~type_info();
bool operator==(const type_info &) const;
bool operator!=(const type_info &) const;
bool before(const type_info &) const;
type_info();
private:
type_info(const type_info& rhs);
type_info& operator= (const type_info& rhs);
const char *__type_name;
protected:
type_info(const char *name): __type_name(name) { }
public:
const char* name() const { return __type_name; }
};
}
extern "C" void __cxa_throw(void*, std::type_info*, void(*)(void*));
extern "C" void __cxa_rethrow();
namespace
{
/**
@ -140,49 +48,6 @@ std::atomic<ptrdiff_t> type_info_offset;
*/
std::atomic<size_t> exception_struct_size;
/**
* Helper function to find a particular value scanning backwards in a
* structure.
*/
template<typename T>
ptrdiff_t find_backwards(void *addr, T val)
{
T *ptr = reinterpret_cast<T*>(addr);
for (ptrdiff_t disp = -1 ; (disp * sizeof(T) > -128) ; disp--)
{
if (ptr[disp] == val)
{
return disp * sizeof(T);
}
}
fprintf(stderr, "Unable to find field in C++ exception structure\n");
abort();
}
/**
* Helper function to find a particular value scanning forwards in a
* structure.
*/
template<typename T>
ptrdiff_t find_forwards(void *addr, T val)
{
T *ptr = reinterpret_cast<T*>(addr);
for (ptrdiff_t disp = 0 ; (disp * sizeof(T) < 256) ; disp++)
{
if (ptr[disp] == val)
{
return disp * sizeof(T);
}
}
fprintf(stderr, "Unable to find field in C++ exception structure\n");
abort();
}
template<typename T>
T *pointer_add(void *ptr, ptrdiff_t offset)
{
return reinterpret_cast<T*>(reinterpret_cast<char*>(ptr) + offset);
}
/**
* Exception cleanup function for C++ exceptions that wrap Objective-C
@ -225,76 +90,33 @@ namespace gnustep
{
namespace libobjc
{
/**
* Superclass for the type info for Objective-C exceptions.
*/
struct OBJC_PUBLIC __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,
__objc_type_info::__objc_type_info(const char *name) : type_info(name) {}
bool __objc_type_info::__is_pointer_p() const { return true; }
bool __objc_type_info::__is_function_p() const { return false; }
bool __objc_type_info::__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(
{
assert(0);
return false;
};
bool __objc_type_info::__do_upcast(
const __class_type_info *target,
void **thrown_object) const
{
return false;
};
{
return false;
};
/**
* Singleton type info for the `id` type.
* The `id` type is mangled to `@id`, which is not a valid mangling
* of anything else.
*/
struct OBJC_PUBLIC __objc_id_type_info : __objc_type_info
{
/**
* 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_PUBLIC __objc_class_type_info : __objc_type_info
{
virtual ~__objc_class_type_info();
virtual bool __do_catch(const type_info *thrownType,
void **obj,
unsigned outer) const;
};
__objc_id_type_info::__objc_id_type_info() : __objc_type_info("@id") {};
}
static inline id dereference_thrown_object_pointer(void** obj) {
@ -430,29 +252,7 @@ void* objc_object_for_cxx_exception(void *thrown_exception, int *isValid)
} // extern "C"
/**
* C++ structure that is thrown through a frame with the `test_eh_personality`
* personality function. This contains a well-known value that we can search
* for after the unwind header.
*/
struct
PRIVATE
MagicValueHolder
{
/**
* The constant that we will search for to identify this object.
*/
static constexpr uint32_t magic = 0x01020304;
/**
* The single field in this structure.
*/
uint32_t magic_value;
/**
* Constructor. Initialises the field with the magic constant.
*/
MagicValueHolder() { magic_value = magic; }
};
MagicValueHolder::MagicValueHolder() { magic_value = magic; }
/**
* Function that simply throws an instance of `MagicValueHolder`.
@ -508,21 +308,4 @@ extern "C" void test_cxx_eh_implementation()
}
assert(caught);
}
#else
static void eh_cleanup(void *exception)
{
DEBUG_LOG("eh_cleanup: Releasing 0x%x\n", *(id*)exception);
objc_release(*(id*)exception);
}
extern "C"
OBJC_PUBLIC
void objc_exception_throw(id object)
{
id *exc = (id *)__cxa_allocate_exception(sizeof(id));
*exc = object;
objc_retain(object);
DEBUG_LOG("objc_exception_throw: Throwing 0x%x\n", *exc);
__cxa_throw(exc, & __objc_id_type_info, eh_cleanup);
}
#endif

@ -0,0 +1,98 @@
#include <atomic>
#include <stdlib.h>
#include <stdio.h>
#include "dwarf_eh.h"
#include "objcxx_eh_private.h"
#include "objcxx_eh.h"
#include "objc/runtime.h"
#include "objc/objc-arc.h"
#include "objc/objc-exception.h"
#include "objc/hooks.h"
namespace __cxxabiv1
{
struct __cxa_refcounted_exception
{
int referenceCount;
};
}
using namespace __cxxabiv1;
extern "C" __cxa_refcounted_exception* __cxa_init_primary_exception(void *obj, std::type_info *tinfo, void (*dest) (void *));
static void eh_cleanup(void *exception)
{
DEBUG_LOG("eh_cleanup: Releasing 0x%x\n", *(id*)exception);
objc_release(*(id*)exception);
}
/**
* Flag indicating that we've already inspected a C++ exception and found all
* of the offsets.
*/
std::atomic<bool> done_setup;
/**
* The size of the `_Unwind_Exception` (including padding) in a
* `__cxa_exception`.
*/
std::atomic<size_t> exception_struct_size;
extern "C"
OBJC_PUBLIC
void objc_exception_throw(id object)
{
#ifdef __GLIBCXX__
// Don't bother with a mutex here. It doesn't matter if two threads set
// these values at the same time.
if (!done_setup)
{
DEBUG_LOG("objc_exception_throw: Doing initial setup\n");
MagicValueHolder *magicExc = (MagicValueHolder *)__cxa_allocate_exception(sizeof(MagicValueHolder));
MagicValueHolder x;
*magicExc = x;
__cxa_refcounted_exception *header =
__cxa_init_primary_exception(magicExc, & __objc_id_type_info, NULL);
exception_struct_size = find_forwards(header, MagicValueHolder::magic);
__cxa_free_exception(magicExc);
DEBUG_LOG("objc_exception_throw: exception_struct_size: 0x%x\n", unsigned(exception_struct_size));
done_setup = true;
}
#endif
id *exc = (id *)__cxa_allocate_exception(sizeof(id));
*exc = object;
objc_retain(object);
DEBUG_LOG("objc_exception_throw: Throwing 0x%x\n", *exc);
#ifndef __GLIBCXX__
// At the moment, only libstdc++ exposes __cxa_init_primary_exception.
__cxa_throw(exc, & __objc_id_type_info, eh_cleanup);
#else
__cxa_eh_globals *globals = __cxa_get_globals ();
globals->uncaughtExceptions += 1;
__cxa_refcounted_exception *header =
__cxa_init_primary_exception(exc, & __objc_id_type_info, eh_cleanup);
header->referenceCount = 1;
_Unwind_Exception *unwindHeader = pointer_add<_Unwind_Exception>(header, exception_struct_size - sizeof(_Unwind_Exception));
_Unwind_Reason_Code err = _Unwind_RaiseException (unwindHeader);
if (_URC_END_OF_STACK == err && 0 != _objc_unexpected_exception)
{
DEBUG_LOG("Invoking _objc_unexpected_exception\n");
_objc_unexpected_exception(object);
}
DEBUG_LOG("Throw returned %d\n",(int) err);
abort();
#endif
}
OBJC_PUBLIC extern objc_uncaught_exception_handler objc_setUncaughtExceptionHandler(objc_uncaught_exception_handler handler)
{
return __atomic_exchange_n(&_objc_unexpected_exception, handler, __ATOMIC_SEQ_CST);
}

@ -0,0 +1,241 @@
typedef struct objc_object* id;
#include "objc/runtime.h"
#include "visibility.h"
#ifndef DEBUG_EXCEPTIONS
#define DEBUG_LOG(...)
#else
#define DEBUG_LOG(str, ...) fprintf(stderr, str, ## __VA_ARGS__)
#endif
/**
* Our own definitions of C++ ABI functions and types. These are provided
* because this file must not include cxxabi.h. We need to handle subtly
* different variations of the ABI and including one specific implementation
* would make that very difficult.
*/
namespace __cxxabiv1
{
/**
* Type info for classes. Forward declared because the GNU ABI provides a
* method on all type_info objects that the dynamic the dynamic cast header
* needs.
*/
struct __class_type_info;
/**
* The C++ in-flight exception object. We will derive the offset of fields
* in this, so we do not ever actually see a concrete definition of it.
*/
struct __cxa_exception;
/**
* The public ABI structure for current exception state.
*/
struct __cxa_eh_globals
{
/**
* The current exception that has been caught.
*/
__cxa_exception *caughtExceptions;
/**
* The number of uncaught exceptions still in flight.
*/
unsigned int uncaughtExceptions;
};
/**
* Retrieve the above structure.
*/
extern "C" __cxa_eh_globals *__cxa_get_globals();
}
namespace std
{
struct type_info;
}
// Define some C++ ABI types here, rather than including them. This prevents
// conflicts with the libstdc++ headers, which expose only a subset of the
// type_info class (the part required for standards compliance, not the
// implementation details).
typedef void (*unexpected_handler)();
typedef void (*terminate_handler)();
namespace std
{
/**
* 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
{
public:
virtual ~type_info();
bool operator==(const type_info &) const;
bool operator!=(const type_info &) const;
bool before(const type_info &) const;
type_info();
private:
type_info(const type_info& rhs);
type_info& operator= (const type_info& rhs);
const char *__type_name;
protected:
type_info(const char *name): __type_name(name) { }
public:
const char* name() const { return __type_name; }
};
}
extern "C" void __cxa_throw(void*, std::type_info*, void(*)(void*));
extern "C" void __cxa_rethrow();
/**
* Helper function to find a particular value scanning backwards in a
* structure.
*/
template<typename T>
ptrdiff_t find_backwards(void *addr, T val)
{
T *ptr = reinterpret_cast<T*>(addr);
for (ptrdiff_t disp = -1 ; (disp * sizeof(T) > -128) ; disp--)
{
if (ptr[disp] == val)
{
return disp * sizeof(T);
}
}
fprintf(stderr, "Unable to find field in C++ exception structure\n");
abort();
}
/**
* Helper function to find a particular value scanning forwards in a
* structure.
*/
template<typename T>
ptrdiff_t find_forwards(void *addr, T val)
{
T *ptr = reinterpret_cast<T*>(addr);
for (ptrdiff_t disp = 0 ; (disp * sizeof(T) < 256) ; disp++)
{
if (ptr[disp] == val)
{
return disp * sizeof(T);
}
}
fprintf(stderr, "Unable to find field in C++ exception structure\n");
abort();
}
template<typename T>
T *pointer_add(void *ptr, ptrdiff_t offset)
{
return reinterpret_cast<T*>(reinterpret_cast<char*>(ptr) + offset);
}
namespace gnustep
{
namespace libobjc
{
/**
* Superclass for the type info for Objective-C exceptions.
*/
struct OBJC_PUBLIC __objc_type_info : std::type_info
{
/**
* Constructor that sets the name.
*/
__objc_type_info(const char *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;
/**
* 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;
/**
* 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;
/**
* 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 __cxxabiv1::__class_type_info *target,
void **thrown_object) const;
};
/**
* Singleton type info for the `id` type.
*/
struct OBJC_PUBLIC __objc_id_type_info : __objc_type_info
{
__objc_id_type_info();
virtual ~__objc_id_type_info();
virtual bool __do_catch(const type_info *thrownType,
void **obj,
unsigned outer) const;
};
struct OBJC_PUBLIC __objc_class_type_info : __objc_type_info
{
virtual ~__objc_class_type_info();
virtual bool __do_catch(const type_info *thrownType,
void **obj,
unsigned outer) const;
};
}
}
/**
* Public interface to the Objective-C++ exception mechanism
*/
extern "C"
{
/**
* The public symbol that the compiler uses to indicate the Objective-C id type.
*/
extern OBJC_PUBLIC gnustep::libobjc::__objc_id_type_info __objc_id_type_info;
} // extern "C"
/**
* C++ structure that is thrown through a frame with the `test_eh_personality`
* personality function. This contains a well-known value that we can search
* for after the unwind header.
*/
struct
PRIVATE
MagicValueHolder
{
/**
* The constant that we will search for to identify the MagicValueHolder object.
*/
static constexpr uint32_t magic = 0x01020304;
/**
* The single field in this structure.
*/
uint32_t magic_value;
/**
* Constructor. Initialises the field with the magic constant.
*/
MagicValueHolder();
};
Loading…
Cancel
Save