Skip to content

Commit

Permalink
MinGW: Use _Unwind_RaiseException to throw exceptions (#278)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
qmfrederik authored Feb 21, 2024
1 parent f983cdb commit 6bd3db5
Show file tree
Hide file tree
Showing 5 changed files with 363 additions and 302 deletions.
6 changes: 2 additions & 4 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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 ()

Expand Down Expand Up @@ -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)
Expand Down
59 changes: 0 additions & 59 deletions eh_win32_mingw.m

This file was deleted.

261 changes: 22 additions & 239 deletions objcxx_eh.cc
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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
{
/**
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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`.
Expand Down Expand Up @@ -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
Loading

0 comments on commit 6bd3db5

Please sign in to comment.