Skip to content

Commit 375a009

Browse files
committed
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 <jmsaunde@microsoft.com> Contributing-author: Shayne Hiet-Block <shaynehi@microsoft.com>
1 parent 652be81 commit 375a009

File tree

1 file changed

+170
-0
lines changed

1 file changed

+170
-0
lines changed

eh_win32_msvc.cc

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
#include <stdlib.h>
2+
#include <stdio.h>
3+
#include <string.h>
4+
#include <vector>
5+
6+
#include "objc/runtime.h"
7+
#include "visibility.h"
8+
9+
#ifndef __has_builtin
10+
#define __has_builtin(x) 0
11+
#endif
12+
13+
#if !__has_builtin(__builtin_unreachable)
14+
#define __builtin_unreachable abort
15+
#endif
16+
17+
struct _MSVC_TypeDescriptor
18+
{
19+
const void* pVFTable;
20+
void* spare;
21+
char name[0];
22+
};
23+
24+
struct _MSVC_CatchableType
25+
{
26+
unsigned int flags;
27+
_MSVC_TypeDescriptor* type;
28+
int mdisp;
29+
int pdisp;
30+
int vdisp;
31+
int size;
32+
void* copyFunction;
33+
};
34+
35+
struct _MSVC_CatchableTypeArray
36+
{
37+
int count;
38+
_MSVC_CatchableType* types[0];
39+
};
40+
41+
struct _MSVC_ThrowInfo
42+
{
43+
unsigned int attributes;
44+
void* pfnUnwind;
45+
void* pfnForwardCompat;
46+
_MSVC_CatchableTypeArray* pCatchableTypeArray;
47+
};
48+
49+
#if defined(_WIN64)
50+
extern "C" int __ImageBase;
51+
#define IMAGE_RELATIVE(ptr) ((decltype(ptr))(ptr ? ((uintptr_t)ptr - (uintptr_t)&__ImageBase) : (uintptr_t)nullptr))
52+
#else
53+
#define IMAGE_RELATIVE(ptr) (ptr)
54+
#endif
55+
56+
extern "C" void __stdcall _CxxThrowException(void*, _MSVC_ThrowInfo*);
57+
58+
namespace
59+
{
60+
61+
static std::string mangleObjcObject()
62+
{
63+
// This mangled name doesn't vary based on bitness.
64+
return ".PAUobjc_object@@";
65+
}
66+
67+
static std::string mangleStructNamed(const char* className)
68+
{
69+
// 32-bit:
70+
// .PAUxxx@@ = ?? struct xxx * `RTTI Type Descriptor'
71+
// 64-bit:
72+
// .PEAUxxx@@ = ?? struct xxx * __ptr64 `RTTI Type Descriptor'
73+
return
74+
#if defined(__SIZEOF_POINTER__) && __SIZEOF_POINTER__ == 8
75+
std::string(".PEAU") +
76+
#else
77+
std::string(".PAU") +
78+
#endif
79+
className + "@@";
80+
}
81+
82+
void fillCatchableType(_MSVC_CatchableType* exceptType)
83+
{
84+
exceptType->flags = 1;
85+
exceptType->mdisp = 0;
86+
exceptType->pdisp = -1;
87+
exceptType->vdisp = 0;
88+
exceptType->size = sizeof(id);
89+
exceptType->copyFunction = nullptr;
90+
}
91+
92+
} // <anonymous-namespace>
93+
94+
PUBLIC extern "C" void objc_exception_throw(id object)
95+
{
96+
// This is the base vtable for all RTTI entries
97+
static const void* typeid_vtable = *(void**)&typeid(void *);
98+
99+
SEL rethrow_sel = sel_registerName("rethrow");
100+
if ((nil != object) &&
101+
(class_respondsToSelector(object_getClass(object), rethrow_sel)))
102+
{
103+
IMP rethrow = objc_msg_lookup(object, rethrow_sel);
104+
rethrow(object, rethrow_sel);
105+
// Should not be reached! If it is, then the rethrow method actually
106+
// didn't, so we throw it normally.
107+
}
108+
109+
SEL processException_sel = sel_registerName("_processException");
110+
if ((nil != object) &&
111+
(class_respondsToSelector(object_getClass(object), processException_sel)))
112+
{
113+
IMP processException = objc_msg_lookup(object, processException_sel);
114+
processException(object, processException_sel);
115+
}
116+
117+
// The 'id' base type will be taking up a spot in the list:
118+
size_t typeCount = 1;
119+
120+
// Get count of all types in exception
121+
for (Class cls = object_getClass(object); cls != nil; cls = class_getSuperclass(cls), ++typeCount)
122+
;
123+
124+
// Unfortunately we can't put this in a real function since the alloca has to be in this stack frame:
125+
#define CREATE_TYPE_DESCRIPTOR(desc, symName) \
126+
desc = reinterpret_cast<_MSVC_TypeDescriptor*>(alloca(sizeof(_MSVC_TypeDescriptor) + symName.size() + 1 /* null terminator */)); \
127+
desc->pVFTable = typeid_vtable; \
128+
desc->spare = nullptr; \
129+
strcpy_s(desc->name, symName.size() + 1, symName.c_str());
130+
131+
auto exceptTypes =
132+
(_MSVC_CatchableTypeArray*)_alloca(sizeof(_MSVC_CatchableTypeArray) + sizeof(_MSVC_CatchableType*) * typeCount);
133+
exceptTypes->count = typeCount;
134+
135+
// Add exception type and all base types to throw information
136+
size_t curTypeIndex = 0;
137+
for (Class cls = object_getClass(object); cls != nil; cls = class_getSuperclass(cls))
138+
{
139+
auto exceptType = (_MSVC_CatchableType*)_alloca(sizeof(_MSVC_CatchableType));
140+
fillCatchableType(exceptType);
141+
142+
auto mangledName = mangleStructNamed(class_getName(cls));
143+
CREATE_TYPE_DESCRIPTOR(exceptType->type, mangledName);
144+
exceptType->type = IMAGE_RELATIVE(exceptType->type);
145+
exceptTypes->types[curTypeIndex++] = IMAGE_RELATIVE(exceptType);
146+
}
147+
148+
// Add id (struct objc_object*)
149+
auto exceptType = (_MSVC_CatchableType*)_alloca(sizeof(_MSVC_CatchableType));
150+
fillCatchableType(exceptType);
151+
auto idName = mangleObjcObject();
152+
CREATE_TYPE_DESCRIPTOR(exceptType->type, idName);
153+
exceptType->type = IMAGE_RELATIVE(exceptType->type);
154+
exceptTypes->types[curTypeIndex++] = IMAGE_RELATIVE(exceptType);
155+
156+
_MSVC_ThrowInfo ti = {
157+
0, // attributes
158+
NULL, // pfnUnwind
159+
NULL, // pfnForwardCompat
160+
IMAGE_RELATIVE(exceptTypes) // pCatchableTypeArray
161+
};
162+
_CxxThrowException(&object, &ti);
163+
__builtin_unreachable();
164+
}
165+
166+
PUBLIC extern "C" void objc_exception_rethrow(void* exc)
167+
{
168+
_CxxThrowException(nullptr, nullptr);
169+
__builtin_unreachable();
170+
}

0 commit comments

Comments
 (0)