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..a0cf9acf66501bebb8fbe6fe36a212dfcf353f76 |
--- /dev/null |
+++ b/libstdc++-v3/libsupc++/eh_pnacl.cc |
@@ -0,0 +1,370 @@ |
+// -*- 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. |
+// |
+// This returns whether an exception (specified by the pointer, |
+// *THROWN_PTR_P, to an exception of type THROW_TYPE) matches the type |
+// CATCH_TYPE. That is, it returns whether THROW_TYPE is a subtype of |
+// CATCH_TYPE. |
+// |
+// If there's a match, get_adjusted_ptr() adjusts *THROWN_PTR_P to |
+// upcast it to be a pointer to type CATCH_TYPE. (For example, if |
+// THROW_TYPE uses multiple inheritance and derives from multiple base |
+// classes, this might involve adding a constant offset to |
+// *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 and the clause is a "catch" clause, this |
+// adjusts *OBJ to upcast it to the type specified in the "catch" |
+// clause. |
+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. |
+// The exception is specified by THROW_TYPE and *OBJ. |
+// |
+// If a frame is found that will handle the exception, this adjusts |
+// *OBJ (to upcast it to the "catch" type, if there is one), sets |
+// *RESULT_FRAME and *RESULT_CLAUSE_ID to the frame and clause ID that |
+// matched the exception, and returns true. |
+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. |
+ |
+ |
+// This implements _Unwind_RaiseException(). This is called when |
+// raising an exception for the first time, i.e. for the statement |
+// "throw EXPR;". The compiler lowers "throw EXPR;" to: |
+// * a call to __cxa_allocate_exception() to allocate memory; |
+// * a call to __cxa_throw() which throws the exception by calling |
+// _Unwind_RaiseException(). |
+// |
+// _Unwind_RaiseException() unwinds the stack, looking for the first |
+// C++ destructor or catch() block to pass control to. In LLVM terms, |
+// it searches for the first matching invoke/landingpad instruction. |
+// When it finds a match, this implementation passes control to the |
+// landingpad block by longjmp()'ing to it. |
+// |
+// In a traditional implementation (based on the Itanium ABI), |
+// _Unwind_RaiseException() is implemented in a library (libgcc_eh) |
+// that is separate from libsupc++. It calls back to libsupc++'s |
+// personality function (__gxx_personality_v0()) to determine whether |
+// a call on the stack has a handler for the exception. |
+// __gxx_personality_v0() knows that UE_HEADER was allocated by |
+// libsupc++'s __cxa_allocate_exception() and can downcast it to |
+// libsupc++'s __cxa_exception type. |
+// |
+// In contrast, in the implementation below, the functionality of the |
+// personality function is folded into _Unwind_RaiseException(), so |
+// this implements C++-specific matching of exceptions and downcasts |
+// UE_HEADER to __cxa_exception immediately. |
+// |
+// This function returns if stack unwinding did not find any stack |
+// frames that match the exception being thrown. |
+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)) |
+ { |
+ // Save adjusted exception pointer so that it can be returned by |
+ // __cxa_begin_catch() when entering a catch() block. |
+ xh->adjustedPtr = obj; |
+ |
+ // Save the clause ID so that if the landingpad block calls |
+ // __cxa_call_unexpected() and the std::set_unexpected() handler |
+ // throws an exception, we can re-check that exception against |
+ // the exception specification. |
+ 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)); |
+ |
+ // Return to the landingpad block, passing it two values. |
+ 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. |
+// |
+// This is called by a landingpad block as a final step after it has |
+// run C++ destructors. This is only called by a landingpad if it did |
+// not enter a catch() block. |
+// |
+// This function never returns. |
+extern "C" void |
+__pnacl_eh_resume (struct _Unwind_Exception *ue_header) |
+{ |
+ __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 (); |
+} |
+ |
+// _Unwind_Resume_or_Rethrow() is called when rethrowing an |
+// exception, i.e. for the statement "throw;" (with no arguments). |
+// The compiler lowers "throw;" to a call to __cxa_rethrow(), which |
+// calls this function. |
+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. |
+// |
+// This is called when a catch() block that handles an exception exits |
+// without rethrowing the exception. This is called by |
+// __cxa_end_catch(). The compiler generates a call to |
+// __cxa_end_catch() at the end of a catch() block. |
+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. |
+// |
+// __cxa_call_unexpected() is called by landingpad blocks when an |
+// exception has been thrown that doesn't match a function's exception |
+// specification. Calls to __cxa_call_unexpected() are generated by |
+// the compiler. |
+// |
+// __cxa_call_unexpected() calls the handler registered via |
+// std::set_unexpected(). This handler is allowed to throw, in which |
+// case we must re-check the resulting exception against the original |
+// exception specification. |
+// |
+// The reason that __cxa_call_unexpected() is called by landingpad |
+// code rather than by the personality function is so that the |
+// landingpad code can run destructors first. |
+// |
+// This function never returns. |
+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 set_unexpected() handler function that was cached by |
+ // __cxa_throw(). |
+ 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); |
+ } |
+} |