OLD | NEW |
(Empty) | |
| 1 |
| 2 #include <setjmp.h> |
| 3 #include <stdint.h> |
| 4 #include <stdio.h> |
| 5 #include <stdlib.h> |
| 6 #include <string.h> |
| 7 |
| 8 #include <typeinfo> |
| 9 |
| 10 #include "unwind-cxx.h" |
| 11 |
| 12 |
| 13 using namespace __cxxabiv1; |
| 14 |
| 15 struct exc_result { |
| 16 void *exc; |
| 17 uint32_t exc_type_id; |
| 18 }; |
| 19 |
| 20 struct unwind_frame { |
| 21 union { |
| 22 jmp_buf jmpbuf; |
| 23 struct exc_result result; |
| 24 }; |
| 25 struct unwind_frame *next; |
| 26 uint32_t action_id; |
| 27 }; |
| 28 |
| 29 struct action_table_entry { |
| 30 int32_t type_filter_id; |
| 31 uint32_t next_action_id; |
| 32 }; |
| 33 |
| 34 extern std::type_info *const __pnacl_eh_type_table[]; |
| 35 extern const struct action_table_entry __pnacl_eh_action_table[]; |
| 36 extern const uint32_t __pnacl_eh_filter_table[]; |
| 37 |
| 38 __thread struct unwind_frame *__pnacl_eh_stack; |
| 39 |
| 40 |
| 41 static bool get_adjusted_ptr (const std::type_info *catch_type, |
| 42 const std::type_info *throw_type, |
| 43 void **thrown_ptr_p) { |
| 44 if (catch_type == NULL) |
| 45 return true; |
| 46 |
| 47 void *thrown_ptr = *thrown_ptr_p; |
| 48 |
| 49 // Pointer types need to adjust the actual pointer, not |
| 50 // the pointer to pointer that is the exception object. |
| 51 // This also has the effect of passing pointer types |
| 52 // "by value" through the __cxa_begin_catch return value. |
| 53 if (throw_type->__is_pointer_p ()) |
| 54 thrown_ptr = *(void **) thrown_ptr; |
| 55 |
| 56 if (catch_type->__do_catch (throw_type, &thrown_ptr, 1)) { |
| 57 *thrown_ptr_p = thrown_ptr; |
| 58 return true; |
| 59 } |
| 60 return false; |
| 61 } |
| 62 |
| 63 static int check_exception_spec(const std::type_info *throw_type, void *obj, |
| 64 int32_t filter_id) { |
| 65 const uint32_t *filter_ptr = |
| 66 &__pnacl_eh_filter_table[-filter_id - 1]; |
| 67 for (; *filter_ptr != -1; ++filter_ptr) { |
| 68 std::type_info *catch_type = __pnacl_eh_type_table[*filter_ptr]; |
| 69 // If any type matches, the overall filter doesn't trigger. |
| 70 // We ignore the modified value of obj. |
| 71 if (get_adjusted_ptr(catch_type, throw_type, &obj)) |
| 72 return 1; |
| 73 } |
| 74 // No type matched, so we have an exception specification error. |
| 75 return 0; |
| 76 } |
| 77 |
| 78 static int filter_matches(std::type_info *throw_type, void **obj, |
| 79 int32_t filter_id) { |
| 80 if (filter_id < 0) |
| 81 return !check_exception_spec(throw_type, obj, filter_id); |
| 82 |
| 83 std::type_info *catch_type = __pnacl_eh_type_table[filter_id]; |
| 84 printf(" exception id: type_id=%i -> %p (%s)\n", |
| 85 filter_id, catch_type, |
| 86 catch_type ? catch_type->name() : "catch-all"); |
| 87 if (get_adjusted_ptr(catch_type, throw_type, obj)) { |
| 88 printf(" match found (%s match)\n", |
| 89 throw_type == catch_type ? "exact" : "subtype"); |
| 90 return 1; |
| 91 } else { |
| 92 return 0; |
| 93 } |
| 94 } |
| 95 |
| 96 static void find_match(std::type_info *throw_type, void **obj, |
| 97 struct unwind_frame **result_frame, |
| 98 int *result_filter_id) { |
| 99 while (__pnacl_eh_stack != NULL) { |
| 100 struct unwind_frame *frame = __pnacl_eh_stack; |
| 101 __pnacl_eh_stack = frame->next; |
| 102 |
| 103 for (uint32_t action_id = frame->action_id; action_id != 0; ) { |
| 104 const struct action_table_entry *action = |
| 105 &__pnacl_eh_action_table[action_id - 1]; |
| 106 if (filter_matches(throw_type, obj, action->type_filter_id)) { |
| 107 // Match found. |
| 108 *result_frame = frame; |
| 109 *result_filter_id = action->type_filter_id; |
| 110 return; |
| 111 } |
| 112 action_id = action->next_action_id; |
| 113 } |
| 114 printf(" no match: trying next frame\n"); |
| 115 } |
| 116 |
| 117 printf("no match\n"); |
| 118 abort(); |
| 119 } |
| 120 |
| 121 __attribute__((noreturn)) |
| 122 static void do_return(struct unwind_frame *frame, |
| 123 void *ue_header, int filter_id) { |
| 124 // We reuse the storage of jmpbuf for the landingpad data, so |
| 125 // save a copy of jmpbuf. |
| 126 jmp_buf jmpbuf_copy; |
| 127 memcpy(&jmpbuf_copy, &frame->jmpbuf, sizeof(jmpbuf_copy)); |
| 128 |
| 129 frame->result.exc = ue_header; |
| 130 frame->result.exc_type_id = filter_id; |
| 131 longjmp(jmpbuf_copy, 1); |
| 132 abort(); |
| 133 } |
| 134 |
| 135 static void do_unwinding(struct _Unwind_Exception *ue_header) { |
| 136 __cxa_exception *xh = __get_exception_header_from_ue(ue_header); |
| 137 printf(" thrown type: %p (%s)\n", |
| 138 xh->exceptionType, xh->exceptionType->name()); |
| 139 |
| 140 void *obj = __get_object_from_ue(ue_header); |
| 141 std::type_info *throw_type = xh->exceptionType; |
| 142 |
| 143 struct unwind_frame *frame; |
| 144 int filter_id; |
| 145 find_match(throw_type, &obj, &frame, &filter_id); |
| 146 |
| 147 xh->adjustedPtr = obj; |
| 148 xh->handlerSwitchValue = filter_id; |
| 149 |
| 150 do_return(frame, ue_header, filter_id); |
| 151 } |
| 152 |
| 153 extern "C" _Unwind_Reason_Code |
| 154 __pnacl_eh_sjlj_Unwind_RaiseException(struct _Unwind_Exception *ue_header) { |
| 155 printf("_Unwind_RaiseException: %p\n", ue_header); |
| 156 |
| 157 do_unwinding(ue_header); |
| 158 abort(); |
| 159 } |
| 160 |
| 161 extern "C" void __pnacl_eh_resume(struct _Unwind_Exception *ue_header) { |
| 162 printf("__pnacl_eh_resume() called\n"); |
| 163 do_unwinding(ue_header); |
| 164 } |
| 165 |
| 166 extern "C" _Unwind_Reason_Code |
| 167 _Unwind_Resume_or_Rethrow(struct _Unwind_Exception *ue_header) { |
| 168 printf("_Unwind_Resume_or_Rethrow: %p\n", ue_header); |
| 169 return _Unwind_RaiseException(ue_header); |
| 170 } |
| 171 |
| 172 extern "C" void |
| 173 _Unwind_DeleteException (struct _Unwind_Exception *exc) |
| 174 { |
| 175 if (exc->exception_cleanup) |
| 176 (*exc->exception_cleanup) (_URC_FOREIGN_EXCEPTION_CAUGHT, exc); |
| 177 } |
| 178 |
| 179 extern "C" void __pnacl_eh_sjlj_cxa_call_unexpected(_Unwind_Exception *ue_header
) { |
| 180 __cxa_exception *xh = __get_exception_header_from_ue(ue_header); |
| 181 int xh_switch_value = xh->handlerSwitchValue; |
| 182 std::terminate_handler xh_terminate_handler = xh->terminateHandler; |
| 183 |
| 184 __cxa_begin_catch(ue_header); |
| 185 |
| 186 // This function is a handler for our exception argument. If we exit |
| 187 // by throwing a different exception, we'll need the original cleaned up. |
| 188 struct end_catch_protect |
| 189 { |
| 190 end_catch_protect() { } |
| 191 ~end_catch_protect() { __cxa_end_catch(); } |
| 192 } end_catch_protect_obj; |
| 193 |
| 194 // We use the handler function that was cached by __cxa_throw(). |
| 195 // libstdc++'s unwind-cxx.h says this is cached because "The C++ |
| 196 // standard has entertaining rules wrt calling set_terminate and |
| 197 // set_unexpected in the middle of the exception cleanup process." |
| 198 printf(" calling 'unexpected' handler\n"); |
| 199 try { |
| 200 xh->unexpectedHandler(); |
| 201 } catch (...) { |
| 202 printf(" 'unexpected' handler threw\n"); |
| 203 __cxa_eh_globals *globals = __cxa_get_globals_fast (); |
| 204 __cxa_exception *new_xh = globals->caughtExceptions; |
| 205 void *new_ptr = __get_object_from_ambiguous_exception (new_xh); |
| 206 |
| 207 // If this new exception meets the exception spec, allow it. |
| 208 if (check_exception_spec( |
| 209 __get_exception_header_from_obj(new_ptr)->exceptionType, |
| 210 new_ptr, xh_switch_value)) { |
| 211 throw; |
| 212 } |
| 213 |
| 214 // If the exception spec allows std::bad_exception, throw that. |
| 215 // We don't have a thrown object to compare against, but since |
| 216 // bad_exception doesn't have virtual bases, that's OK; just pass 0. |
| 217 const std::type_info *bad_exc = &typeid(std::bad_exception); |
| 218 if (check_exception_spec(bad_exc, NULL, xh_switch_value)) |
| 219 throw std::bad_exception(); |
| 220 |
| 221 __terminate (xh_terminate_handler); |
| 222 } |
| 223 printf(" 'unexpected' handler returned\n"); |
| 224 std::terminate(); |
| 225 } |
OLD | NEW |