Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // -*- C++ -*- Exception handling routines for PNaCl. | |
| 2 // Copyright (C) 2013 | |
| 3 // Free Software Foundation, Inc. | |
| 4 // | |
| 5 // This file is part of GCC. | |
| 6 // | |
| 7 // GCC is free software; you can redistribute it and/or modify | |
| 8 // it under the terms of the GNU General Public License as published by | |
| 9 // the Free Software Foundation; either version 3, or (at your option) | |
| 10 // any later version. | |
| 11 // | |
| 12 // GCC is distributed in the hope that it will be useful, | |
| 13 // but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| 15 // GNU General Public License for more details. | |
| 16 // | |
| 17 // Under Section 7 of GPL version 3, you are granted additional | |
| 18 // permissions described in the GCC Runtime Library Exception, version | |
| 19 // 3.1, as published by the Free Software Foundation. | |
| 20 | |
| 21 // You should have received a copy of the GNU General Public License and | |
| 22 // a copy of the GCC Runtime Library Exception along with this program; | |
| 23 // see the files COPYING3 and COPYING.RUNTIME respectively. If not, see | |
| 24 // <http://www.gnu.org/licenses/>. | |
| 25 | |
| 26 #include <setjmp.h> | |
| 27 #include <stdint.h> | |
| 28 #include <stdlib.h> | |
| 29 #include <string.h> | |
| 30 #include <typeinfo> | |
| 31 #include "unwind-cxx.h" | |
| 32 | |
| 33 using namespace __cxxabiv1; | |
| 34 | |
| 35 | |
| 36 // Exception info written by ExceptionInfoWriter.cpp. | |
| 37 | |
| 38 struct action_table_entry | |
| 39 { | |
| 40 int32_t clause_id; | |
| 41 uint32_t next_clause_list_id; | |
| 42 }; | |
| 43 | |
| 44 extern const struct action_table_entry __pnacl_eh_action_table[]; | |
| 45 extern const std::type_info *const __pnacl_eh_type_table[]; | |
| 46 extern const int32_t __pnacl_eh_filter_table[]; | |
| 47 | |
| 48 // Data structures used by PNaClSjLjEH.cpp. | |
| 49 | |
| 50 struct landing_pad_result | |
| 51 { | |
| 52 void *exception_obj; | |
| 53 uint32_t matched_clause_id; | |
| 54 }; | |
| 55 | |
| 56 struct exception_frame | |
| 57 { | |
| 58 union | |
| 59 { | |
| 60 jmp_buf jmpbuf; | |
| 61 struct landing_pad_result result; | |
| 62 }; | |
| 63 struct exception_frame *next; | |
| 64 uint32_t clause_list_id; | |
| 65 }; | |
| 66 | |
| 67 __thread struct exception_frame *__pnacl_eh_stack; | |
| 68 | |
| 69 | |
| 70 // This is based on get_adjusted_ptr() in eh_personality.cc. | |
| 71 // | |
| 72 // Given the thrown type THROW_TYPE, pointer to a variable containing a | |
| 73 // pointer to the exception object THROWN_PTR_P and a type CATCH_TYPE to | |
| 74 // compare against, return whether or not there is a match and if so, | |
| 75 // update *THROWN_PTR_P. | |
| 76 static bool | |
| 77 get_adjusted_ptr (const std::type_info *catch_type, | |
| 78 const std::type_info *throw_type, | |
| 79 void **thrown_ptr_p) | |
| 80 { | |
| 81 if (catch_type == NULL) | |
| 82 return true; | |
| 83 | |
| 84 void *thrown_ptr = *thrown_ptr_p; | |
| 85 | |
| 86 // Pointer types need to adjust the actual pointer, not | |
| 87 // the pointer to pointer that is the exception object. | |
| 88 // This also has the effect of passing pointer types | |
| 89 // "by value" through the __cxa_begin_catch return value. | |
| 90 if (throw_type->__is_pointer_p ()) | |
| 91 thrown_ptr = *(void **) thrown_ptr; | |
| 92 | |
| 93 if (catch_type->__do_catch (throw_type, &thrown_ptr, 1)) | |
| 94 { | |
| 95 *thrown_ptr_p = thrown_ptr; | |
| 96 return true; | |
| 97 } | |
| 98 | |
| 99 return false; | |
| 100 } | |
| 101 | |
| 102 // Returns whether the thrown exception (specified by THROW_TYPE and | |
| 103 // OBJ) matches one of the exception types in a C++ exception | |
| 104 // specification (specified by FILTER_ID). | |
| 105 static bool | |
| 106 check_exception_spec (const std::type_info *throw_type, void *obj, | |
| 107 int32_t filter_id) | |
| 108 { | |
| 109 const int32_t *filter_ptr = | |
| 110 &__pnacl_eh_filter_table[-filter_id - 1]; | |
| 111 for (; *filter_ptr != -1; ++filter_ptr) | |
| 112 { | |
| 113 const std::type_info *catch_type = __pnacl_eh_type_table[*filter_ptr]; | |
| 114 // We ignore the modified value of obj here. | |
| 115 if (get_adjusted_ptr (catch_type, throw_type, &obj)) | |
| 116 return true; | |
| 117 } | |
| 118 // No type matched, so we have an exception specification error. | |
| 119 return false; | |
| 120 } | |
| 121 | |
| 122 // Returns whether the thrown exception (specified by THROW_TYPE and | |
| 123 // OBJ) matches the given landingpad clause (CLAUSE_ID). If the | |
| 124 // exception matches, this adjusts *OBJ. | |
| 125 static bool | |
| 126 does_clause_match (const std::type_info *throw_type, void **obj, | |
| 127 int32_t clause_id) | |
| 128 { | |
| 129 if (clause_id < 0) | |
| 130 return !check_exception_spec (throw_type, obj, clause_id); | |
| 131 | |
| 132 const std::type_info *catch_type = __pnacl_eh_type_table[clause_id]; | |
| 133 return get_adjusted_ptr (catch_type, throw_type, obj); | |
| 134 } | |
| 135 | |
| 136 // Search for a stack frame that will handle the given exception. | |
|
Derek Schuff
2013/10/28 22:14:45
add the description of what the arguments mean her
| |
| 137 static bool | |
| 138 find_match (const std::type_info *throw_type, void **obj, | |
| 139 struct exception_frame **result_frame, int32_t *result_clause_id) | |
| 140 { | |
| 141 while (__pnacl_eh_stack != NULL) | |
| 142 { | |
| 143 struct exception_frame *frame = __pnacl_eh_stack; | |
| 144 __pnacl_eh_stack = frame->next; | |
| 145 | |
| 146 for (uint32_t clause_list_id = frame->clause_list_id; | |
| 147 clause_list_id != 0; ) | |
| 148 { | |
| 149 const struct action_table_entry *list_node = | |
| 150 &__pnacl_eh_action_table[clause_list_id - 1]; | |
| 151 if (does_clause_match (throw_type, obj, list_node->clause_id)) | |
| 152 { | |
| 153 // Match found. | |
| 154 *result_frame = frame; | |
| 155 *result_clause_id = list_node->clause_id; | |
| 156 return true; | |
| 157 } | |
| 158 clause_list_id = list_node->next_clause_list_id; | |
| 159 } | |
| 160 } | |
| 161 return false; | |
| 162 } | |
| 163 | |
| 164 | |
| 165 // Each __pnacl_eh_sjlj_Unwind_*() function below provides the | |
| 166 // definition of _Unwind_*(). | |
| 167 // | |
| 168 // The "__pnacl_eh_sjlj" prefix is added so that PNaCl's SJLJ | |
| 169 // (setjmp()/longjmp()-based) implementation of C++ exception handling | |
| 170 // can coexist with other implementations in the same build of | |
| 171 // libstdc++/libsupc++. When SJLJ EH is enabled, each | |
| 172 // __pnacl_eh_sjlj_Unwind_*() symbol will get renamed to _Unwind_*() | |
| 173 // when linking a PNaCl pexe. | |
| 174 | |
| 175 | |
| 176 // This implements _Unwind_RaiseException(). This is called when | |
| 177 // raising an exception for the first time, i.e. for the statement | |
| 178 // "throw EXPR;". The compiler lowers "throw EXPR;" to: | |
| 179 // * a call to __cxa_allocate_exception() to allocate memory; | |
| 180 // * a call to __cxa_throw() which throws the exception by calling | |
| 181 // _Unwind_RaiseException(). | |
| 182 // | |
| 183 // _Unwind_RaiseException() unwinds the stack, looking for the first | |
| 184 // C++ destructor or catch() block to pass control to. In LLVM terms, | |
| 185 // it searches for the first matching invoke/landingpad instruction. | |
| 186 // When it finds a match, this implementation passes control to the | |
| 187 // landingpad block by longjmp()'ing to it. | |
| 188 // | |
| 189 // In a traditional implementation (based on the Itanium ABI), | |
| 190 // _Unwind_RaiseException() is implemented in a library (libgcc_eh) | |
| 191 // that is separate from libsupc++. It calls back to libsupc++'s | |
| 192 // personality function (__gxx_personality_v0()) to determine whether | |
| 193 // a call on the stack has a handler for the exception. | |
| 194 // __gxx_personality_v0() knows that UE_HEADER was allocated by | |
| 195 // libsupc++'s __cxa_allocate_exception() and can downcast it to | |
| 196 // libsupc++'s __cxa_exception type. | |
| 197 // | |
| 198 // In contrast, in the implementation below, the functionality of the | |
| 199 // personality function is folded into _Unwind_RaiseException(), so | |
| 200 // this implements C++-specific matching of exceptions and downcasts | |
| 201 // UE_HEADER to __cxa_exception immediately. | |
| 202 // | |
| 203 // This function returns if stack unwinding did not find any stack | |
| 204 // frames that match the exception being thrown. | |
| 205 extern "C" _Unwind_Reason_Code | |
| 206 __pnacl_eh_sjlj_Unwind_RaiseException (struct _Unwind_Exception *ue_header) | |
| 207 { | |
| 208 __cxa_exception *xh = __get_exception_header_from_ue (ue_header); | |
| 209 | |
| 210 void *obj = __get_object_from_ue (ue_header); | |
| 211 struct exception_frame *frame; | |
| 212 int32_t clause_id; | |
| 213 if (find_match (xh->exceptionType, &obj, &frame, &clause_id)) | |
| 214 { | |
| 215 // Save adjusted exception pointer so that it can be returned by | |
| 216 // __cxa_begin_catch() when entering a catch() block. | |
| 217 xh->adjustedPtr = obj; | |
| 218 | |
| 219 // Save the clause ID so that if the landingpad block calls | |
| 220 // __cxa_call_unexpected() and the std::set_unexpected() handler | |
| 221 // throws an exception, we can re-check that exception against | |
| 222 // the exception specification. | |
| 223 xh->handlerSwitchValue = clause_id; | |
| 224 | |
| 225 // exception_frame uses the same location for storing the | |
| 226 // jmp_buf and the landing_pad_result, so we must make a copy of | |
| 227 // the jmp_buf first. | |
| 228 jmp_buf jmpbuf_copy; | |
| 229 memcpy (&jmpbuf_copy, &frame->jmpbuf, sizeof (jmpbuf_copy)); | |
| 230 | |
| 231 // Return to the landingpad block, passing it two values. | |
| 232 frame->result.exception_obj = ue_header; | |
| 233 frame->result.matched_clause_id = clause_id; | |
| 234 longjmp (jmpbuf_copy, 1); | |
| 235 } | |
| 236 | |
| 237 return _URC_END_OF_STACK; | |
| 238 } | |
| 239 | |
| 240 // This is the equivalent of _Unwind_Resume() from libgcc_eh, but we | |
| 241 // use a different name for PNaCl SJLJ to avoid accidental collisions | |
| 242 // with libgcc_eh. | |
| 243 // | |
| 244 // This is called by a landingpad block as a final step after it has | |
| 245 // run C++ destructors. This is only called by a landingpad if it did | |
| 246 // not enter a catch() block. | |
| 247 // | |
| 248 // This function never returns. | |
| 249 extern "C" void | |
| 250 __pnacl_eh_resume (struct _Unwind_Exception *ue_header) | |
| 251 { | |
| 252 __pnacl_eh_sjlj_Unwind_RaiseException (ue_header); | |
| 253 | |
| 254 // We've run C++ destructors (cleanup handlers), but no further | |
| 255 // handlers were found, so abort. | |
| 256 // | |
| 257 // Note that this situation does not normally happen with GNU C++, | |
| 258 // which scans the stack to check for a matching catch or filter | |
| 259 // clause, and calls std::terminate() if none is found, before | |
| 260 // calling any cleanup handlers. | |
| 261 // | |
| 262 // Both behaviours are allowed by the C++ standard, which says "If | |
| 263 // no matching handler is found, the function std::terminate() is | |
| 264 // called; whether or not the stack is unwound before this call to | |
| 265 // std::terminate() is implementation-defined". | |
| 266 __cxa_begin_catch (ue_header); | |
| 267 std::terminate (); | |
| 268 } | |
| 269 | |
| 270 // _Unwind_Resume_or_Rethrow() is called when rethrowing an | |
| 271 // exception, i.e. for the statement "throw;" (with no arguments). | |
| 272 // The compiler lowers "throw;" to a call to __cxa_rethrow(), which | |
| 273 // calls this function. | |
| 274 extern "C" _Unwind_Reason_Code | |
| 275 __pnacl_eh_sjlj_Unwind_Resume_or_Rethrow (struct _Unwind_Exception *ue_header) | |
| 276 { | |
| 277 return __pnacl_eh_sjlj_Unwind_RaiseException (ue_header); | |
| 278 } | |
| 279 | |
| 280 // A convenience function that calls the exception_cleanup field. | |
| 281 // Based on the definition in libgcc_eh's unwind.inc. | |
| 282 // | |
| 283 // This is called when a catch() block that handles an exception exits | |
| 284 // without rethrowing the exception. This is called by | |
| 285 // __cxa_end_catch(). The compiler generates a call to | |
| 286 // __cxa_end_catch() at the end of a catch() block. | |
| 287 extern "C" void | |
| 288 __pnacl_eh_sjlj_Unwind_DeleteException (struct _Unwind_Exception *exc) | |
| 289 { | |
| 290 if (exc->exception_cleanup) | |
| 291 (*exc->exception_cleanup) (_URC_FOREIGN_EXCEPTION_CAUGHT, exc); | |
| 292 } | |
| 293 | |
| 294 // This is based on __cxa_call_unexpected() in eh_personality.cc, | |
| 295 // adapted to use PNaCl's exception info instead of the non-portable | |
| 296 // LSDA. | |
| 297 // | |
| 298 // __cxa_call_unexpected() is called by landingpad blocks when an | |
| 299 // exception has been thrown that doesn't match a function's exception | |
| 300 // specification. Calls to __cxa_call_unexpected() are generated by | |
| 301 // the compiler. | |
| 302 // | |
| 303 // __cxa_call_unexpected() calls the handler registered via | |
| 304 // std::set_unexpected(). This handler is allowed to throw, in which | |
| 305 // case we must re-check the resulting exception against the original | |
| 306 // exception specification. | |
| 307 // | |
| 308 // The reason that __cxa_call_unexpected() is called by landingpad | |
| 309 // code rather than by the personality function is so that the | |
| 310 // landingpad code can run destructors first. | |
| 311 // | |
| 312 // This function never returns. | |
| 313 extern "C" void | |
| 314 __pnacl_eh_sjlj_cxa_call_unexpected (_Unwind_Exception *ue_header) | |
| 315 { | |
| 316 __cxa_exception *xh = __get_exception_header_from_ue (ue_header); | |
| 317 int xh_switch_value = xh->handlerSwitchValue; | |
| 318 std::terminate_handler xh_terminate_handler = xh->terminateHandler; | |
| 319 | |
| 320 __cxa_begin_catch (ue_header); | |
| 321 | |
| 322 // This function is a handler for our exception argument. If we exit | |
| 323 // by throwing a different exception, we'll need the original cleaned up. | |
| 324 struct end_catch_protect | |
| 325 { | |
| 326 end_catch_protect() { } | |
| 327 ~end_catch_protect() { __cxa_end_catch(); } | |
| 328 } end_catch_protect_obj; | |
| 329 | |
| 330 // We use the set_unexpected() handler function that was cached by | |
| 331 // __cxa_throw(). | |
| 332 try | |
| 333 { __unexpected (xh->unexpectedHandler); } | |
| 334 catch (...) | |
| 335 { | |
| 336 __cxa_eh_globals *globals = __cxa_get_globals_fast (); | |
| 337 __cxa_exception *new_xh = globals->caughtExceptions; | |
| 338 void *new_ptr = __get_object_from_ambiguous_exception (new_xh); | |
| 339 | |
| 340 // If this new exception meets the exception spec, allow it. | |
| 341 if (check_exception_spec ( | |
| 342 __get_exception_header_from_obj (new_ptr)->exceptionType, | |
| 343 new_ptr, xh_switch_value)) | |
| 344 throw; | |
| 345 | |
| 346 // If the exception spec allows std::bad_exception, throw that. | |
| 347 // We don't have a thrown object to compare against, but since | |
| 348 // bad_exception doesn't have virtual bases, that's OK; just pass 0. | |
| 349 const std::type_info *bad_exc = &typeid (std::bad_exception); | |
| 350 if (check_exception_spec (bad_exc, NULL, xh_switch_value)) | |
| 351 throw std::bad_exception (); | |
| 352 | |
| 353 __terminate (xh_terminate_handler); | |
| 354 } | |
| 355 } | |
| OLD | NEW |