From f352f62423b339aabd2721f49333c49222b09964 Mon Sep 17 00:00:00 2001 From: "Dustin L. Howett" Date: Sun, 21 Jan 2018 17:37:23 -0800 Subject: [PATCH] Add a MSVC/VS2017-compatible objc_exception_{,re}throw This implementation of objc_exception_throw constructs a _CxxThrowException-compatible ThrowInfo based on the runtime type of the passed-in exception. Everything pertaining to the exception is allocated on the stack before the call and destroyed when/if the stack frame is aborted. The construction of ThrowInfo, CatchableTypes, and TypeDescriptors, as well as mangling, is compatible with Clang/LLVM's MicrosoftCXXABI exception generator. This ties Objective-C exceptions on Windows to the C++ exception model, which allows us to support (unboxed) foreign exceptions and C++ catch of Objective-C types. objc_exception_rethrow recycles MicrosoftCXXABI's rethrow. Contributing-author: Jordan Saunders Contributing-author: Shayne Hiet-Block --- eh_win32_msvc.cc | 170 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 170 insertions(+) create mode 100644 eh_win32_msvc.cc diff --git a/eh_win32_msvc.cc b/eh_win32_msvc.cc new file mode 100644 index 0000000..7c7ba63 --- /dev/null +++ b/eh_win32_msvc.cc @@ -0,0 +1,170 @@ +#include +#include +#include +#include + +#include "objc/runtime.h" +#include "visibility.h" + +#ifndef __has_builtin +#define __has_builtin(x) 0 +#endif + +#if !__has_builtin(__builtin_unreachable) +#define __builtin_unreachable abort +#endif + +struct _MSVC_TypeDescriptor +{ + const void* pVFTable; + void* spare; + char name[0]; +}; + +struct _MSVC_CatchableType +{ + unsigned int flags; + _MSVC_TypeDescriptor* type; + int mdisp; + int pdisp; + int vdisp; + int size; + void* copyFunction; +}; + +struct _MSVC_CatchableTypeArray +{ + int count; + _MSVC_CatchableType* types[0]; +}; + +struct _MSVC_ThrowInfo +{ + unsigned int attributes; + void* pfnUnwind; + void* pfnForwardCompat; + _MSVC_CatchableTypeArray* pCatchableTypeArray; +}; + +#if defined(_WIN64) +extern "C" int __ImageBase; +#define IMAGE_RELATIVE(ptr) ((decltype(ptr))(ptr ? ((uintptr_t)ptr - (uintptr_t)&__ImageBase) : (uintptr_t)nullptr)) +#else +#define IMAGE_RELATIVE(ptr) (ptr) +#endif + +extern "C" void __stdcall _CxxThrowException(void*, _MSVC_ThrowInfo*); + +namespace +{ + +static std::string mangleObjcObject() +{ + // This mangled name doesn't vary based on bitness. + return ".PAUobjc_object@@"; +} + +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 +#if defined(__SIZEOF_POINTER__) && __SIZEOF_POINTER__ == 8 + std::string(".PEAU") + +#else + std::string(".PAU") + +#endif + className + "@@"; +} + +void fillCatchableType(_MSVC_CatchableType* exceptType) +{ + exceptType->flags = 1; + exceptType->mdisp = 0; + exceptType->pdisp = -1; + exceptType->vdisp = 0; + exceptType->size = sizeof(id); + exceptType->copyFunction = nullptr; +} + +} // + +PUBLIC extern "C" void objc_exception_throw(id object) +{ + // 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)); + CREATE_TYPE_DESCRIPTOR(exceptType->type, mangledName); + exceptType->type = IMAGE_RELATIVE(exceptType->type); + exceptTypes->types[curTypeIndex++] = IMAGE_RELATIVE(exceptType); + } + + // Add id (struct objc_object*) + auto exceptType = (_MSVC_CatchableType*)_alloca(sizeof(_MSVC_CatchableType)); + fillCatchableType(exceptType); + auto idName = mangleObjcObject(); + CREATE_TYPE_DESCRIPTOR(exceptType->type, idName); + exceptType->type = IMAGE_RELATIVE(exceptType->type); + exceptTypes->types[curTypeIndex++] = IMAGE_RELATIVE(exceptType); + + _MSVC_ThrowInfo ti = { + 0, // attributes + NULL, // pfnUnwind + NULL, // pfnForwardCompat + IMAGE_RELATIVE(exceptTypes) // pCatchableTypeArray + }; + _CxxThrowException(&object, &ti); + __builtin_unreachable(); +} + +PUBLIC extern "C" void objc_exception_rethrow(void* exc) +{ + _CxxThrowException(nullptr, nullptr); + __builtin_unreachable(); +}