You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
288 lines
9.2 KiB
C++
288 lines
9.2 KiB
C++
#include <cstdlib>
|
|
#include <cstdio>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include "objc/runtime.h"
|
|
#include "objc/objc-exception.h"
|
|
#include "visibility.h"
|
|
|
|
#include <windows.h>
|
|
#define RtlAddGrowableFunctionTable ClangIsConfusedByTypedefReturnTypes
|
|
#include <rtlsupportapi.h>
|
|
|
|
|
|
#ifndef __has_builtin
|
|
#define __has_builtin(x) 0
|
|
#endif
|
|
|
|
#if !__has_builtin(__builtin_unreachable)
|
|
#define __builtin_unreachable abort
|
|
#endif
|
|
|
|
#define EH_EXCEPTION_NUMBER ('msc' | 0xE0000000)
|
|
#define EH_MAGIC_NUMBER1 0x19930520
|
|
#define EXCEPTION_NONCONTINUABLE 0x1
|
|
|
|
struct _MSVC_TypeDescriptor
|
|
{
|
|
const void* pVFTable;
|
|
void* spare;
|
|
char name[0];
|
|
};
|
|
|
|
struct _MSVC_CatchableType
|
|
{
|
|
unsigned int flags;
|
|
unsigned long type;
|
|
int mdisp;
|
|
int pdisp;
|
|
int vdisp;
|
|
int size;
|
|
unsigned long copyFunction;
|
|
};
|
|
|
|
struct _MSVC_CatchableTypeArray
|
|
{
|
|
int count;
|
|
unsigned long types[0];
|
|
};
|
|
|
|
struct _MSVC_ThrowInfo
|
|
{
|
|
unsigned int attributes;
|
|
unsigned long pfnUnwind;
|
|
unsigned long pfnForwardCompat;
|
|
unsigned long pCatchableTypeArray;
|
|
};
|
|
|
|
static LPTOP_LEVEL_EXCEPTION_FILTER originalUnhandledExceptionFilter = nullptr;
|
|
void (*_objc_unexpected_exception)(id exception);
|
|
LONG WINAPI _objc_unhandled_exception_filter(struct _EXCEPTION_POINTERS* exceptionInfo);
|
|
|
|
#if defined(_WIN64)
|
|
#define IMAGE_RELATIVE(ptr, base) (static_cast<unsigned long>((ptr ? ((uintptr_t)ptr - (uintptr_t)base) : (uintptr_t)nullptr)))
|
|
#else
|
|
#define IMAGE_RELATIVE(ptr, base) reinterpret_cast<unsigned long>((ptr))
|
|
#endif
|
|
|
|
extern "C" void __stdcall _CxxThrowException(void*, _MSVC_ThrowInfo*);
|
|
|
|
namespace
|
|
{
|
|
|
|
static std::string mangleObjcObject()
|
|
{
|
|
#if defined(__SIZEOF_POINTER__) && __SIZEOF_POINTER__ == 8
|
|
return ".PEAUobjc_object@@";
|
|
#else
|
|
return ".PAUobjc_object@@";
|
|
#endif
|
|
}
|
|
|
|
static std::string mangleStructNamed(const char* className)
|
|
{
|
|
// 32-bit:
|
|
// .PAUxxx@@ = ?? struct xxx * `RTTI Type Descriptor'
|
|
// 64-bit:
|
|
// .PEAUxxx@@ = ?? struct xxx * __ptr64 `RTTI Type Descriptor'
|
|
//return
|
|
auto r =
|
|
#if defined(__SIZEOF_POINTER__) && __SIZEOF_POINTER__ == 8
|
|
std::string(".PEAU") +
|
|
#else
|
|
std::string(".PAU") +
|
|
#endif
|
|
className + "@@";
|
|
return r;
|
|
}
|
|
|
|
void fillCatchableType(_MSVC_CatchableType* exceptType)
|
|
{
|
|
exceptType->flags = 1;
|
|
exceptType->mdisp = 0;
|
|
exceptType->pdisp = -1;
|
|
exceptType->vdisp = 0;
|
|
exceptType->size = sizeof(id);
|
|
exceptType->copyFunction = 0;
|
|
}
|
|
|
|
} // <anonymous-namespace>
|
|
|
|
struct X {};
|
|
OBJC_PUBLIC extern "C" void objc_exception_rethrow(void* exc);
|
|
|
|
OBJC_PUBLIC extern "C" void objc_exception_throw(id object)
|
|
{
|
|
// Base used for image-relative addresses.
|
|
char x;
|
|
// This is the base vtable for all RTTI entries
|
|
static const void* typeid_vtable = *(void**)&typeid(void *);
|
|
|
|
SEL rethrow_sel = sel_registerName("rethrow");
|
|
if ((nil != object) &&
|
|
(class_respondsToSelector(object_getClass(object), rethrow_sel)))
|
|
{
|
|
IMP rethrow = objc_msg_lookup(object, rethrow_sel);
|
|
rethrow(object, rethrow_sel);
|
|
// Should not be reached! If it is, then the rethrow method actually
|
|
// didn't, so we throw it normally.
|
|
}
|
|
|
|
SEL processException_sel = sel_registerName("_processException");
|
|
if ((nil != object) &&
|
|
(class_respondsToSelector(object_getClass(object), processException_sel)))
|
|
{
|
|
IMP processException = objc_msg_lookup(object, processException_sel);
|
|
processException(object, processException_sel);
|
|
}
|
|
|
|
// The 'id' base type will be taking up a spot in the list:
|
|
size_t typeCount = 1;
|
|
|
|
// Get count of all types in exception
|
|
for (Class cls = object_getClass(object); cls != Nil; cls = class_getSuperclass(cls), ++typeCount)
|
|
;
|
|
|
|
// Unfortunately we can't put this in a real function since the alloca has to be in this stack frame:
|
|
#define CREATE_TYPE_DESCRIPTOR(desc, symName) \
|
|
desc = reinterpret_cast<_MSVC_TypeDescriptor*>(alloca(sizeof(_MSVC_TypeDescriptor) + symName.size() + 1 /* null terminator */)); \
|
|
desc->pVFTable = typeid_vtable; \
|
|
desc->spare = nullptr; \
|
|
strcpy_s(desc->name, symName.size() + 1, symName.c_str());
|
|
|
|
auto exceptTypes =
|
|
(_MSVC_CatchableTypeArray*)_alloca(sizeof(_MSVC_CatchableTypeArray) + sizeof(_MSVC_CatchableType*) * typeCount);
|
|
exceptTypes->count = typeCount;
|
|
|
|
// Add exception type and all base types to throw information
|
|
size_t curTypeIndex = 0;
|
|
for (Class cls = object_getClass(object); cls != Nil; cls = class_getSuperclass(cls))
|
|
{
|
|
auto exceptType = (_MSVC_CatchableType*)_alloca(sizeof(_MSVC_CatchableType));
|
|
fillCatchableType(exceptType);
|
|
|
|
auto mangledName = mangleStructNamed(class_getName(cls));
|
|
_MSVC_TypeDescriptor *ty;
|
|
CREATE_TYPE_DESCRIPTOR(ty, mangledName);
|
|
exceptType->type = IMAGE_RELATIVE(ty, &x);
|
|
exceptTypes->types[curTypeIndex++] = IMAGE_RELATIVE(exceptType, &x);
|
|
}
|
|
|
|
// Add id (struct objc_object*)
|
|
auto exceptType = (_MSVC_CatchableType*)_alloca(sizeof(_MSVC_CatchableType));
|
|
fillCatchableType(exceptType);
|
|
auto idName = mangleObjcObject();
|
|
_MSVC_TypeDescriptor *ty;
|
|
CREATE_TYPE_DESCRIPTOR(ty, idName);
|
|
exceptType->type = IMAGE_RELATIVE(ty, &x);
|
|
exceptTypes->types[curTypeIndex++] = IMAGE_RELATIVE(exceptType, &x);
|
|
|
|
_MSVC_ThrowInfo ti = {
|
|
0, // attributes
|
|
0, // pfnUnwind
|
|
0, // pfnForwardCompat
|
|
IMAGE_RELATIVE(exceptTypes, &x) // pCatchableTypeArray
|
|
};
|
|
EXCEPTION_RECORD exception;
|
|
exception.ExceptionCode = EH_EXCEPTION_NUMBER;
|
|
exception.ExceptionFlags = EXCEPTION_NONCONTINUABLE;
|
|
exception.ExceptionRecord = nullptr;
|
|
exception.ExceptionAddress = nullptr;
|
|
// The fourth parameter is the base address of the image (for us, this stack
|
|
// frame), but we only use image-relative 32-bit addresses on 64-bit
|
|
// platforms. On 32-bit platforms, we use 32-bit absolute addresses.
|
|
exception.NumberParameters = sizeof(void*) == 4 ? 3 : 4;
|
|
exception.ExceptionInformation[0] = EH_MAGIC_NUMBER1;
|
|
exception.ExceptionInformation[1] = reinterpret_cast<ULONG_PTR>(&object);
|
|
exception.ExceptionInformation[2] = reinterpret_cast<ULONG_PTR>(&ti);
|
|
exception.ExceptionInformation[3] = reinterpret_cast<ULONG_PTR>(&x);
|
|
|
|
#ifdef _WIN64
|
|
RtlRaiseException(&exception);
|
|
#else
|
|
RaiseException(exception.ExceptionCode,
|
|
exception.ExceptionFlags,
|
|
exception.NumberParameters,
|
|
exception.ExceptionInformation);
|
|
#endif
|
|
__builtin_unreachable();
|
|
}
|
|
|
|
OBJC_PUBLIC extern "C" void objc_exception_rethrow(void* exc)
|
|
{
|
|
_CxxThrowException(nullptr, nullptr);
|
|
__builtin_unreachable();
|
|
}
|
|
|
|
// rebase_and_cast adds a constant offset to a U value, converting it into a T
|
|
template <typename T, typename U>
|
|
static std::add_const_t<std::decay_t<T>> rebase_and_cast(intptr_t base, U value) {
|
|
// U value -> const T* (base+value)
|
|
return reinterpret_cast<std::add_const_t<std::decay_t<T>>>(base + (long)(value));
|
|
}
|
|
|
|
/**
|
|
* Unhandled exception filter that we install to get called when an exception is
|
|
* not otherwise handled in a process that is not being debugged. In here we
|
|
* check if the exception is an Objective C exception raised by
|
|
* objc_exception_throw() above, and if so call the _objc_unexpected_exception
|
|
* hook with the Objective-C exception object.
|
|
*
|
|
* https://docs.microsoft.com/en-us/windows/win32/api/errhandlingapi/nf-errhandlingapi-setunhandledexceptionfilter
|
|
*/
|
|
LONG WINAPI _objc_unhandled_exception_filter(struct _EXCEPTION_POINTERS* exceptionInfo)
|
|
{
|
|
const EXCEPTION_RECORD* ex = exceptionInfo->ExceptionRecord;
|
|
|
|
if (_objc_unexpected_exception != 0
|
|
&& ex->ExceptionCode == EH_EXCEPTION_NUMBER
|
|
&& ex->ExceptionInformation[0] == EH_MAGIC_NUMBER1
|
|
&& ex->NumberParameters >= 3)
|
|
{
|
|
// On 64-bit platforms, thrown exception catch data are relative virtual addresses off the module base.
|
|
intptr_t imageBase = ex->NumberParameters >= 4 ? (intptr_t)(ex->ExceptionInformation[3]) : 0;
|
|
|
|
auto throwInfo = reinterpret_cast<_MSVC_ThrowInfo*>(ex->ExceptionInformation[2]);
|
|
if (throwInfo && throwInfo->pCatchableTypeArray) {
|
|
auto catchableTypes = rebase_and_cast<_MSVC_CatchableTypeArray*>(imageBase, throwInfo->pCatchableTypeArray);
|
|
bool foundobjc_object = false;
|
|
for (int i = 0; i < catchableTypes->count; ++i) {
|
|
const _MSVC_CatchableType* catchableType = rebase_and_cast<_MSVC_CatchableType*>(imageBase, catchableTypes->types[i]);
|
|
const _MSVC_TypeDescriptor* typeDescriptor = rebase_and_cast<_MSVC_TypeDescriptor*>(imageBase, catchableType->type);
|
|
if (strcmp(typeDescriptor->name, mangleObjcObject().c_str()) == 0) {
|
|
foundobjc_object = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (foundobjc_object) {
|
|
id exception = *reinterpret_cast<id*>(ex->ExceptionInformation[1]);
|
|
_objc_unexpected_exception(exception);
|
|
}
|
|
}
|
|
}
|
|
|
|
// call original exception filter if any
|
|
if (originalUnhandledExceptionFilter) {
|
|
return originalUnhandledExceptionFilter(exceptionInfo);
|
|
}
|
|
|
|
// EXCEPTION_CONTINUE_SEARCH instructs the exception handler to continue searching for appropriate exception handlers.
|
|
// Since this is the last one, it is not likely to find any more.
|
|
return EXCEPTION_CONTINUE_SEARCH;
|
|
}
|
|
|
|
OBJC_PUBLIC extern "C" 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);
|
|
|
|
// set unhandled exception filter to support hook
|
|
LPTOP_LEVEL_EXCEPTION_FILTER previousExceptionFilter = SetUnhandledExceptionFilter(&_objc_unhandled_exception_filter);
|
|
if (previousExceptionFilter != &_objc_unhandled_exception_filter) {
|
|
originalUnhandledExceptionFilter = previousExceptionFilter;
|
|
}
|
|
|
|
return previousHandler;
|
|
}
|