aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorSimon Hausmann <simon.hausmann@digia.com>2013-05-12 12:07:15 +0200
committerLars Knoll <lars.knoll@digia.com>2013-05-15 23:34:11 +0200
commit52012a548c030a5d299e0132a01657d848f4289f (patch)
treed0620c787fc0396562d54129ba44a1ca215c95d0 /src
parent69c6209e1c337af1cc55f48c78937083dd2eba7e (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.h3
-rw-r--r--src/qml/qml/v4/qv4engine.cpp2
-rw-r--r--src/qml/qml/v4/qv4engine_p.h1
-rw-r--r--src/qml/qml/v4/qv4executableallocator.cpp12
-rw-r--r--src/qml/qml/v4/qv4executableallocator_p.h19
-rw-r--r--src/qml/qml/v4/qv4function_p.h1
-rw-r--r--src/qml/qml/v4/qv4regexp.cpp2
-rw-r--r--src/qml/qml/v4/qv4unwindhelper.cpp4
-rw-r--r--src/qml/qml/v4/qv4unwindhelper_p-arm.h4
-rw-r--r--src/qml/qml/v4/qv4unwindhelper_p-dw2.h90
-rw-r--r--src/qml/qml/v4/qv4unwindhelper_p.h4
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);