Index: libstdc++-v3/libsupc++/eh_pnacl.cc |
diff --git a/libstdc++-v3/libsupc++/eh_pnacl.cc b/libstdc++-v3/libsupc++/eh_pnacl.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..564917f4f605213e75ac5dcf7c3ef8f1eb885931 |
--- /dev/null |
+++ b/libstdc++-v3/libsupc++/eh_pnacl.cc |
@@ -0,0 +1,225 @@ |
+ |
+#include <setjmp.h> |
+#include <stdint.h> |
+#include <stdio.h> |
+#include <stdlib.h> |
+#include <string.h> |
+ |
+#include <typeinfo> |
+ |
+#include "unwind-cxx.h" |
+ |
+ |
+using namespace __cxxabiv1; |
+ |
+struct exc_result { |
+ void *exc; |
+ uint32_t exc_type_id; |
+}; |
+ |
+struct unwind_frame { |
+ union { |
+ jmp_buf jmpbuf; |
+ struct exc_result result; |
+ }; |
+ struct unwind_frame *next; |
+ uint32_t action_id; |
+}; |
+ |
+struct action_table_entry { |
+ int32_t type_filter_id; |
+ uint32_t next_action_id; |
+}; |
+ |
+extern std::type_info *const __pnacl_eh_type_table[]; |
+extern const struct action_table_entry __pnacl_eh_action_table[]; |
+extern const uint32_t __pnacl_eh_filter_table[]; |
+ |
+__thread struct unwind_frame *__pnacl_eh_stack; |
+ |
+ |
+static bool get_adjusted_ptr (const std::type_info *catch_type, |
+ const std::type_info *throw_type, |
+ void **thrown_ptr_p) { |
+ if (catch_type == NULL) |
+ return true; |
+ |
+ void *thrown_ptr = *thrown_ptr_p; |
+ |
+ // Pointer types need to adjust the actual pointer, not |
+ // the pointer to pointer that is the exception object. |
+ // This also has the effect of passing pointer types |
+ // "by value" through the __cxa_begin_catch return value. |
+ if (throw_type->__is_pointer_p ()) |
+ thrown_ptr = *(void **) thrown_ptr; |
+ |
+ if (catch_type->__do_catch (throw_type, &thrown_ptr, 1)) { |
+ *thrown_ptr_p = thrown_ptr; |
+ return true; |
+ } |
+ return false; |
+} |
+ |
+static int check_exception_spec(const std::type_info *throw_type, void *obj, |
+ int32_t filter_id) { |
+ const uint32_t *filter_ptr = |
+ &__pnacl_eh_filter_table[-filter_id - 1]; |
+ for (; *filter_ptr != -1; ++filter_ptr) { |
+ std::type_info *catch_type = __pnacl_eh_type_table[*filter_ptr]; |
+ // If any type matches, the overall filter doesn't trigger. |
+ // We ignore the modified value of obj. |
+ if (get_adjusted_ptr(catch_type, throw_type, &obj)) |
+ return 1; |
+ } |
+ // No type matched, so we have an exception specification error. |
+ return 0; |
+} |
+ |
+static int filter_matches(std::type_info *throw_type, void **obj, |
+ int32_t filter_id) { |
+ if (filter_id < 0) |
+ return !check_exception_spec(throw_type, obj, filter_id); |
+ |
+ std::type_info *catch_type = __pnacl_eh_type_table[filter_id]; |
+ printf(" exception id: type_id=%i -> %p (%s)\n", |
+ filter_id, catch_type, |
+ catch_type ? catch_type->name() : "catch-all"); |
+ if (get_adjusted_ptr(catch_type, throw_type, obj)) { |
+ printf(" match found (%s match)\n", |
+ throw_type == catch_type ? "exact" : "subtype"); |
+ return 1; |
+ } else { |
+ return 0; |
+ } |
+} |
+ |
+static void find_match(std::type_info *throw_type, void **obj, |
+ struct unwind_frame **result_frame, |
+ int *result_filter_id) { |
+ while (__pnacl_eh_stack != NULL) { |
+ struct unwind_frame *frame = __pnacl_eh_stack; |
+ __pnacl_eh_stack = frame->next; |
+ |
+ for (uint32_t action_id = frame->action_id; action_id != 0; ) { |
+ const struct action_table_entry *action = |
+ &__pnacl_eh_action_table[action_id - 1]; |
+ if (filter_matches(throw_type, obj, action->type_filter_id)) { |
+ // Match found. |
+ *result_frame = frame; |
+ *result_filter_id = action->type_filter_id; |
+ return; |
+ } |
+ action_id = action->next_action_id; |
+ } |
+ printf(" no match: trying next frame\n"); |
+ } |
+ |
+ printf("no match\n"); |
+ abort(); |
+} |
+ |
+__attribute__((noreturn)) |
+static void do_return(struct unwind_frame *frame, |
+ void *ue_header, int filter_id) { |
+ // We reuse the storage of jmpbuf for the landingpad data, so |
+ // save a copy of jmpbuf. |
+ jmp_buf jmpbuf_copy; |
+ memcpy(&jmpbuf_copy, &frame->jmpbuf, sizeof(jmpbuf_copy)); |
+ |
+ frame->result.exc = ue_header; |
+ frame->result.exc_type_id = filter_id; |
+ longjmp(jmpbuf_copy, 1); |
+ abort(); |
+} |
+ |
+static void do_unwinding(struct _Unwind_Exception *ue_header) { |
+ __cxa_exception *xh = __get_exception_header_from_ue(ue_header); |
+ printf(" thrown type: %p (%s)\n", |
+ xh->exceptionType, xh->exceptionType->name()); |
+ |
+ void *obj = __get_object_from_ue(ue_header); |
+ std::type_info *throw_type = xh->exceptionType; |
+ |
+ struct unwind_frame *frame; |
+ int filter_id; |
+ find_match(throw_type, &obj, &frame, &filter_id); |
+ |
+ xh->adjustedPtr = obj; |
+ xh->handlerSwitchValue = filter_id; |
+ |
+ do_return(frame, ue_header, filter_id); |
+} |
+ |
+extern "C" _Unwind_Reason_Code |
+__pnacl_eh_sjlj_Unwind_RaiseException(struct _Unwind_Exception *ue_header) { |
+ printf("_Unwind_RaiseException: %p\n", ue_header); |
+ |
+ do_unwinding(ue_header); |
+ abort(); |
+} |
+ |
+extern "C" void __pnacl_eh_resume(struct _Unwind_Exception *ue_header) { |
+ printf("__pnacl_eh_resume() called\n"); |
+ do_unwinding(ue_header); |
+} |
+ |
+extern "C" _Unwind_Reason_Code |
+_Unwind_Resume_or_Rethrow(struct _Unwind_Exception *ue_header) { |
+ printf("_Unwind_Resume_or_Rethrow: %p\n", ue_header); |
+ return _Unwind_RaiseException(ue_header); |
+} |
+ |
+extern "C" void |
+_Unwind_DeleteException (struct _Unwind_Exception *exc) |
+{ |
+ if (exc->exception_cleanup) |
+ (*exc->exception_cleanup) (_URC_FOREIGN_EXCEPTION_CAUGHT, exc); |
+} |
+ |
+extern "C" void __pnacl_eh_sjlj_cxa_call_unexpected(_Unwind_Exception *ue_header) { |
+ __cxa_exception *xh = __get_exception_header_from_ue(ue_header); |
+ int xh_switch_value = xh->handlerSwitchValue; |
+ std::terminate_handler xh_terminate_handler = xh->terminateHandler; |
+ |
+ __cxa_begin_catch(ue_header); |
+ |
+ // This function is a handler for our exception argument. If we exit |
+ // by throwing a different exception, we'll need the original cleaned up. |
+ struct end_catch_protect |
+ { |
+ end_catch_protect() { } |
+ ~end_catch_protect() { __cxa_end_catch(); } |
+ } end_catch_protect_obj; |
+ |
+ // We use the handler function that was cached by __cxa_throw(). |
+ // libstdc++'s unwind-cxx.h says this is cached because "The C++ |
+ // standard has entertaining rules wrt calling set_terminate and |
+ // set_unexpected in the middle of the exception cleanup process." |
+ printf(" calling 'unexpected' handler\n"); |
+ try { |
+ xh->unexpectedHandler(); |
+ } catch (...) { |
+ printf(" 'unexpected' handler threw\n"); |
+ __cxa_eh_globals *globals = __cxa_get_globals_fast (); |
+ __cxa_exception *new_xh = globals->caughtExceptions; |
+ void *new_ptr = __get_object_from_ambiguous_exception (new_xh); |
+ |
+ // If this new exception meets the exception spec, allow it. |
+ if (check_exception_spec( |
+ __get_exception_header_from_obj(new_ptr)->exceptionType, |
+ new_ptr, xh_switch_value)) { |
+ throw; |
+ } |
+ |
+ // If the exception spec allows std::bad_exception, throw that. |
+ // We don't have a thrown object to compare against, but since |
+ // bad_exception doesn't have virtual bases, that's OK; just pass 0. |
+ const std::type_info *bad_exc = &typeid(std::bad_exception); |
+ if (check_exception_spec(bad_exc, NULL, xh_switch_value)) |
+ throw std::bad_exception(); |
+ |
+ __terminate (xh_terminate_handler); |
+ } |
+ printf(" 'unexpected' handler returned\n"); |
+ std::terminate(); |
+} |