Chromium Code Reviews| 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..1b4c6ef73137c838ce9f56af66ea10fc83125d9a |
| --- /dev/null |
| +++ b/libstdc++-v3/libsupc++/eh_pnacl.cc |
| @@ -0,0 +1,287 @@ |
| +// -*- C++ -*- Exception handling routines for PNaCl. |
| +// Copyright (C) 2013 |
| +// Free Software Foundation, Inc. |
| +// |
| +// This file is part of GCC. |
| +// |
| +// GCC is free software; you can redistribute it and/or modify |
| +// it under the terms of the GNU General Public License as published by |
| +// the Free Software Foundation; either version 3, or (at your option) |
| +// any later version. |
| +// |
| +// GCC is distributed in the hope that it will be useful, |
| +// but WITHOUT ANY WARRANTY; without even the implied warranty of |
| +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| +// GNU General Public License for more details. |
| +// |
| +// Under Section 7 of GPL version 3, you are granted additional |
| +// permissions described in the GCC Runtime Library Exception, version |
| +// 3.1, as published by the Free Software Foundation. |
| + |
| +// You should have received a copy of the GNU General Public License and |
| +// a copy of the GCC Runtime Library Exception along with this program; |
| +// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see |
| +// <http://www.gnu.org/licenses/>. |
| + |
| +#include <setjmp.h> |
| +#include <stdint.h> |
| +#include <stdlib.h> |
| +#include <string.h> |
| +#include <typeinfo> |
| +#include "unwind-cxx.h" |
| + |
| +using namespace __cxxabiv1; |
| + |
| + |
| +// Exception info written by ExceptionInfoWriter.cpp. |
| + |
| +struct action_table_entry |
| +{ |
| + int32_t clause_id; |
| + uint32_t next_clause_list_id; |
| +}; |
| + |
| +extern const struct action_table_entry __pnacl_eh_action_table[]; |
| +extern const std::type_info *const __pnacl_eh_type_table[]; |
| +extern const int32_t __pnacl_eh_filter_table[]; |
| + |
| +// Data structures used by PNaClSjLjEH.cpp. |
| + |
| +struct landing_pad_result |
| +{ |
| + void *exception_obj; |
| + uint32_t matched_clause_id; |
| +}; |
| + |
| +struct exception_frame |
| +{ |
| + union |
| + { |
| + jmp_buf jmpbuf; |
| + struct landing_pad_result result; |
| + }; |
| + struct exception_frame *next; |
| + uint32_t clause_list_id; |
| +}; |
| + |
| +__thread struct exception_frame *__pnacl_eh_stack; |
| + |
| + |
| +// This is based on get_adjusted_ptr() in eh_personality.cc. |
| +// |
| +// Given the thrown type THROW_TYPE, pointer to a variable containing a |
| +// pointer to the exception object THROWN_PTR_P and a type CATCH_TYPE to |
| +// compare against, return whether or not there is a match and if so, |
| +// update *THROWN_PTR_P. |
| +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; |
| +} |
| + |
| +// Returns whether the thrown exception (specified by THROW_TYPE and |
| +// OBJ) matches one of the exception types in a C++ exception |
| +// specification (specified by FILTER_ID). |
| +static bool |
| +check_exception_spec (const std::type_info *throw_type, void *obj, |
| + int32_t filter_id) |
| +{ |
| + const int32_t *filter_ptr = |
| + &__pnacl_eh_filter_table[-filter_id - 1]; |
| + for (; *filter_ptr != -1; ++filter_ptr) |
| + { |
| + const std::type_info *catch_type = __pnacl_eh_type_table[*filter_ptr]; |
| + // We ignore the modified value of obj here. |
| + if (get_adjusted_ptr (catch_type, throw_type, &obj)) |
| + return true; |
| + } |
| + // No type matched, so we have an exception specification error. |
| + return false; |
| +} |
| + |
| +// Returns whether the thrown exception (specified by THROW_TYPE and |
| +// OBJ) matches the given landingpad clause (CLAUSE_ID). If the |
| +// exception matches, this adjusts *OBJ. |
| +static bool |
| +does_clause_match (const std::type_info *throw_type, void **obj, |
| + int32_t clause_id) |
| +{ |
| + if (clause_id < 0) |
| + return !check_exception_spec (throw_type, obj, clause_id); |
| + |
| + const std::type_info *catch_type = __pnacl_eh_type_table[clause_id]; |
| + return get_adjusted_ptr (catch_type, throw_type, obj); |
| +} |
| + |
| +// Search for a stack frame that will handle the given exception. |
| +static bool |
| +find_match (const std::type_info *throw_type, void **obj, |
| + struct exception_frame **result_frame, int32_t *result_clause_id) |
| +{ |
| + while (__pnacl_eh_stack != NULL) |
| + { |
| + struct exception_frame *frame = __pnacl_eh_stack; |
| + __pnacl_eh_stack = frame->next; |
| + |
| + for (uint32_t clause_list_id = frame->clause_list_id; |
| + clause_list_id != 0; ) |
| + { |
| + const struct action_table_entry *list_node = |
| + &__pnacl_eh_action_table[clause_list_id - 1]; |
| + if (does_clause_match (throw_type, obj, list_node->clause_id)) |
| + { |
| + // Match found. |
| + *result_frame = frame; |
| + *result_clause_id = list_node->clause_id; |
| + return true; |
| + } |
| + clause_list_id = list_node->next_clause_list_id; |
| + } |
| + } |
| + return false; |
| +} |
| + |
| +// Each __pnacl_eh_sjlj_Unwind_*() function below provides the |
| +// definition of _Unwind_*(). |
| +// |
| +// The "__pnacl_eh_sjlj" prefix is added so that PNaCl's SJLJ |
| +// (setjmp()/longjmp()-based) implementation of C++ exception handling |
| +// can coexist with other implementations in the same build of |
| +// libstdc++/libsupc++. When SJLJ EH is enabled, each |
| +// __pnacl_eh_sjlj_Unwind_*() symbol will get renamed to _Unwind_*() |
| +// when linking a PNaCl pexe. |
| + |
| +extern "C" _Unwind_Reason_Code |
| +__pnacl_eh_sjlj_Unwind_RaiseException (struct _Unwind_Exception *ue_header) |
| +{ |
| + __cxa_exception *xh = __get_exception_header_from_ue (ue_header); |
| + |
| + void *obj = __get_object_from_ue (ue_header); |
| + struct exception_frame *frame; |
| + int32_t clause_id; |
| + if (find_match (xh->exceptionType, &obj, &frame, &clause_id)) |
| + { |
| + xh->adjustedPtr = obj; |
| + xh->handlerSwitchValue = clause_id; |
| + |
| + // exception_frame uses the same location for storing the |
| + // jmp_buf and the landing_pad_result, so we must make a copy of |
| + // the jmp_buf first. |
| + jmp_buf jmpbuf_copy; |
| + memcpy (&jmpbuf_copy, &frame->jmpbuf, sizeof (jmpbuf_copy)); |
| + |
| + frame->result.exception_obj = ue_header; |
| + frame->result.matched_clause_id = clause_id; |
| + longjmp (jmpbuf_copy, 1); |
| + } |
| + |
| + return _URC_END_OF_STACK; |
| +} |
| + |
| +// This is the equivalent of _Unwind_Resume() from libgcc_eh, but we |
| +// use a different name for PNaCl SJLJ to avoid accidental collisions |
| +// with libgcc_eh. |
| +extern "C" void |
| +__pnacl_eh_resume (struct _Unwind_Exception *ue_header) |
|
Derek Schuff
2013/10/24 20:41:17
why __pnacl_eh_resume and not __pnacl_eh_sjlj_Unwi
Mark Seaborn
2013/10/24 21:43:54
My thinking here was that there is a distinction b
|
| +{ |
| + __pnacl_eh_sjlj_Unwind_RaiseException (ue_header); |
| + |
| + // We've run C++ destructors (cleanup handlers), but no further |
| + // handlers were found, so abort. |
| + // |
| + // Note that this situation does not normally happen with GNU C++, |
| + // which scans the stack to check for a matching catch or filter |
| + // clause, and calls std::terminate() if none is found, before |
| + // calling any cleanup handlers. |
| + // |
| + // Both behaviours are allowed by the C++ standard, which says "If |
| + // no matching handler is found, the function std::terminate() is |
| + // called; whether or not the stack is unwound before this call to |
| + // std::terminate() is implementation-defined". |
| + __cxa_begin_catch (ue_header); |
| + std::terminate (); |
| +} |
| + |
| +extern "C" _Unwind_Reason_Code |
| +__pnacl_eh_sjlj_Unwind_Resume_or_Rethrow (struct _Unwind_Exception *ue_header) |
| +{ |
| + return __pnacl_eh_sjlj_Unwind_RaiseException (ue_header); |
| +} |
| + |
| +// A convenience function that calls the exception_cleanup field. |
| +// Based on the definition in libgcc_eh's unwind.inc. |
| +extern "C" void |
| +__pnacl_eh_sjlj_Unwind_DeleteException (struct _Unwind_Exception *exc) |
| +{ |
| + if (exc->exception_cleanup) |
| + (*exc->exception_cleanup) (_URC_FOREIGN_EXCEPTION_CAUGHT, exc); |
| +} |
| + |
| +// This is based on __cxa_call_unexpected in eh_personality.cc, |
| +// adapted to use PNaCl's exception info instead of the non-portable |
| +// LSDA. |
| +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." |
| + try |
| + {__unexpected (xh->unexpectedHandler); } |
| + catch (...) |
| + { |
| + __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); |
| + } |
| +} |