Merge remote-tracking branch 'upstream/master' into HEAD
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2f0da84 --- /dev/null +++ b/.gitignore
@@ -0,0 +1,25 @@ +#==============================================================================# +# This file specifies intentionally untracked files that git should ignore. +# See: http://www.kernel.org/pub/software/scm/git/docs/gitignore.html +# +# This file is intentionally different from the output of `git svn show-ignore`, +# as most of those are useless. +#==============================================================================# + +#==============================================================================# +# File extensions to be ignored anywhere in the tree. +#==============================================================================# +# Temp files created by most text editors. +*~ +# Merge files created by git. +*.orig +# Byte compiled python modules. +*.pyc +# vim swap files +.*.swp +.sw? + +#==============================================================================# +# Explicit files to ignore (only matches one). +#==============================================================================# +.gitusers
diff --git a/OWNERS b/OWNERS new file mode 100644 index 0000000..ac015a5 --- /dev/null +++ b/OWNERS
@@ -0,0 +1,8 @@ +dschuff@chromium.org +eliben@chromium.org +jfb@chromium.org +jvoung@chromium.org +kschimpf@chromium.org +mseaborn@chromium.org +sehr@chromium.org +stichnot@chromium.org
diff --git a/codereview.settings b/codereview.settings new file mode 100644 index 0000000..6dc999c --- /dev/null +++ b/codereview.settings
@@ -0,0 +1,10 @@ +# This file is used by gcl to get repository specific information. +CODE_REVIEW_SERVER: codereview.chromium.org +CC_LIST: native-client-reviews@googlegroups.com +VIEW_VC: https://gerrit.chromium.org/gerrit/gitweb?p=native_client/pnacl-libcxxabi.git;a=commit;h= +STATUS: http://nativeclient-status.appspot.com/status +TRY_ON_UPLOAD: False +TRYSERVER_PROJECT: nacl +TRYSERVER_SVN_URL: svn://svn.chromium.org/chrome-try/try-nacl +PUSH_URL_CONFIG: url.ssh://gerrit.chromium.org.pushinsteadof +ORIGIN_URL_CONFIG: http://chromium.googlesource.com
diff --git a/src/cxa_demangle.cpp b/src/cxa_demangle.cpp index 839aebe..39b6f3e 100644 --- a/src/cxa_demangle.cpp +++ b/src/cxa_demangle.cpp
@@ -4876,6 +4876,16 @@ *status = invalid_args; return nullptr; } + // @LOCALMOD-START The demangler is *huge* and only used in + // default_terminate_handler with a fallback to printing mangled + // names instead. pexe size matters a lot, so PNaCl only prints out + // mangled names when exceptions are uncaught. +#ifdef __pnacl__ + if (status) + *status = memory_alloc_failure; + return nullptr; +#else + // @LOCALMOD-END size_t internal_size = buf != nullptr ? *n : 0; arena<bs> a; Db db(a); @@ -4932,6 +4942,9 @@ if (status) *status = internal_status; return buf; + // @LOCALMOD-START +#endif // __pnacl__ + // @LOCALMOD-END } } // __cxxabiv1
diff --git a/src/cxa_personality.cpp b/src/cxa_personality.cpp index 97e5800..589a27c 100644 --- a/src/cxa_personality.cpp +++ b/src/cxa_personality.cpp
@@ -421,7 +421,14 @@ set_registers(_Unwind_Exception* unwind_exception, _Unwind_Context* context, const scan_results& results) { -#if __arm__ + // @LOCALMOD-START +#if defined(__pnacl__) + _Unwind_PNaClSetResult0( + context, reinterpret_cast<uintptr_t>(unwind_exception)); + _Unwind_PNaClSetResult1( + context, static_cast<uintptr_t>(results.ttypeIndex)); +#elif __arm__ + // @LOCALMOD-END _Unwind_SetGR(context, 0, reinterpret_cast<uintptr_t>(unwind_exception)); _Unwind_SetGR(context, 1, static_cast<uintptr_t>(results.ttypeIndex)); #else
diff --git a/src/cxa_pnacl_sjlj_exception.cpp b/src/cxa_pnacl_sjlj_exception.cpp new file mode 100644 index 0000000..0b9f7cf --- /dev/null +++ b/src/cxa_pnacl_sjlj_exception.cpp
@@ -0,0 +1,408 @@ +//===- cxa_pnacl_sjlj_exception.cpp - PNaCl SJLJ-based exception handling--===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This implements setjmp()/longjmp()-based (SJLJ) C++ exception +// handling for PNaCl. This uses the C++ exception info tables +// generated by the PNaClSjLjEH LLVM pass. +// +// 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 +// libc++/libcxxabi. When SJLJ EH is enabled, each +// __pnacl_eh_sjlj_Unwind_*() symbol will get renamed to _Unwind_*() +// when linking a PNaCl pexe. +// +//===----------------------------------------------------------------------===// + +#include <setjmp.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <typeinfo> + +#include "cxa_exception.hpp" +#include "cxa_handlers.hpp" +#include "private_typeinfo.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 __shim_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; + + +// Returns whether the thrown exception (specified by throw_type and +// obj) matches none of the exception types in a C++ exception +// specification (specified by filter_id). +static bool +exception_spec_can_catch(const __shim_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 != 0; ++filter_ptr) + { + const __shim_type_info *catch_type = + __pnacl_eh_type_table[*filter_ptr - 1]; + // We ignore the modified value of obj here. + if (catch_type->can_catch(throw_type, obj)) + return false; + } + // No type matched, so we have an exception specification error. + return true; +} + +// 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. (For example, if throw_type uses multiple inheritance and +// derives from multiple base classes, this might involve adding a +// constant offset to *obj.) +static bool +does_clause_match(const __shim_type_info *throw_type, void **obj, + int32_t clause_id) +{ + // Handle "cleanup" clause. + if (clause_id == 0) + return true; + + // Handle "filter" clause. + if (clause_id < 0) + return exception_spec_can_catch(throw_type, obj, clause_id); + + // Handle "catch" clause. + const __shim_type_info *catch_type = __pnacl_eh_type_table[clause_id - 1]; + if (catch_type == NULL) + return true; + return catch_type->can_catch(throw_type, *obj); +} + +// Returns whether the given frame should be entered in order to +// handle the thrown exception (specified by throw_type and *obj). If +// so, this adjusts *obj (see does_clause_match()) and sets +// *result_clause_id. +static bool +does_frame_match(const __shim_type_info *throw_type, void **obj, + struct exception_frame *frame, int32_t *result_clause_id) +{ + for (int32_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)) + { + *result_clause_id = list_node->clause_id; + return true; + } + clause_list_id = list_node->next_clause_list_id; + } + return false; +} + +// Search for a stack frame that will handle the given exception, +// starting from frame. 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 __shim_type_info *throw_type, void **obj, + struct exception_frame *frame, + struct exception_frame **result_frame, int32_t *result_clause_id) +{ + for (; frame != NULL; frame = frame->next) + { + if (does_frame_match(throw_type, obj, frame, result_clause_id)) + { + *result_frame = frame; + return true; + } + } + return false; +} + +// Search for a non-cleanup stack frame that will handle the given +// exception, starting from frame. Returns whether a matching frame +// was found. +static bool +is_exception_caught(const __shim_type_info *throw_type, void *obj, + struct exception_frame *frame) +{ + for (; frame != NULL; frame = frame->next) + { + int32_t clause_id; + if (does_frame_match(throw_type, &obj, frame, &clause_id) + && clause_id != 0) + return true; + } + return false; +} + +static __cxa_exception * +get_exception_header_from_ue(struct _Unwind_Exception *ue_header) +{ + return (__cxa_exception *) (ue_header + 1) - 1; +} + +static __cxa_dependent_exception * +get_dependent_exception_from_ue(struct _Unwind_Exception *ue_header) +{ + return (__cxa_dependent_exception *) (ue_header + 1) - 1; +} + +static void * +get_object_from_ue(struct _Unwind_Exception *ue_header) +{ + if (ue_header->exception_class == kOurDependentExceptionClass) + { + return get_dependent_exception_from_ue(ue_header)->primaryException; + } + return ue_header + 1; +} + +// handle_exception() is called by _Unwind_RaiseException(). It +// 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 libcxxabi. It calls back to libcxxabi'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 +// libcxxabi's __cxa_allocate_exception() and can downcast it to +// libcxxabi'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. +static void +handle_exception(struct _Unwind_Exception *ue_header, bool check_for_catch) +{ + __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((__shim_type_info *) xh->exceptionType, &obj, + __pnacl_eh_stack, &frame, &clause_id)) + return; + + // Check that there is a non-cleanup handler for the exception. + // If not, we should abort before running cleanup handlers + // (i.e. destructors). + // + // This is mainly a convenience for debugging. It means that if + // the program throws an uncaught exception, the location of the + // "throw" will be on the stack when the program aborts. If we + // ran cleanup handlers before aborting, this context would be + // lost. + // + // This is optional in 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". + if (check_for_catch && clause_id == 0 && + !is_exception_caught((__shim_type_info *) xh->exceptionType, obj, + frame->next)) + return; + + __pnacl_eh_stack = frame->next; + + // 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); +} + + +// 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(). +extern "C" _Unwind_Reason_Code +__pnacl_eh_sjlj_Unwind_RaiseException(struct _Unwind_Exception *ue_header) +{ + handle_exception(ue_header, true); + 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) +{ + // Pass check_for_catch=false so that unwinding does not take O(n^2) + // time in the number of cleanup landingpads entered before entering + // the catch() block. + handle_exception(ue_header, false); + + // We've run C++ destructors (cleanup handlers), but no further + // handlers were found, so abort. We should not reach here, because + // __pnacl_eh_sjlj_Unwind_RaiseException() already checked that + // there was a handler for this exception other than cleanup + // handlers. + __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 function implements __cxa_call_unexpected(), which is called +// by a landingpad block when an exception is thrown that doesn't +// match a function's exception spec (i.e. a "throw(...)" attribute on +// a function). Calls to __cxa_call_unexpected() are generated by the +// C++ front end. +// +// This calls the handler registered with 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 is loosely based on the __cxa_call_unexpected() implementation +// in cxa_personality.cpp. +// +// This function never returns. +extern "C" void +__pnacl_eh_sjlj_cxa_call_unexpected(struct _Unwind_Exception *ue_header) +{ + // Mark the exception as being handled, so that the + // set_unexpected() handler can rethrow it. + __cxa_begin_catch(ue_header); + + // Ensure that the corresponding __cxa_end_catch() call happens on + // all paths out of this function. + struct do_end_catch + { + ~do_end_catch() { __cxa_end_catch(); } + } do_end_catch_obj; + + __cxa_exception *old_exception_header = + get_exception_header_from_ue(ue_header); + int32_t filter_id = old_exception_header->handlerSwitchValue; + std::unexpected_handler u_handler = old_exception_header->unexpectedHandler; + std::terminate_handler t_handler = old_exception_header->terminateHandler; + + try + { + std::__unexpected(u_handler); + } + catch (...) + { + __cxa_eh_globals *globals = __cxa_get_globals_fast(); + __cxa_exception *new_exception_header = globals->caughtExceptions; + + // If the handler threw an exception that is allowed by the + // original exception spec, allow this exception to propagate. + if (!exception_spec_can_catch( + (const __shim_type_info *) new_exception_header->exceptionType, + get_object_from_ue(&new_exception_header->unwindHeader), + filter_id)) + throw; + + // Otherwise, if the original exception spec allows + // std::bad_exception, throw an exception of that type. + std::bad_exception be; + const __shim_type_info *be_type = + (const __shim_type_info *) &typeid(be); + if (!exception_spec_can_catch(be_type, &be, filter_id)) + throw be; + } + std::__terminate(t_handler); +}