aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorSimon Hausmann <simon.hausmann@digia.com>2013-07-10 17:01:47 +0200
committerThe Qt Project <gerrit-noreply@qt-project.org>2013-07-17 11:26:32 +0200
commita62e32cae6606db64632e418262ab01e7806480c (patch)
tree6112b70be61f6796b8d84289600d016c8eafeebe /src
parent54435e420671b5f77e724260516d4154aef245ac (diff)
Implement gcc compatible exception throwing using _Unwind_RaiseException
On ARM/Android we have the problem of not being able to inject the ARM exception unwind tables into the exception handling run-time - we need to be able to override the __gnu_Unwind_Find_exidx symbol, which is weakly declared in libgcc and strong in libc or the dynamic linker. The solution is to avoid throwing the exception from within libstdc++ but instead do it ourselves and include the unwinding code along with it, so that we can replace the symbol. This works by throwing the exception the way the standard describes __cxa_throw() works, eventually calling _Unwind_RaiseException. That function in turn will (indirectly) reference __gnu_Unwind_Find_exidx. If our code references _Unwind_RaiseException and we link libgcc statically, then the linker will include all the dependencies of _Unwind_RaiseException in our library, including the resolution of the weak __gnu_Find_exidx symbol to our implementation in qv4unwindhelper_p-arm.h. The "catch" with this approach is that it relies on the layout of the exception object in libstdc++, which deviates from the standard by an added reference count. The code in question has NOT changed in years, so for now that's a risk we accept. A different solution that only depends on the cross-vendor C++ ABI (implemented by pretty much any compiler other than MSVC that we support) would be to not throw a pure C++ exception but a foreign exception (still ABI compliant __cxa_exception, but with a different class). The GNU personality routine would still catch it if we used catch (...), and we could get hold of the __cxa_exception pointer via the standardized __cxa_get_globals(). But that in turn would require replacing the use of "catch (QV4::Exception &e) { ..." in our client code with macros that abstract away the difference between the win32 C++ based exceptions and the CXX ABI way, thus making the catch code a little uglier. Change-Id: I3f9bfaa9f657e80ebe4f88369cf51922367570b1 Reviewed-by: Erik Verbruggen <erik.verbruggen@digia.com>
Diffstat (limited to 'src')
-rw-r--r--src/qml/qml/v4/qv4exception.cpp9
-rw-r--r--src/qml/qml/v4/qv4exception_gcc.cpp143
-rw-r--r--src/qml/qml/v4/qv4exception_p.h3
-rw-r--r--src/qml/qml/v4/v4.pri10
4 files changed, 164 insertions, 1 deletions
diff --git a/src/qml/qml/v4/qv4exception.cpp b/src/qml/qml/v4/qv4exception.cpp
index de8604e97e..9f15c27ffc 100644
--- a/src/qml/qml/v4/qv4exception.cpp
+++ b/src/qml/qml/v4/qv4exception.cpp
@@ -83,7 +83,7 @@ void Exception::throwException(ExecutionContext *context, const Value &value)
printf("stack walked. throwing exception now...\n");
#endif
- throw Exception(context, value);
+ throwInternal(context, value);
}
Exception::Exception(ExecutionContext *throwingContext, const Value &exceptionValue)
@@ -119,4 +119,11 @@ void Exception::partiallyUnwindContext(ExecutionContext *catchingContext)
throwingContext = context;
}
+#if !defined(V4_CXX_ABI_EXCEPTION)
+void Exception::throwInternal(ExecutionContext *throwingContext, const Value &exceptionValue)
+{
+ throw Exception(throwingContext, exceptionValue);
+}
+#endif
+
QT_END_NAMESPACE
diff --git a/src/qml/qml/v4/qv4exception_gcc.cpp b/src/qml/qml/v4/qv4exception_gcc.cpp
new file mode 100644
index 0000000000..0324a06e0b
--- /dev/null
+++ b/src/qml/qml/v4/qv4exception_gcc.cpp
@@ -0,0 +1,143 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qv4exception_p.h"
+
+#include <unwind.h>
+#include <cxxabi.h>
+#include <bits/atomic_word.h>
+#include <typeinfo>
+#include <exception>
+
+/*
+ * 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
+struct cxa_exception {
+ std::type_info *typeInfo;
+ void (*exceptionDestructor)(void*);
+ std::unexpected_handler unexpectedHandler;
+ std::terminate_handler terminateHandler;
+ cxa_exception *nextException;
+ int handlerCount;
+#ifdef __ARM_EABI_UNWINDER__
+ cxa_exception *nextPropagatingException;
+ int propagationCount;
+#else
+ int handlerSwitchValue;
+ const char *actionRecord;
+ const char *languageSpecificData;
+ void *catchTemp;
+ void *adjustedPtr;
+#endif
+ _Unwind_Exception unwindHeader;
+};
+
+// This is what libstdc++ actually allocates
+struct gcc_refcounted_compatible_exception {
+ _Atomic_word refCount;
+ cxa_exception x;
+};
+
+}
+
+static void exception_cleanup(_Unwind_Reason_Code, _Unwind_Exception *ex)
+{
+ gcc_refcounted_compatible_exception *exception = reinterpret_cast<gcc_refcounted_compatible_exception *>(ex + 1) - 1;
+ if (!--exception->refCount) {
+ if (exception->x.exceptionDestructor)
+ exception->x.exceptionDestructor(ex + 1);
+ abi::__cxa_free_exception(ex + 1);
+ }
+}
+
+static void exception_destructor(void *ex)
+{
+ reinterpret_cast<QV4::Exception *>(ex)->~Exception();
+}
+
+QT_BEGIN_NAMESPACE
+
+using namespace QV4;
+
+void Exception::throwInternal(ExecutionContext *throwingContext, const Value &exceptionValue)
+{
+ void *rawException = abi::__cxa_allocate_exception(sizeof(QV4::Exception));
+ gcc_refcounted_compatible_exception *refCountedException = reinterpret_cast<gcc_refcounted_compatible_exception *>(rawException) - 1;
+ cxa_exception *exception = &refCountedException->x;
+
+ (void)new (rawException) Exception(throwingContext, exceptionValue);
+
+ refCountedException->refCount = 1;
+ exception->typeInfo = const_cast<std::type_info*>(&typeid(Exception));
+ exception->exceptionDestructor = &exception_destructor;
+ exception->unexpectedHandler = std::unexpected;
+ exception->terminateHandler = std::terminate;
+ exception->unwindHeader.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;
+#else
+ exception->unwindHeader.exception_class = 0x474e5543432b2b00; // GNUCC++0
+#endif
+
+ _Unwind_RaiseException(&exception->unwindHeader);
+ abi::__cxa_begin_catch(rawException);
+ std::terminate();
+}
+
+QT_END_NAMESPACE
diff --git a/src/qml/qml/v4/qv4exception_p.h b/src/qml/qml/v4/qv4exception_p.h
index d828092e6a..8ba06f57f4 100644
--- a/src/qml/qml/v4/qv4exception_p.h
+++ b/src/qml/qml/v4/qv4exception_p.h
@@ -63,12 +63,15 @@ struct Q_QML_EXPORT Exception {
ExecutionEngine::StackTrace stackTrace() const { return m_stackTrace; }
private:
+ void *operator new(size_t, void *p) { return p; }
+
explicit Exception(ExecutionContext *throwingContext, const Value &exceptionValue);
ExecutionContext *throwingContext;
bool accepted;
PersistentValue exception;
ExecutionEngine::StackTrace m_stackTrace;
+ static void throwInternal(ExecutionContext *throwingContext, const Value &exceptionValue);
};
} // namespace QV4
diff --git a/src/qml/qml/v4/v4.pri b/src/qml/qml/v4/v4.pri
index ddf8f5ba5a..1c63b4962a 100644
--- a/src/qml/qml/v4/v4.pri
+++ b/src/qml/qml/v4/v4.pri
@@ -161,6 +161,16 @@ 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
+ SOURCES += $$PWD/qv4exception_gcc.cpp
+ DEFINES += V4_CXX_ABI_EXCEPTION
+}
+
debug-with-libunwind {
UW_INC=$$(LIBUNWIND_INCLUDES)
isEmpty(UW_INC): error("Please set LIBUNWIND_INCLUDES")