diff options
author | Simon Hausmann <simon.hausmann@digia.com> | 2013-05-12 12:07:15 +0200 |
---|---|---|
committer | Lars Knoll <lars.knoll@digia.com> | 2013-05-15 23:34:11 +0200 |
commit | 52012a548c030a5d299e0132a01657d848f4289f (patch) | |
tree | d0620c787fc0396562d54129ba44a1ca215c95d0 /src | |
parent | 69c6209e1c337af1cc55f48c78937083dd2eba7e (diff) |
Fix exception handling not working reliably on x86/x86-64 Linux (Part 2)
The registration of the unwind tables is done through the interposition
of the _Unwind_Find_FDE symbol from libgcc. Unfortunately that interposition
breaks when libgcc happens to come first in the linker scope. As it turns
out, the order is not for us to control, therefore the interposition may not
always work and our JIT generated functions may not get their unwind information
found at exception throwing time. That results in the program aborting with
an uncaught exception.
The proposed solution of replacing the interposition approach is two-fold:
(1) Go back to calling __register_frame explicitly, but only for functions that
exception _may_ pass through. In addition the performance of scalability of the
objects registered with __register_frame is a known issue upstream, as the LLVM
JIT also triggers the issue. It is being tracked at
http://gcc.gnu.org/bugzilla/show_bug.cgi?id=56460
(2) Instead of registering one FDE per function, we can theoretically register
one FDE for _all_ JIT generate functions, because they all use the same stack
layout that has exactly the same unwinding requirements from any function call
site. Since we can't guarantee the presence of all JIT generated code within
the same contiguous memory area, we can at least do it per executable memory
allocation chunk (page size). One chunk can contain many functions.
This patch implements part (2) by moving the per-function unwind info straight
to into the executable memory chunk and registering the entire chunk (page)
with libgcc. This also separates the regexp JIT executable memory from regular
functions, because only for the memory of the latter we need to register unwind
info.
Change-Id: Ic4d1978686463c6d319436c9083e4d7cf0409829
Reviewed-by: Lars Knoll <lars.knoll@digia.com>
Diffstat (limited to 'src')
-rw-r--r-- | src/3rdparty/masm/stubs/ExecutableAllocator.h | 3 | ||||
-rw-r--r-- | src/qml/qml/v4/qv4engine.cpp | 2 | ||||
-rw-r--r-- | src/qml/qml/v4/qv4engine_p.h | 1 | ||||
-rw-r--r-- | src/qml/qml/v4/qv4executableallocator.cpp | 12 | ||||
-rw-r--r-- | src/qml/qml/v4/qv4executableallocator_p.h | 19 | ||||
-rw-r--r-- | src/qml/qml/v4/qv4function_p.h | 1 | ||||
-rw-r--r-- | src/qml/qml/v4/qv4regexp.cpp | 2 | ||||
-rw-r--r-- | src/qml/qml/v4/qv4unwindhelper.cpp | 4 | ||||
-rw-r--r-- | src/qml/qml/v4/qv4unwindhelper_p-arm.h | 4 | ||||
-rw-r--r-- | src/qml/qml/v4/qv4unwindhelper_p-dw2.h | 90 | ||||
-rw-r--r-- | src/qml/qml/v4/qv4unwindhelper_p.h | 4 |
11 files changed, 78 insertions, 64 deletions
diff --git a/src/3rdparty/masm/stubs/ExecutableAllocator.h b/src/3rdparty/masm/stubs/ExecutableAllocator.h index 351851c68d..cd8d3e06f4 100644 --- a/src/3rdparty/masm/stubs/ExecutableAllocator.h +++ b/src/3rdparty/masm/stubs/ExecutableAllocator.h @@ -79,6 +79,9 @@ struct ExecutableMemoryHandle : public RefCounted<ExecutableMemoryHandle> { void* start() { return m_allocation->start(); } int sizeInBytes() { return m_size; } + QV4::ExecutableAllocator::ChunkOfPages *chunk() const + { return m_allocator->chunkForAllocation(m_allocation); } + QV4::ExecutableAllocator *m_allocator; QV4::ExecutableAllocator::Allocation *m_allocation; int m_size; diff --git a/src/qml/qml/v4/qv4engine.cpp b/src/qml/qml/v4/qv4engine.cpp index 9262436fd1..5adb6dac88 100644 --- a/src/qml/qml/v4/qv4engine.cpp +++ b/src/qml/qml/v4/qv4engine.cpp @@ -73,6 +73,7 @@ using namespace QV4; ExecutionEngine::ExecutionEngine(QQmlJS::EvalISelFactory *factory) : memoryManager(new QV4::MemoryManager) , executableAllocator(new QV4::ExecutableAllocator) + , regExpAllocator(new QV4::ExecutableAllocator) , bumperPointerAllocator(new WTF::BumpPointerAllocator) , debugger(0) , globalObject(0) @@ -258,6 +259,7 @@ ExecutionEngine::~ExecutionEngine() UnwindHelper::deregisterFunctions(functions); qDeleteAll(functions); delete memoryManager; + delete regExpAllocator; delete executableAllocator; } diff --git a/src/qml/qml/v4/qv4engine_p.h b/src/qml/qml/v4/qv4engine_p.h index 5c556c9d2d..7be77711cc 100644 --- a/src/qml/qml/v4/qv4engine_p.h +++ b/src/qml/qml/v4/qv4engine_p.h @@ -111,6 +111,7 @@ struct Q_QML_EXPORT ExecutionEngine { MemoryManager *memoryManager; ExecutableAllocator *executableAllocator; + ExecutableAllocator *regExpAllocator; QScopedPointer<QQmlJS::EvalISelFactory> iselFactory; ExecutionContext *current; diff --git a/src/qml/qml/v4/qv4executableallocator.cpp b/src/qml/qml/v4/qv4executableallocator.cpp index e710eba21b..4c5d238c99 100644 --- a/src/qml/qml/v4/qv4executableallocator.cpp +++ b/src/qml/qml/v4/qv4executableallocator.cpp @@ -113,6 +113,7 @@ bool ExecutableAllocator::Allocation::mergePrevious(ExecutableAllocator *allocat ExecutableAllocator::ChunkOfPages::~ChunkOfPages() { + delete unwindInfo; Allocation *alloc = firstAllocation; while (alloc) { Allocation *next = alloc->next; @@ -206,3 +207,14 @@ void ExecutableAllocator::free(Allocation *allocation) return; } } + +ExecutableAllocator::ChunkOfPages *ExecutableAllocator::chunkForAllocation(Allocation *allocation) const +{ + QMap<quintptr, ChunkOfPages*>::ConstIterator it = chunks.lowerBound(allocation->addr); + if (it != chunks.begin()) + --it; + if (it == chunks.end()) + return 0; + return *it; +} + diff --git a/src/qml/qml/v4/qv4executableallocator_p.h b/src/qml/qml/v4/qv4executableallocator_p.h index 0defd984a3..475445cc35 100644 --- a/src/qml/qml/v4/qv4executableallocator_p.h +++ b/src/qml/qml/v4/qv4executableallocator_p.h @@ -47,6 +47,7 @@ #include <QMultiMap> #include <QHash> #include <QVector> +#include <QByteArray> namespace WTF { class PageAllocation; @@ -58,8 +59,8 @@ namespace QV4 { class Q_AUTOTEST_EXPORT ExecutableAllocator { - struct ChunkOfPages; public: + struct ChunkOfPages; struct Allocation; ~ExecutableAllocator(); @@ -97,17 +98,31 @@ public: int freeAllocationCount() const { return freeAllocations.count(); } int chunkCount() const { return chunks.count(); } -private: + // Used to store CIE+FDE on x86/x86-64. + struct PlatformUnwindInfo + { + virtual ~PlatformUnwindInfo() {} + }; + struct ChunkOfPages { + ChunkOfPages() + : pages(0) + , firstAllocation(0) + , unwindInfo(0) + {} ~ChunkOfPages(); WTF::PageAllocation *pages; Allocation *firstAllocation; + PlatformUnwindInfo *unwindInfo; bool contains(Allocation *alloc) const; }; + ChunkOfPages *chunkForAllocation(Allocation *allocation) const; + +private: QMultiMap<size_t, Allocation*> freeAllocations; QMap<quintptr, ChunkOfPages*> chunks; }; diff --git a/src/qml/qml/v4/qv4function_p.h b/src/qml/qml/v4/qv4function_p.h index c7ae954879..d73dc8d28f 100644 --- a/src/qml/qml/v4/qv4function_p.h +++ b/src/qml/qml/v4/qv4function_p.h @@ -86,7 +86,6 @@ struct Function { const uchar *codeData; JSC::MacroAssemblerCodeRef codeRef; quint32 codeSize; - QByteArray unwindInfo; // CIE+FDE on x86/x86-64. Stored directly in code on ARM. QVector<String *> formals; QVector<String *> locals; diff --git a/src/qml/qml/v4/qv4regexp.cpp b/src/qml/qml/v4/qv4regexp.cpp index 053a32d6a1..e7e2187a50 100644 --- a/src/qml/qml/v4/qv4regexp.cpp +++ b/src/qml/qml/v4/qv4regexp.cpp @@ -112,7 +112,7 @@ RegExp::RegExp(ExecutionEngine* engine, const QString &pattern, bool ignoreCase, m_byteCode = JSC::Yarr::byteCompile(yarrPattern, engine->bumperPointerAllocator); #if ENABLE(YARR_JIT) if (!yarrPattern.m_containsBackreferences && engine->iselFactory->jitCompileRegexps()) { - JSC::JSGlobalData dummy(engine->executableAllocator); + JSC::JSGlobalData dummy(engine->regExpAllocator); JSC::Yarr::jitCompile(yarrPattern, JSC::Yarr::Char16, &dummy, m_jitCode); } #endif diff --git a/src/qml/qml/v4/qv4unwindhelper.cpp b/src/qml/qml/v4/qv4unwindhelper.cpp index 90750e9fe3..3d54ea277a 100644 --- a/src/qml/qml/v4/qv4unwindhelper.cpp +++ b/src/qml/qml/v4/qv4unwindhelper.cpp @@ -31,8 +31,8 @@ using namespace QV4; void UnwindHelper::ensureUnwindInfo(Function *function) {Q_UNUSED(function);} void UnwindHelper::registerFunction(Function *function) {Q_UNUSED(function);} -void UnwindHelper::registerFunctions(QVector<Function *> functions) {Q_UNUSED(functions);} +void UnwindHelper::registerFunctions(const QVector<Function *> &functions) {Q_UNUSED(functions);} void UnwindHelper::deregisterFunction(Function *function) {Q_UNUSED(function);} -void UnwindHelper::deregisterFunctions(QVector<Function *> functions) {Q_UNUSED(functions);} +void UnwindHelper::deregisterFunctions(const QVector<Function *> &functions) {Q_UNUSED(functions);} #endif // USE_NULL_HELPER diff --git a/src/qml/qml/v4/qv4unwindhelper_p-arm.h b/src/qml/qml/v4/qv4unwindhelper_p-arm.h index 92e38f7c80..7055c3a152 100644 --- a/src/qml/qml/v4/qv4unwindhelper_p-arm.h +++ b/src/qml/qml/v4/qv4unwindhelper_p-arm.h @@ -96,7 +96,7 @@ void UnwindHelper::deregisterFunction(Function *function) allFunctions.remove(reinterpret_cast<quintptr>(function->code)); } -void UnwindHelper::deregisterFunctions(QVector<Function *> functions) +void UnwindHelper::deregisterFunctions(const QVector<Function *> &functions) { QMutexLocker locker(&functionProtector); foreach (Function *f, functions) @@ -109,7 +109,7 @@ void UnwindHelper::registerFunction(Function *function) allFunctions.insert(reinterpret_cast<quintptr>(function->code), function); } -void UnwindHelper::registerFunctions(QVector<Function *> functions) +void UnwindHelper::registerFunctions(const QVector<Function *> &functions) { QMutexLocker locker(&functionProtector); foreach (Function *f, functions) diff --git a/src/qml/qml/v4/qv4unwindhelper_p-dw2.h b/src/qml/qml/v4/qv4unwindhelper_p-dw2.h index 841636d912..0e4296769c 100644 --- a/src/qml/qml/v4/qv4unwindhelper_p-dw2.h +++ b/src/qml/qml/v4/qv4unwindhelper_p-dw2.h @@ -5,6 +5,8 @@ #include "qv4functionobject_p.h" #include "qv4function_p.h" #include <wtf/Platform.h> +#include <wtf/PageAllocation.h> +#include <ExecutableAllocator.h> #include <QMap> #include <QMutex> @@ -45,27 +47,7 @@ static const int fde_offset = 20; static const int initial_location_offset = 28; static const int address_range_offset = 32; #endif -} // anonymous namespace - -static QMutex functionProtector; -static QMap<quintptr, Function*> allFunctions; -static Function *lookupFunction(void *pc) -{ - quintptr key = reinterpret_cast<quintptr>(pc); - QMap<quintptr, Function*>::ConstIterator it = allFunctions.lowerBound(key); - if (it != allFunctions.begin() && allFunctions.count() > 0) - --it; - if (it == allFunctions.end()) - return 0; - - quintptr codeStart = reinterpret_cast<quintptr>((*it)->code); - if (key < codeStart || key >= codeStart + (*it)->codeSize) - return 0; - return *it; -} - -namespace { void writeIntPtrValue(unsigned char *addr, intptr_t val) { addr[0] = (val >> 0) & 0xff; @@ -84,66 +66,66 @@ void writeIntPtrValue(unsigned char *addr, intptr_t val) extern "C" void __register_frame(void *fde); extern "C" void __deregister_frame(void *fde); +struct UnwindInfo : public ExecutableAllocator::PlatformUnwindInfo +{ + UnwindInfo(const QByteArray &cieFde); + virtual ~UnwindInfo(); + QByteArray data; +}; + +UnwindInfo::UnwindInfo(const QByteArray &cieFde) + : data(cieFde) +{ + __register_frame(data.data() + fde_offset); +} + +UnwindInfo::~UnwindInfo() +{ + __deregister_frame(data.data() + fde_offset); +} + void UnwindHelper::ensureUnwindInfo(Function *f) { if (!f->codeRef) return; // Not a JIT generated function - if (!f->unwindInfo.isEmpty()) + + JSC::ExecutableMemoryHandle *handle = f->codeRef.executableMemory(); + if (!handle) return; + ExecutableAllocator::ChunkOfPages *chunk = handle->chunk(); + + // Already registered? + if (chunk->unwindInfo) + return; + QByteArray info; info.resize(sizeof(cie_fde_data)); unsigned char *cie_and_fde = reinterpret_cast<unsigned char *>(info.data()); memcpy(cie_and_fde, cie_fde_data, sizeof(cie_fde_data)); - intptr_t ptr = static_cast<char *>(f->codeRef.code().executableAddress()) - static_cast<char *>(0); + intptr_t ptr = static_cast<char *>(chunk->pages->base()) - static_cast<char *>(0); writeIntPtrValue(cie_and_fde + initial_location_offset, ptr); - writeIntPtrValue(cie_and_fde + address_range_offset, f->codeSize); + writeIntPtrValue(cie_and_fde + address_range_offset, chunk->pages->size()); - f->unwindInfo = info; - __register_frame(f->unwindInfo.data() + fde_offset); -} - -static void registerFunctionUnlocked(Function *f) -{ - quintptr addr = reinterpret_cast<quintptr>(f->code); - if (allFunctions.contains(addr)) - return; - allFunctions.insert(addr, f); -} - -static void deregisterFunctionUnlocked(Function *f) -{ - allFunctions.remove(reinterpret_cast<quintptr>(f->code)); - if (!f->unwindInfo.isEmpty()) - __deregister_frame(f->unwindInfo.data() + fde_offset); + chunk->unwindInfo = new UnwindInfo(info); } -void UnwindHelper::registerFunction(Function *function) +void UnwindHelper::registerFunction(Function *) { - QMutexLocker locker(&functionProtector); - registerFunctionUnlocked(function); } -void UnwindHelper::registerFunctions(QVector<Function *> functions) +void UnwindHelper::registerFunctions(const QVector<Function *>&) { - QMutexLocker locker(&functionProtector); - foreach (Function *f, functions) - registerFunctionUnlocked(f); } -void UnwindHelper::deregisterFunction(Function *function) +void UnwindHelper::deregisterFunction(Function *) { - QMutexLocker locker(&functionProtector); - deregisterFunctionUnlocked(function); } -void UnwindHelper::deregisterFunctions(QVector<Function *> functions) +void UnwindHelper::deregisterFunctions(const QVector<Function *> &) { - QMutexLocker locker(&functionProtector); - foreach (Function *f, functions) - deregisterFunctionUnlocked(f); } } diff --git a/src/qml/qml/v4/qv4unwindhelper_p.h b/src/qml/qml/v4/qv4unwindhelper_p.h index 72bf56c531..7949278aad 100644 --- a/src/qml/qml/v4/qv4unwindhelper_p.h +++ b/src/qml/qml/v4/qv4unwindhelper_p.h @@ -12,9 +12,9 @@ class UnwindHelper public: static void ensureUnwindInfo(Function *function); static void registerFunction(Function *function); - static void registerFunctions(QVector<Function *> functions); + static void registerFunctions(const QVector<Function *> &functions); static void deregisterFunction(Function *function); - static void deregisterFunctions(QVector<Function *> functions); + static void deregisterFunctions(const QVector<Function *> &functions); #ifdef Q_PROCESSOR_ARM static int unwindInfoSize(); static void writeARMUnwindInfo(void *codeAddr, int codeSize); |