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 and |
| 70 // obj) matches none of the exception types in a C++ exception |
| 71 // specification (specified by filter_id). |
| 72 static bool |
| 73 exception_spec_can_catch(const __shim_type_info *throw_type, void *obj, |
| 74 int32_t filter_id) |
| 75 { |
| 76 const int32_t *filter_ptr = |
| 77 &__pnacl_eh_filter_table[-filter_id - 1]; |
| 78 for (; *filter_ptr != 0; ++filter_ptr) |
| 79 { |
| 80 const __shim_type_info *catch_type = |
| 81 __pnacl_eh_type_table[*filter_ptr - 1]; |
| 82 // We ignore the modified value of obj here. |
| 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, obj, 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, |
| 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 get_object_from_ue(&new_exception_header->unwindHeader), |
| 396 filter_id)) |
| 397 throw; |
| 398 |
| 399 // Otherwise, if the original exception spec allows |
| 400 // std::bad_exception, throw an exception of that type. |
| 401 std::bad_exception be; |
| 402 const __shim_type_info *be_type = |
| 403 (const __shim_type_info *) &typeid(be); |
| 404 if (!exception_spec_can_catch(be_type, &be, filter_id)) |
| 405 throw be; |
| 406 } |
| 407 std::__terminate(t_handler); |
| 408 } |
OLD | NEW |