| 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();
|
| +}
|
|
|