OLD | NEW |
---|---|
(Empty) | |
1 //===- cxa_pnacl_sjlj_exception.cpp - PNaCl SJLJ-based exception handling--===// | |
2 // | |
3 // The LLVM Compiler Infrastructure | |
4 // | |
5 // This file is dual licensed under the MIT and the University of Illinois Open | |
6 // Source Licenses. See LICENSE.TXT for details. | |
7 // | |
8 //===----------------------------------------------------------------------===// | |
9 // | |
10 // This implements setjmp()/longjmp()-based (SJLJ) C++ exception | |
11 // handling for PNaCl. This uses the C++ exception info tables | |
12 // generated by the PNaClSjLjEH LLVM pass. | |
13 // | |
14 // Each __pnacl_eh_sjlj_Unwind_*() function below provides the | |
15 // definition of _Unwind_*(). | |
16 // | |
17 // The "__pnacl_eh_sjlj" prefix is added so that PNaCl's SJLJ | |
18 // (setjmp()/longjmp()-based) implementation of C++ exception handling | |
19 // can coexist with other implementations in the same build of | |
20 // libc++/libcxxabi. When SJLJ EH is enabled, each | |
21 // __pnacl_eh_sjlj_Unwind_*() symbol will get renamed to _Unwind_*() | |
22 // when linking a PNaCl pexe. | |
23 // | |
24 //===----------------------------------------------------------------------===// | |
25 | |
26 #include <setjmp.h> | |
27 #include <stdint.h> | |
28 #include <stdlib.h> | |
29 #include <string.h> | |
30 #include <typeinfo> | |
31 | |
32 #include "cxa_exception.hpp" | |
33 #include "cxa_handlers.hpp" | |
34 #include "private_typeinfo.h" | |
35 | |
36 using namespace __cxxabiv1; | |
37 | |
38 | |
39 // Exception info written by ExceptionInfoWriter.cpp. | |
40 | |
41 struct action_table_entry { | |
42 int32_t clause_id; | |
43 uint32_t next_clause_list_id; | |
44 }; | |
45 | |
46 extern const struct action_table_entry __pnacl_eh_action_table[]; | |
47 extern const __shim_type_info *const __pnacl_eh_type_table[]; | |
48 extern const int32_t __pnacl_eh_filter_table[]; | |
49 | |
50 // Data structures used by PNaClSjLjEH.cpp. | |
51 | |
52 struct landing_pad_result { | |
53 void *exception_obj; | |
54 uint32_t matched_clause_id; | |
55 }; | |
56 | |
57 struct exception_frame { | |
58 union { | |
59 jmp_buf jmpbuf; | |
60 struct landing_pad_result result; | |
61 }; | |
62 struct exception_frame *next; | |
63 uint32_t clause_list_id; | |
64 }; | |
65 | |
66 __thread struct exception_frame *__pnacl_eh_stack; | |
67 | |
68 | |
69 // Returns whether the thrown exception (specified by throw_type) | |
70 // matches none of the exception types in a C++ exception | |
Derek Schuff
2013/12/04 22:26:26
s/none/one... small change, big meaning :)
Mark Seaborn
2013/12/04 23:03:17
That was intentional! I do mean "none".
The mean
| |
71 // specification (specified by filter_id). | |
72 static bool | |
73 exception_spec_can_catch(const __shim_type_info *throw_type, int32_t filter_id) | |
74 { | |
75 const int32_t *filter_ptr = | |
76 &__pnacl_eh_filter_table[-filter_id - 1]; | |
77 for (; *filter_ptr != 0; ++filter_ptr) | |
78 { | |
79 const __shim_type_info *catch_type = | |
80 __pnacl_eh_type_table[*filter_ptr - 1]; | |
81 // We ignore the modified value of obj here. | |
82 void *obj = NULL; | |
83 if (catch_type->can_catch(throw_type, obj)) | |
84 return false; | |
85 } | |
86 // No type matched, so we have an exception specification error. | |
87 return true; | |
88 } | |
89 | |
90 // Returns whether the thrown exception (specified by throw_type and | |
91 // obj) matches the given landingpad clause (clause_id). | |
92 // | |
93 // If the exception matches and the clause is a "catch" clause, this | |
94 // adjusts *obj to upcast it to the type specified in the "catch" | |
95 // clause. (For example, if throw_type uses multiple inheritance and | |
96 // derives from multiple base classes, this might involve adding a | |
97 // constant offset to *obj.) | |
98 static bool | |
99 does_clause_match(const __shim_type_info *throw_type, void **obj, | |
100 int32_t clause_id) | |
101 { | |
102 // Handle "cleanup" clause. | |
103 if (clause_id == 0) | |
104 return true; | |
105 | |
106 // Handle "filter" clause. | |
107 if (clause_id < 0) | |
108 return exception_spec_can_catch(throw_type, clause_id); | |
109 | |
110 // Handle "catch" clause. | |
111 const __shim_type_info *catch_type = __pnacl_eh_type_table[clause_id - 1]; | |
112 if (catch_type == NULL) | |
113 return true; | |
114 return catch_type->can_catch(throw_type, *obj); | |
115 } | |
116 | |
117 // Returns whether the given frame should be entered in order to | |
118 // handle the thrown exception (specified by throw_type and *obj). If | |
119 // so, this adjusts *obj (see does_clause_match()) and sets | |
120 // *result_clause_id. | |
121 static bool | |
122 does_frame_match(const __shim_type_info *throw_type, void **obj, | |
123 struct exception_frame *frame, int32_t *result_clause_id) | |
124 { | |
125 for (int32_t clause_list_id = frame->clause_list_id; clause_list_id != 0; ) | |
126 { | |
127 const struct action_table_entry *list_node = | |
128 &__pnacl_eh_action_table[clause_list_id - 1]; | |
129 if (does_clause_match(throw_type, obj, list_node->clause_id)) | |
130 { | |
131 *result_clause_id = list_node->clause_id; | |
132 return true; | |
133 } | |
134 clause_list_id = list_node->next_clause_list_id; | |
135 } | |
136 return false; | |
137 } | |
138 | |
139 // Search for a stack frame that will handle the given exception, | |
140 // starting from frame. The exception is specified by throw_type and | |
141 // *obj. | |
142 // | |
143 // If a frame is found that will handle the exception, this adjusts | |
144 // *obj (to upcast it to the "catch" type, if there is one), sets | |
145 // *result_frame and *result_clause_id to the frame and clause ID that | |
146 // matched the exception, and returns true. | |
147 static bool | |
148 find_match(const __shim_type_info *throw_type, void **obj, | |
149 struct exception_frame *frame, | |
150 struct exception_frame **result_frame, int32_t *result_clause_id) | |
151 { | |
152 for (; frame != NULL; frame = frame->next) | |
153 { | |
154 if (does_frame_match(throw_type, obj, frame, result_clause_id)) | |
155 { | |
156 *result_frame = frame; | |
157 return true; | |
158 } | |
159 } | |
160 return false; | |
161 } | |
162 | |
163 // Search for a non-cleanup stack frame that will handle the given | |
164 // exception, starting from frame. Returns whether a matching frame | |
165 // was found. | |
166 static bool | |
167 is_exception_caught(const __shim_type_info *throw_type, void *obj, | |
168 struct exception_frame *frame) | |
169 { | |
170 for (; frame != NULL; frame = frame->next) | |
171 { | |
172 int32_t clause_id; | |
173 if (does_frame_match(throw_type, &obj, frame, &clause_id) | |
174 && clause_id != 0) | |
175 return true; | |
176 } | |
177 return false; | |
178 } | |
179 | |
180 static __cxa_exception * | |
181 get_exception_header_from_ue(struct _Unwind_Exception *ue_header) | |
182 { | |
183 return (__cxa_exception *) (ue_header + 1) - 1; | |
184 } | |
185 | |
186 static __cxa_dependent_exception * | |
187 get_dependent_exception_from_ue(struct _Unwind_Exception *ue_header) | |
188 { | |
189 return (__cxa_dependent_exception *) (ue_header + 1) - 1; | |
190 } | |
191 | |
192 static void * | |
193 get_object_from_ue(struct _Unwind_Exception *ue_header) | |
194 { | |
195 if (ue_header->exception_class == kOurDependentExceptionClass) | |
196 { | |
197 return get_dependent_exception_from_ue(ue_header)->primaryException; | |
198 } | |
199 return ue_header + 1; | |
200 } | |
201 | |
202 // handle_exception() is called by _Unwind_RaiseException(). It | |
203 // unwinds the stack, looking for the first C++ destructor or catch() | |
204 // block to pass control to. In LLVM terms, it searches for the first | |
205 // matching invoke/landingpad instruction. When it finds a match, | |
206 // this implementation passes control to the landingpad block by | |
207 // longjmp()'ing to it. | |
208 // | |
209 // In a traditional implementation (based on the Itanium ABI), | |
210 // _Unwind_RaiseException() is implemented in a library (libgcc_eh) | |
211 // that is separate from libcxxabi. It calls back to libcxxabi's | |
212 // personality function (__gxx_personality_v0()) to determine whether | |
213 // a call on the stack has a handler for the exception. | |
214 // __gxx_personality_v0() knows that ue_header was allocated by | |
215 // libcxxabi's __cxa_allocate_exception() and can downcast it to | |
216 // libcxxabi's __cxa_exception type. | |
217 // | |
218 // In contrast, in the implementation below, the functionality of the | |
219 // personality function is folded into _Unwind_RaiseException(), so | |
220 // this implements C++-specific matching of exceptions and downcasts | |
221 // ue_header to __cxa_exception immediately. | |
222 // | |
223 // This function returns if stack unwinding did not find any stack | |
224 // frames that match the exception being thrown. | |
225 static void | |
226 handle_exception(struct _Unwind_Exception *ue_header, bool check_for_catch) | |
227 { | |
228 __cxa_exception *xh = get_exception_header_from_ue(ue_header); | |
229 | |
230 void *obj = get_object_from_ue(ue_header); | |
231 struct exception_frame *frame; | |
232 int32_t clause_id; | |
233 if (!find_match((__shim_type_info *) xh->exceptionType, &obj, | |
Derek Schuff
2013/12/04 22:26:26
does libcxxabi's can_catch handle dependent except
Mark Seaborn
2013/12/04 23:03:17
It's not that can_catch() handles them, but __cxa_
| |
234 __pnacl_eh_stack, &frame, &clause_id)) | |
235 return; | |
236 | |
237 // Check that there is a non-cleanup handler for the exception. | |
238 // If not, we should abort before running cleanup handlers | |
239 // (i.e. destructors). | |
240 // | |
241 // This is mainly a convenience for debugging. It means that if | |
242 // the program throws an uncaught exception, the location of the | |
243 // "throw" will be on the stack when the program aborts. If we | |
244 // ran cleanup handlers before aborting, this context would be | |
245 // lost. | |
246 // | |
247 // This is optional in the C++ standard, which says "If no | |
248 // matching handler is found, the function std::terminate() is | |
249 // called; whether or not the stack is unwound before this call to | |
250 // std::terminate() is implementation-defined". | |
251 if (check_for_catch && clause_id == 0 && | |
252 !is_exception_caught((__shim_type_info *) xh->exceptionType, obj, | |
253 frame->next)) | |
254 return; | |
255 | |
256 __pnacl_eh_stack = frame->next; | |
257 | |
258 // Save adjusted exception pointer so that it can be returned by | |
259 // __cxa_begin_catch() when entering a catch() block. | |
260 xh->adjustedPtr = obj; | |
261 | |
262 // Save the clause ID so that if the landingpad block calls | |
263 // __cxa_call_unexpected() and the std::set_unexpected() handler | |
264 // throws an exception, we can re-check that exception against the | |
265 // exception specification. | |
266 xh->handlerSwitchValue = clause_id; | |
267 | |
268 // exception_frame uses the same location for storing the jmp_buf | |
269 // and the landing_pad_result, so we must make a copy of the | |
270 // jmp_buf first. | |
271 jmp_buf jmpbuf_copy; | |
272 memcpy(&jmpbuf_copy, &frame->jmpbuf, sizeof(jmpbuf_copy)); | |
273 | |
274 // Return to the landingpad block, passing it two values. | |
275 frame->result.exception_obj = ue_header; | |
276 frame->result.matched_clause_id = clause_id; | |
277 longjmp(jmpbuf_copy, 1); | |
278 } | |
279 | |
280 | |
281 // This implements _Unwind_RaiseException(). This is called when | |
282 // raising an exception for the first time, i.e. for the statement | |
283 // "throw EXPR;". The compiler lowers "throw EXPR;" to: | |
284 // * a call to __cxa_allocate_exception() to allocate memory; | |
285 // * a call to __cxa_throw() which throws the exception by calling | |
286 // _Unwind_RaiseException(). | |
287 extern "C" _Unwind_Reason_Code | |
288 __pnacl_eh_sjlj_Unwind_RaiseException(struct _Unwind_Exception *ue_header) | |
289 { | |
290 handle_exception(ue_header, true); | |
291 return _URC_END_OF_STACK; | |
292 } | |
293 | |
294 // This is the equivalent of _Unwind_Resume() from libgcc_eh, but we | |
295 // use a different name for PNaCl SJLJ to avoid accidental collisions | |
296 // with libgcc_eh. | |
297 // | |
298 // This is called by a landingpad block as a final step after it has | |
299 // run C++ destructors. This is only called by a landingpad if it did | |
300 // not enter a catch() block. | |
301 // | |
302 // This function never returns. | |
303 extern "C" void | |
304 __pnacl_eh_resume(struct _Unwind_Exception *ue_header) | |
305 { | |
306 // Pass check_for_catch=false so that unwinding does not take O(n^2) | |
307 // time in the number of cleanup landingpads entered before entering | |
308 // the catch() block. | |
309 handle_exception(ue_header, false); | |
310 | |
311 // We've run C++ destructors (cleanup handlers), but no further | |
312 // handlers were found, so abort. We should not reach here, because | |
313 // __pnacl_eh_sjlj_Unwind_RaiseException() already checked that | |
314 // there was a handler for this exception other than cleanup | |
315 // handlers. | |
316 __cxa_begin_catch(ue_header); | |
317 std::terminate(); | |
318 } | |
319 | |
320 // _Unwind_Resume_or_Rethrow() is called when rethrowing an | |
321 // exception, i.e. for the statement "throw;" (with no arguments). | |
322 // The compiler lowers "throw;" to a call to __cxa_rethrow(), which | |
323 // calls this function. | |
324 extern "C" _Unwind_Reason_Code | |
325 __pnacl_eh_sjlj_Unwind_Resume_or_Rethrow(struct _Unwind_Exception *ue_header) | |
326 { | |
327 return __pnacl_eh_sjlj_Unwind_RaiseException(ue_header); | |
328 } | |
329 | |
330 // A convenience function that calls the exception_cleanup field. | |
331 // Based on the definition in libgcc_eh's unwind.inc. | |
332 // | |
333 // This is called when a catch() block that handles an exception exits | |
334 // without rethrowing the exception. This is called by | |
335 // __cxa_end_catch(). The compiler generates a call to | |
336 // __cxa_end_catch() at the end of a catch() block. | |
337 extern "C" void | |
338 __pnacl_eh_sjlj_Unwind_DeleteException(struct _Unwind_Exception *exc) | |
339 { | |
340 if (exc->exception_cleanup) | |
341 (*exc->exception_cleanup)(_URC_FOREIGN_EXCEPTION_CAUGHT, exc); | |
342 } | |
343 | |
344 // This function implements __cxa_call_unexpected(), which is called | |
345 // by a landingpad block when an exception is thrown that doesn't | |
346 // match a function's exception spec (i.e. a "throw(...)" attribute on | |
347 // a function). Calls to __cxa_call_unexpected() are generated by the | |
348 // C++ front end. | |
349 // | |
350 // This calls the handler registered with std::set_unexpected(). This | |
351 // handler is allowed to throw, in which case we must re-check the | |
352 // resulting exception against the original exception specification. | |
353 // | |
354 // The reason that __cxa_call_unexpected() is called by landingpad | |
355 // code rather than by the personality function is so that the | |
356 // landingpad code can run destructors first. | |
357 // | |
358 // This is loosely based on the __cxa_call_unexpected() implementation | |
359 // in cxa_personality.cpp. | |
360 // | |
361 // This function never returns. | |
362 extern "C" void | |
363 __pnacl_eh_sjlj_cxa_call_unexpected(struct _Unwind_Exception *ue_header) | |
364 { | |
365 // Mark the exception as being handled, so that the | |
366 // set_unexpected() handler can rethrow it. | |
367 __cxa_begin_catch(ue_header); | |
368 | |
369 // Ensure that the corresponding __cxa_end_catch() call happens on | |
370 // all paths out of this function. | |
371 struct do_end_catch | |
372 { | |
373 ~do_end_catch() { __cxa_end_catch(); } | |
374 } do_end_catch_obj; | |
375 | |
376 __cxa_exception *old_exception_header = | |
377 get_exception_header_from_ue(ue_header); | |
378 int32_t filter_id = old_exception_header->handlerSwitchValue; | |
379 std::unexpected_handler u_handler = old_exception_header->unexpectedHandler; | |
380 std::terminate_handler t_handler = old_exception_header->terminateHandler; | |
381 | |
382 try | |
383 { | |
384 std::__unexpected(u_handler); | |
385 } | |
386 catch (...) | |
387 { | |
388 __cxa_eh_globals *globals = __cxa_get_globals_fast(); | |
389 __cxa_exception *new_exception_header = globals->caughtExceptions; | |
390 | |
391 // If the handler threw an exception that is allowed by the | |
392 // original exception spec, allow this exception to propagate. | |
393 if (!exception_spec_can_catch( | |
394 (const __shim_type_info *) new_exception_header->exceptionType, | |
395 filter_id)) | |
396 throw; | |
397 | |
398 // Otherwise, if the original exception spec allows | |
399 // std::bad_exception, throw an exception of that type. | |
400 std::bad_exception be; | |
401 const __shim_type_info *be_type = | |
402 (const __shim_type_info *) &typeid(be); | |
403 if (!exception_spec_can_catch(be_type, filter_id)) | |
404 throw be; | |
405 } | |
406 std::__terminate(t_handler); | |
407 } | |
OLD | NEW |