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
parent
f983cdbf68
commit
6bd3db5866
@ -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;
|
||||
}
|
||||
@ -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…
Reference in New Issue