/*
* Copyright (c) 2008-2024 Jonathan Schleifer <js@nil.im>
*
* All rights reserved.
*
* This file is part of ObjFW. It may be distributed under the terms of the
* Q Public License 1.0, which can be found in the file LICENSE.QPL included in
* the packaging of this file.
*
* Alternatively, it may be distributed under the terms of the GNU General
* Public License, either version 2 or 3, which can be found in the file
* LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
* file.
*/
#include "config.h"
#define OBJC_NO_PERSONALITY_DECLARATION
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#import "ObjFWRT.h"
#import "private.h"
#import "macros.h"
#ifdef OF_HAVE_THREADS
# import "OFPlainMutex.h"
#endif
#ifdef HAVE_SEH_EXCEPTIONS
# include <windows.h>
#endif
#if defined(HAVE_DWARF_EXCEPTIONS)
# define PERSONALITY __gnu_objc_personality_v0
# define CXX_PERSONALITY_STR "__gxx_personality_v0"
#elif defined(HAVE_SJLJ_EXCEPTIONS)
# define PERSONALITY __gnu_objc_personality_sj0
# define CXX_PERSONALITY_STR "__gxx_personality_sj0"
# define _Unwind_RaiseException _Unwind_SjLj_RaiseException
# define __builtin_eh_return_data_regno(i) (i)
#elif defined(HAVE_SEH_EXCEPTIONS)
# define PERSONALITY gnu_objc_personality
#else
# error Unknown exception type!
#endif
#if defined(OF_ARM) && !defined(__ARM_DWARF_EH__)
# define HAVE_ARM_EHABI_EXCEPTIONS
#endif
#ifndef HAVE_ARM_EHABI_EXCEPTIONS
# define PERSONALITY_FUNC(func) \
_Unwind_Reason_Code \
func(int version, int actions, uint64_t exClass, \
struct _Unwind_Exception *ex, struct _Unwind_Context *ctx)
# define CALL_PERSONALITY(func) func(version, actions, exClass, ex, ctx)
#else
# define PERSONALITY_FUNC(func) \
_Unwind_Reason_Code \
func(uint32_t state, struct _Unwind_Exception *ex, \
struct _Unwind_Context *ctx)
# define CALL_PERSONALITY(func) func(state, ex, ctx)
#endif
#define GNUCOBJC_EXCEPTION_CLASS UINT64_C(0x474E55434F424A43) /* GNUCOBJC */
#define GNUCCXX0_EXCEPTION_CLASS UINT64_C(0x474E5543432B2B00) /* GNUCC++\0 */
#define CLNGCXX0_EXCEPTION_CLASS UINT64_C(0x434C4E47432B2B00) /* CLNGC++\0 */
#define numEmergencyExceptions 4
enum {
_UA_SEARCH_PHASE = 0x01,
_UA_CLEANUP_PHASE = 0x02,
_UA_HANDLER_FRAME = 0x04,
_UA_FORCE_UNWIND = 0x08
};
enum {
DW_EH_PE_absptr = 0x00,
DW_EH_PE_uleb128 = 0x01,
DW_EH_PE_udata2 = 0x02,
DW_EH_PE_udata4 = 0x03,
DW_EH_PE_udata8 = 0x04,
DW_EH_PE_signed = 0x08,
DW_EH_PE_sleb128 = (DW_EH_PE_signed | DW_EH_PE_uleb128),
DW_EH_PE_sdata2 = (DW_EH_PE_signed | DW_EH_PE_udata2),
DW_EH_PE_sdata4 = (DW_EH_PE_signed | DW_EH_PE_udata4),
DW_EH_PE_sdata8 = (DW_EH_PE_signed | DW_EH_PE_udata8),
DW_EH_PE_pcrel = 0x10,
DW_EH_PE_textrel = 0x20,
DW_EH_PE_datarel = 0x30,
DW_EH_PE_funcrel = 0x40,
DW_EH_PE_aligned = 0x50,
DW_EH_PE_indirect = 0x80,
DW_EH_PE_omit = 0xFF
};
enum {
CLEANUP_FOUND = 0x01,
HANDLER_FOUND = 0x02
};
struct _Unwind_Context;
typedef enum {
_URC_OK = 0,
_URC_FATAL_PHASE1_ERROR = 3,
_URC_END_OF_STACK = 5,
_URC_HANDLER_FOUND = 6,
_URC_INSTALL_CONTEXT = 7,
_URC_CONTINUE_UNWIND = 8,
_URC_FAILURE = 9
} _Unwind_Reason_Code;
struct objc_exception {
struct _Unwind_Exception {
uint64_t class;
void (*cleanup)(
_Unwind_Reason_Code, struct _Unwind_Exception *);
#ifndef HAVE_ARM_EHABI_EXCEPTIONS
# ifndef HAVE_SEH_EXCEPTIONS
/*
* The Itanium Exception ABI says to have those and never touch
* them.
*/
uint64_t private1, private2;
# else
uint64_t private[6];
# endif
#else
/* From "Exception Handling ABI for the ARM(R) Architecture" */
struct {
uint32_t reserved1, reserved2, reserved3, reserved4;
uint32_t reserved;
} unwinderCache;
struct {
uint32_t sp;
uint32_t bitPattern[5];
} barrierCache;
struct {
uint32_t bitPattern[4];
} cleanupCache;
struct {
uint32_t fnstart;
uint32_t *ehtp;
uint32_t additional;
uint32_t reserved1;
} PRCache;
long long int : 0;
#endif
} exception;
id object;
#ifndef HAVE_ARM_EHABI_EXCEPTIONS
uintptr_t landingpad;
intptr_t filter;
#endif
};
struct LSDA {
uintptr_t regionStart, landingpadsStart;
uint8_t typesTableEnc;
const uint8_t *typesTable;
uintptr_t typesTableBase;
uint8_t callsitesEnc;
const uint8_t *callsites, *actionTable;
};
extern _Unwind_Reason_Code _Unwind_RaiseException(struct _Unwind_Exception *);
extern void _Unwind_DeleteException(struct _Unwind_Exception *);
extern void *_Unwind_GetLanguageSpecificData(struct _Unwind_Context *);
extern uintptr_t _Unwind_GetRegionStart(struct _Unwind_Context *);
#ifdef HAVE__UNWIND_GETDATARELBASE
extern uintptr_t _Unwind_GetDataRelBase(struct _Unwind_Context *);
#endif
#ifdef HAVE__UNWIND_GETTEXTRELBASE
extern uintptr_t _Unwind_GetTextRelBase(struct _Unwind_Context *);
#endif
#ifndef HAVE_ARM_EHABI_EXCEPTIONS
# define CONTINUE_UNWIND return _URC_CONTINUE_UNWIND
extern uintptr_t _Unwind_GetIP(struct _Unwind_Context *);
extern uintptr_t _Unwind_GetGR(struct _Unwind_Context *, int);
extern void _Unwind_SetIP(struct _Unwind_Context *, uintptr_t);
extern void _Unwind_SetGR(struct _Unwind_Context *, int, uintptr_t);
#else
extern _Unwind_Reason_Code __gnu_unwind_frame(struct _Unwind_Exception *,
struct _Unwind_Context *);
extern int _Unwind_VRS_Get(struct _Unwind_Context *, int, uint32_t, int,
void *);
extern int _Unwind_VRS_Set(struct _Unwind_Context *, int, uint32_t, int,
void *);
# define CONTINUE_UNWIND \
{ \
if (__gnu_unwind_frame(ex, ctx) != _URC_OK) \
return _URC_FAILURE; \
\
return _URC_CONTINUE_UNWIND; \
}
static inline uintptr_t
_Unwind_GetGR(struct _Unwind_Context *ctx, int regNo)
{
uintptr_t value;
_Unwind_VRS_Get(ctx, 0, regNo, 0, &value);
return value;
}
static inline uintptr_t
_Unwind_GetIP(struct _Unwind_Context *ctx)
{
return _Unwind_GetGR(ctx, 15) & ~1;
}
static inline void
_Unwind_SetGR(struct _Unwind_Context *ctx, int regNo, uintptr_t value)
{
_Unwind_VRS_Set(ctx, 0, regNo, 0, &value);
}
static inline void
_Unwind_SetIP(struct _Unwind_Context *ctx, uintptr_t value)
{
uintptr_t thumb = _Unwind_GetGR(ctx, 15) & 1;
_Unwind_SetGR(ctx, 15, (value | thumb));
}
#endif
#ifdef CXX_PERSONALITY
static PERSONALITY_FUNC(cxx_personality) OF_WEAK_REF(CXX_PERSONALITY_STR);
#endif
#ifdef HAVE_SEH_EXCEPTIONS
extern EXCEPTION_DISPOSITION _GCC_specific_handler(PEXCEPTION_RECORD, void *,
PCONTEXT, PDISPATCHER_CONTEXT, _Unwind_Reason_Code (*)(int, int, uint64_t,
struct _Unwind_Exception *, struct _Unwind_Context *));
#endif
static objc_uncaught_exception_handler uncaughtExceptionHandler;
static struct objc_exception emergencyExceptions[numEmergencyExceptions];
#ifdef OF_HAVE_THREADS
static OFSpinlock emergencyExceptionsSpinlock;
OF_CONSTRUCTOR()
{
if (OFSpinlockNew(&emergencyExceptionsSpinlock) != 0)
OBJC_ERROR("Failed to create spinlock!");
}
#endif
static uint64_t
readULEB128(const uint8_t **ptr)
{
uint64_t value = 0;
uint8_t shift = 0;
do {
value |= (**ptr & 0x7F) << shift;
(*ptr)++;
shift += 7;
} while (*(*ptr - 1) & 0x80);
return value;
}
static int64_t
readSLEB128(const uint8_t **ptr)
{
const uint8_t *oldPtr = *ptr;
uint8_t bits;
int64_t value;
value = readULEB128(ptr);
bits = (*ptr - oldPtr) * 7;
if (bits < 64 && value & (INT64_C(1) << (bits - 1)))
value |= -(INT64_C(1) << bits);
return value;
}
static uintptr_t
getBase(struct _Unwind_Context *ctx, uint8_t enc)
{
if (enc == DW_EH_PE_omit)
return 0;
switch (enc & 0x70) {
case DW_EH_PE_absptr:
case DW_EH_PE_pcrel:
case DW_EH_PE_aligned:
return 0;
case DW_EH_PE_funcrel:
return _Unwind_GetRegionStart(ctx);
#ifdef HAVE__UNWIND_GETDATARELBASE
case DW_EH_PE_datarel:
return _Unwind_GetDataRelBase(ctx);
#else
case DW_EH_PE_datarel:
return _Unwind_GetGR(ctx, 1);
#endif
#ifdef HAVE__UNWIND_GETTEXTRELBASE
case DW_EH_PE_textrel:
return _Unwind_GetTextRelBase(ctx);
#endif
}
OBJC_ERROR("Unknown encoding!");
}
static size_t
sizeForEncoding(uint8_t enc)
{
if (enc == DW_EH_PE_omit)
return 0;
switch (enc & 0x07) {
case DW_EH_PE_absptr:
return sizeof(void *);
case DW_EH_PE_udata2:
return 2;
case DW_EH_PE_udata4:
return 4;
case DW_EH_PE_udata8:
return 8;
}
OBJC_ERROR("Unknown encoding!");
}
static uint64_t
readValue(uint8_t enc, const uint8_t **ptr)
{
uint64_t value;
if (enc == DW_EH_PE_aligned) {
const uintptr_t *aligned = (const uintptr_t *)
OFRoundUpToPowerOf2(sizeof(void *), (uintptr_t)*ptr);
*ptr = (const uint8_t *)(aligned + 1);
return *aligned;
}
#define READ(type) \
{ \
type tmp; \
memcpy(&tmp, *ptr, sizeof(type)); \
value = tmp; \
*ptr += sizeForEncoding(enc); \
break; \
}
switch (enc & 0x0F) {
case DW_EH_PE_absptr:
READ(uintptr_t)
case DW_EH_PE_uleb128:
value = readULEB128(ptr);
break;
case DW_EH_PE_udata2:
READ(uint16_t)
case DW_EH_PE_udata4:
READ(uint32_t)
case DW_EH_PE_udata8:
READ(uint64_t)
case DW_EH_PE_sleb128:
value = readSLEB128(ptr);
break;
case DW_EH_PE_sdata2:
READ(int16_t)
case DW_EH_PE_sdata4:
READ(int32_t)
case DW_EH_PE_sdata8:
READ(int64_t)
default:
OBJC_ERROR("Unknown encoding!");
}
#undef READ
return value;
}
#ifndef HAVE_ARM_EHABI_EXCEPTIONS
static uint64_t
resolveValue(uint64_t value, uint8_t enc, const uint8_t *start, uint64_t base)
{
if (value == 0 || enc == DW_EH_PE_aligned)
return value;
value += ((enc & 0x70) == DW_EH_PE_pcrel ? (uintptr_t)start : base);
if (enc & DW_EH_PE_indirect)
value = *(uintptr_t *)(uintptr_t)value;
return value;
}
#endif
static void
readLSDA(struct _Unwind_Context *ctx, const uint8_t *ptr, struct LSDA *LSDA)
{
uint8_t landingpadsStartEnc;
uintptr_t callsitesSize;
LSDA->regionStart = _Unwind_GetRegionStart(ctx);
LSDA->landingpadsStart = LSDA->regionStart;
LSDA->typesTable = NULL;
if ((landingpadsStartEnc = *ptr++) != DW_EH_PE_omit)
LSDA->landingpadsStart =
(uintptr_t)readValue(landingpadsStartEnc, &ptr);
if ((LSDA->typesTableEnc = *ptr++) != DW_EH_PE_omit) {
uintptr_t tmp = (uintptr_t)readULEB128(&ptr);
LSDA->typesTable = ptr + tmp;
}
LSDA->typesTableBase = getBase(ctx, LSDA->typesTableEnc);
LSDA->callsitesEnc = *ptr++;
callsitesSize = (uintptr_t)readULEB128(&ptr);
LSDA->callsites = ptr;
LSDA->actionTable = LSDA->callsites + callsitesSize;
}
static bool
findCallsite(struct _Unwind_Context *ctx, struct LSDA *LSDA,
uintptr_t *landingpad, const uint8_t **actionRecords)
{
uintptr_t IP = _Unwind_GetIP(ctx);
const uint8_t *ptr = LSDA->callsites;
*landingpad = 0;
*actionRecords = NULL;
#ifndef HAVE_SJLJ_EXCEPTIONS
while (ptr < LSDA->actionTable) {
uintptr_t callsiteStart, callsiteLength, callsiteLandingpad;
uintptr_t callsiteAction;
callsiteStart = LSDA->regionStart +
(uintptr_t)readValue(LSDA->callsitesEnc, &ptr);
callsiteLength = (uintptr_t)readValue(LSDA->callsitesEnc, &ptr);
callsiteLandingpad =
(uintptr_t)readValue(LSDA->callsitesEnc, &ptr);
callsiteAction = (uintptr_t)readULEB128(&ptr);
/* We can stop if we passed IP, as the table is sorted */
if (callsiteStart >= IP)
break;
if (callsiteStart + callsiteLength >= IP) {
if (callsiteLandingpad != 0)
*landingpad = LSDA->landingpadsStart +
callsiteLandingpad;
if (callsiteAction != 0)
*actionRecords = LSDA->actionTable +
callsiteAction - 1;
return true;
}
}
return false;
#else
uintptr_t callsiteLandingpad, callsiteAction;
if ((intptr_t)IP < 1)
return false;
do {
callsiteLandingpad = (uintptr_t)readULEB128(&ptr);
callsiteAction = (uintptr_t)readULEB128(&ptr);
} while (--IP > 1);
*landingpad = callsiteLandingpad + 1;
if (callsiteAction != 0)
*actionRecords = LSDA->actionTable + callsiteAction - 1;
return true;
#endif
}
static bool
classMatches(Class class, id object)
{
Class iter;
if (class == Nil)
return true;
if (object == nil)
return false;
for (iter = object_getClass(object); iter != Nil;
iter = class_getSuperclass(iter))
if (iter == class)
return true;
return false;
}
static uint8_t
findActionRecord(const uint8_t *actionRecords, struct LSDA *LSDA, int actions,
bool foreign, struct objc_exception *e, intptr_t *filterPtr)
{
const uint8_t *ptr;
intptr_t filter, displacement;
do {
ptr = actionRecords;
filter = (intptr_t)readSLEB128(&ptr);
/*
* Get the next action record. Since readSLEB128() modifies ptr,
* we first set the actionrecord to the current ptr and then
* add the displacement.
*/
actionRecords = ptr;
displacement = (intptr_t)readSLEB128(&ptr);
actionRecords += displacement;
if (filter > 0 && !(actions & _UA_FORCE_UNWIND) && !foreign) {
Class class;
const char *className;
uintptr_t c;
const uint8_t *tmp;
#ifndef HAVE_ARM_EHABI_EXCEPTIONS
uintptr_t i;
i = filter * sizeForEncoding(LSDA->typesTableEnc);
tmp = LSDA->typesTable - i;
c = (uintptr_t)readValue(LSDA->typesTableEnc, &tmp);
c = (uintptr_t)resolveValue(c, LSDA->typesTableEnc,
LSDA->typesTable - i, LSDA->typesTableBase);
#else
tmp = LSDA->typesTable - (filter * 4);
c = *(uintptr_t *)(void *)tmp;
if (c != 0) {
c += (uintptr_t)tmp;
# if defined(OF_LINUX) || defined(OF_NETBSD)
c = *(uintptr_t *)c;
# endif
}
#endif
className = (const char *)c;
if (className != NULL && *className != '\0' &&
strcmp(className, "@id") != 0)
class = objc_getRequiredClass(className);
else
class = Nil;
if (classMatches(class, e->object)) {
*filterPtr = filter;
return HANDLER_FOUND;
}
} else if (filter == 0)
return CLEANUP_FOUND;
else if (filter < 0)
OBJC_ERROR("Invalid filter!");
} while (displacement != 0);
return 0;
}
#ifdef HAVE_SEH_EXCEPTIONS
static
#endif
PERSONALITY_FUNC(PERSONALITY)
{
#ifdef HAVE_ARM_EHABI_EXCEPTIONS
int version = 1;
uint64_t exClass = ex->class;
int actions;
switch (state) {
case 0: /* _US_VIRTUAL_UNWIND_FRAME */
actions = _UA_SEARCH_PHASE;
break;
case 1: /* _US_UNWIND_FRAME_STARTING */
actions = _UA_CLEANUP_PHASE;
if ((ex->barrierCache.sp == _Unwind_GetGR(ctx, 13)) != 0)
actions |= _UA_HANDLER_FRAME;
break;
case 2: /* _US_UNWIND_FRAME_RESUME */
CONTINUE_UNWIND;
default:
return _URC_FAILURE;
}
_Unwind_SetGR(ctx, 12, (uintptr_t)ex);
#endif
struct objc_exception *e = (struct objc_exception *)ex;
bool foreign = (exClass != GNUCOBJC_EXCEPTION_CLASS);
const uint8_t *LSDAAddr, *actionRecords;
struct LSDA LSDA;
uintptr_t landingpad = 0;
uint8_t found = 0;
intptr_t filter = 0;
if (foreign) {
switch (exClass) {
#ifdef CXX_PERSONALITY
case GNUCCXX0_EXCEPTION_CLASS:
case CLNGCXX0_EXCEPTION_CLASS:
if (cxx_personality != NULL)
return CALL_PERSONALITY(cxx_personality);
break;
#endif
}
/*
* None matched or none available - we'll try to handle it
* anyway, but will most likely fail.
*/
}
if (version != 1 || ctx == NULL)
return _URC_FATAL_PHASE1_ERROR;
/*
* We already cached everything we found in phase 1, so we only need
* to install the context in phase 2.
*/
if (actions & _UA_HANDLER_FRAME && !foreign) {
/*
* For handlers, reg #0 must be the exception's object and reg
* #1 the filter.
*/
_Unwind_SetGR(ctx, __builtin_eh_return_data_regno(0),
(uintptr_t)e->object);
#ifndef HAVE_ARM_EHABI_EXCEPTIONS
_Unwind_SetGR(ctx, __builtin_eh_return_data_regno(1),
e->filter);
_Unwind_SetIP(ctx, e->landingpad);
#else
_Unwind_SetGR(ctx, __builtin_eh_return_data_regno(1),
ex->barrierCache.bitPattern[1]);
_Unwind_SetIP(ctx, ex->barrierCache.bitPattern[3]);
#endif
_Unwind_DeleteException(ex);
return _URC_INSTALL_CONTEXT;
}
/* No LSDA -> nothing to handle */
if ((LSDAAddr = _Unwind_GetLanguageSpecificData(ctx)) == NULL)
CONTINUE_UNWIND;
readLSDA(ctx, LSDAAddr, &LSDA);
if (!findCallsite(ctx, &LSDA, &landingpad, &actionRecords))
CONTINUE_UNWIND;
if (landingpad != 0 && actionRecords != NULL)
found = findActionRecord(actionRecords, &LSDA, actions,
foreign, e, &filter);
else if (landingpad != 0)
found = CLEANUP_FOUND;
if (found == 0)
CONTINUE_UNWIND;
if (actions & _UA_SEARCH_PHASE) {
if (!(found & HANDLER_FOUND) || foreign)
CONTINUE_UNWIND;
/* Cache it so we don't have to search it again in phase 2 */
#ifndef HAVE_ARM_EHABI_EXCEPTIONS
e->landingpad = landingpad;
e->filter = filter;
#else
ex->barrierCache.sp = _Unwind_GetGR(ctx, 13);
ex->barrierCache.bitPattern[1] = filter;
ex->barrierCache.bitPattern[3] = landingpad;
#endif
return _URC_HANDLER_FOUND;
} else if (actions & _UA_CLEANUP_PHASE) {
if (!(found & CLEANUP_FOUND))
CONTINUE_UNWIND;
_Unwind_SetGR(ctx, __builtin_eh_return_data_regno(0),
(uintptr_t)ex);
_Unwind_SetGR(ctx, __builtin_eh_return_data_regno(1), filter);
_Unwind_SetIP(ctx, landingpad);
return _URC_INSTALL_CONTEXT;
}
OBJC_ERROR(
"Neither _UA_SEARCH_PHASE nor _UA_CLEANUP_PHASE in actions!");
}
static void
cleanup(_Unwind_Reason_Code reason, struct _Unwind_Exception *ex)
{
free(ex);
}
static void
emergencyExceptionCleanup(_Unwind_Reason_Code reason,
struct _Unwind_Exception *ex)
{
#ifdef OF_HAVE_THREADS
if (OFSpinlockLock(&emergencyExceptionsSpinlock) != 0)
OBJC_ERROR("Failed to lock spinlock!");
#endif
ex->class = 0;
#ifdef OF_HAVE_THREADS
if (OFSpinlockUnlock(&emergencyExceptionsSpinlock) != 0)
OBJC_ERROR("Failed to unlock spinlock!");
#endif
}
void
objc_exception_throw(id object)
{
struct objc_exception *e = calloc(1, sizeof(*e));
bool emergency = false;
if (e == NULL) {
#ifdef OF_HAVE_THREADS
if (OFSpinlockLock(&emergencyExceptionsSpinlock) != 0)
OBJC_ERROR("Failed to lock spinlock!");
#endif
for (uint_fast8_t i = 0; i < numEmergencyExceptions; i++) {
if (emergencyExceptions[i].exception.class == 0) {
e = &emergencyExceptions[i];
e->exception.class = GNUCOBJC_EXCEPTION_CLASS;
emergency = true;
break;
}
}
#ifdef OF_HAVE_THREADS
if (OFSpinlockUnlock(&emergencyExceptionsSpinlock) != 0)
OBJC_ERROR("Failed to lock spinlock!");
#endif
}
if (e == NULL)
OBJC_ERROR("Not enough memory to allocate exception!");
e->exception.class = GNUCOBJC_EXCEPTION_CLASS;
e->exception.cleanup = (emergency
? emergencyExceptionCleanup : cleanup);
e->object = object;
_Unwind_RaiseException(&e->exception);
if (uncaughtExceptionHandler != NULL)
uncaughtExceptionHandler(object);
OBJC_ERROR("_Unwind_RaiseException() returned!");
}
objc_uncaught_exception_handler
objc_setUncaughtExceptionHandler(objc_uncaught_exception_handler handler)
{
objc_uncaught_exception_handler old = uncaughtExceptionHandler;
uncaughtExceptionHandler = handler;
return old;
}
#ifdef HAVE_SEH_EXCEPTIONS
typedef EXCEPTION_DISPOSITION (*seh_personality_fn)(PEXCEPTION_RECORD, void *,
PCONTEXT, PDISPATCHER_CONTEXT);
static seh_personality_fn __gxx_personality_seh0;
OF_CONSTRUCTOR()
{
/*
* This only works if the application uses libstdc++-6.dll.
* There is unfortunately no other way, as Windows does not support
* proper weak linking.
*/
HMODULE module;
if ((module = GetModuleHandle("libstdc++-6")) == NULL)
return;
__gxx_personality_seh0 = (seh_personality_fn)
GetProcAddress(module, "__gxx_personality_seh0");
}
EXCEPTION_DISPOSITION
__gnu_objc_personality_seh0(PEXCEPTION_RECORD ms_exc, void *this_frame,
PCONTEXT ms_orig_context, PDISPATCHER_CONTEXT ms_disp)
{
struct _Unwind_Exception *ex =
(struct _Unwind_Exception *)ms_exc->ExceptionInformation[0];
switch (ex->class) {
case GNUCCXX0_EXCEPTION_CLASS:
case CLNGCXX0_EXCEPTION_CLASS:
if (__gxx_personality_seh0 != NULL)
return __gxx_personality_seh0(ms_exc, this_frame,
ms_orig_context, ms_disp);
}
return _GCC_specific_handler(ms_exc, this_frame, ms_orig_context,
ms_disp, PERSONALITY);
}
#endif