From: Simon Hausmann Date: Tue, 1 Oct 2013 13:10:33 +0000 (+0200) Subject: Change v4 exceptions to use the common C++ ABIs foreign exceptions X-Git-Tag: upstream/5.2.1~300 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=8abf7f5876a48c0879bce628597533c7b6eca9a0;p=platform%2Fupstream%2Fqtdeclarative.git Change v4 exceptions to use the common C++ ABIs foreign exceptions On platforms where we use the common C++ ABI, throw the exception not using a dummy C++ exception structure and the throw keyboard, but instead use the lower-level _Unwind_RaiseException to throw a foreign exception. It is caught with the existing "catch (...)" and re-throw is implemented similarly, by grabbing the current exception from the globals (a standardized data structure) and re-throwing it. On platforms such as ARM that lack hooks for supplying our unwind tables to the system run-time, this patch will make it possible to link the unwinder statically into libQtQml (libgcc or libunwind) and thus force it to use our unwind tables, because throwing or re-throwing will always go through our statically linked code through direct calls to _Unwind_RaiseException (instead of libstdc++). Change-Id: Ic2ac056fc7ed9e93fb51e30ab45f35b260487c5f Reviewed-by: Lars Knoll --- diff --git a/src/qml/jsruntime/jsruntime.pri b/src/qml/jsruntime/jsruntime.pri index 1739765..88cd3a9 100644 --- a/src/qml/jsruntime/jsruntime.pri +++ b/src/qml/jsruntime/jsruntime.pri @@ -112,12 +112,11 @@ linux*|mac { LIBS += -ldl } -# Only on Android/ARM at the moment, because only there we have issues -# replacing __gnu_Unwind_Find_exidx with our own implementation, -# and thus require static libgcc linkage. -android:equals(QT_ARCH, "arm"):*g++* { - static_libgcc = $$system($$QMAKE_CXX -print-file-name=libgcc.a) - LIBS += $$static_libgcc +!win32:!ios { + *g++*:equals(QT_ARCH, "arm") { + static_libgcc = $$system($$QMAKE_CXX -print-file-name=libgcc.a) + LIBS += $$static_libgcc + } SOURCES += $$PWD/qv4exception_gcc.cpp DEFINES += V4_CXX_ABI_EXCEPTION } diff --git a/src/qml/jsruntime/qv4context.cpp b/src/qml/jsruntime/qv4context.cpp index 78bf662..4bba0bf 100644 --- a/src/qml/jsruntime/qv4context.cpp +++ b/src/qml/jsruntime/qv4context.cpp @@ -648,7 +648,7 @@ void ExecutionContext::throwUnimplemented(const QString &message) ReturnedValue ExecutionContext::catchException(StackTrace *trace) { if (!engine->hasException) - throw; + Exception::rethrow(); while (engine->current != this) engine->popContext(); if (trace) @@ -666,7 +666,7 @@ void ExecutionContext::rethrowException() while (engine->current != this) engine->popContext(); } - throw; + Exception::rethrow(); } void ExecutionContext::throwReferenceError(const ValueRef value) diff --git a/src/qml/jsruntime/qv4exception.cpp b/src/qml/jsruntime/qv4exception.cpp index f55dc94..8cee153 100644 --- a/src/qml/jsruntime/qv4exception.cpp +++ b/src/qml/jsruntime/qv4exception.cpp @@ -98,6 +98,11 @@ void Exception::throwException(ExecutionContext *context, const ValueRef value) } #if !defined(V4_CXX_ABI_EXCEPTION) +void Exception::rethrow() +{ + throw; +} + struct DummyException {}; diff --git a/src/qml/jsruntime/qv4exception_gcc.cpp b/src/qml/jsruntime/qv4exception_gcc.cpp index 0d230ea..d3c406f 100644 --- a/src/qml/jsruntime/qv4exception_gcc.cpp +++ b/src/qml/jsruntime/qv4exception_gcc.cpp @@ -41,24 +41,14 @@ #include "qv4exception_p.h" -#include +// On arm we link libgcc statically and want to avoid exporting the _Unwind* symbols +#if defined(Q_PROCESSOR_ARM) +#define HIDE_EXPORTS +#endif + #include -#include -#include -#include #include -/* - * This is a little bit hacky as it relies on the fact that exceptions are - * reference counted in libstdc++ and that affects the layout of the standardized - * cxa_exception, making it bigger. LLVM's libcxxabi stores the reference count - * differently, so this here is entirely GNU libstdc++ specific. - * - * Eliminating this dependency is doable but requires replacing the use of C++ exceptions - * with foreign exceptions (a different exception class) and then using __cxa_get_globals - * to get hold of the exception inside the catch (...). AFAICS that would be portable. - */ - namespace { // 2.1.1 from http://mentorembedded.github.io/cxx-abi/abi-eh.html @@ -82,67 +72,63 @@ struct cxa_exception { _Unwind_Exception unwindHeader; }; -// This is what libstdc++ actually allocates -struct gcc_refcounted_compatible_exception { - _Atomic_word refCount; - cxa_exception x; +struct cxa_eh_globals +{ + cxa_exception *caughtExceptions; + unsigned int uncaughtExceptions; +#ifdef __ARM_EABI_UNWINDER__ + cxa_exception* propagatingExceptions; +#endif }; } +extern "C" cxa_eh_globals *__cxa_get_globals(); + static void exception_cleanup(_Unwind_Reason_Code, _Unwind_Exception *ex) { - gcc_refcounted_compatible_exception *exception = reinterpret_cast(ex + 1) - 1; - if (!--exception->refCount) { - if (exception->x.exceptionDestructor) - exception->x.exceptionDestructor(ex + 1); - abi::__cxa_free_exception(ex + 1); - } + free(ex); } -struct DummyException -{ - virtual ~DummyException() {} -}; +QT_BEGIN_NAMESPACE -static void exception_destructor(void *ex) +using namespace QV4; + +void Exception::rethrow() { - reinterpret_cast(ex)->~DummyException(); -} + cxa_eh_globals *globals = __cxa_get_globals(); + cxa_exception *exception = globals->caughtExceptions; -QT_BEGIN_NAMESPACE + // Make sure we only re-throw our foreign exceptions. For general re-throw + // we'd need different code. +#ifndef __ARM_EABI_UNWINDER__ + Q_ASSERT(exception->unwindHeader.exception_class == 0x514d4c4a53563400); // QMLJSV40 +#endif -using namespace QV4; + globals->caughtExceptions = 0; + _Unwind_RaiseException(&exception->unwindHeader); +} void Exception::throwInternal() { - void *rawException = abi::__cxa_allocate_exception(sizeof(QV4::Exception)); - gcc_refcounted_compatible_exception *refCountedException = reinterpret_cast(rawException) - 1; - cxa_exception *exception = &refCountedException->x; - - (void)new (rawException) DummyException(); - - refCountedException->refCount = 1; - exception->typeInfo = const_cast(&typeid(DummyException)); - exception->exceptionDestructor = &exception_destructor; - exception->unexpectedHandler = std::unexpected; - exception->terminateHandler = std::terminate; - exception->unwindHeader.exception_cleanup = &exception_cleanup; + _Unwind_Exception *exception = (_Unwind_Exception*)malloc(sizeof(_Unwind_Exception)); + memset(exception, 0, sizeof(*exception)); + exception->exception_cleanup = &exception_cleanup; + #ifdef __ARM_EABI_UNWINDER__ - exception->unwindHeader.exception_class[0] = 'G'; - exception->unwindHeader.exception_class[1] = 'N'; - exception->unwindHeader.exception_class[2] = 'U'; - exception->unwindHeader.exception_class[3] = 'C'; - exception->unwindHeader.exception_class[4] = 'C'; - exception->unwindHeader.exception_class[5] = '+'; - exception->unwindHeader.exception_class[6] = '+'; - exception->unwindHeader.exception_class[7] = 0; + exception->exception_class[0] = 'Q'; + exception->exception_class[1] = 'M'; + exception->exception_class[2] = 'L'; + exception->exception_class[3] = 'J'; + exception->exception_class[4] = 'S'; + exception->exception_class[5] = 'V'; + exception->exception_class[6] = '4'; + exception->exception_class[7] = 0; #else - exception->unwindHeader.exception_class = 0x474e5543432b2b00; // GNUCC++0 + exception->exception_class = 0x514d4c4a53563400; // QMLJSV40 #endif - _Unwind_RaiseException(&exception->unwindHeader); - abi::__cxa_begin_catch(rawException); + _Unwind_RaiseException(exception); std::terminate(); } diff --git a/src/qml/jsruntime/qv4exception_p.h b/src/qml/jsruntime/qv4exception_p.h index 1f02d11..8889630 100644 --- a/src/qml/jsruntime/qv4exception_p.h +++ b/src/qml/jsruntime/qv4exception_p.h @@ -51,6 +51,7 @@ namespace QV4 { struct Q_QML_EXPORT Exception { static void Q_NORETURN throwException(ExecutionContext *throwingContext, const ValueRef exceptionValue); + static void Q_NORETURN rethrow(); private: static void Q_NORETURN throwInternal();