Skip to content
Snippets Groups Projects
Commit 2264e5b7 authored by Frederich Munch's avatar Frederich Munch Committed by Axel Naumann
Browse files

Windows: Exception handling.

parent 88593efa
No related branches found
No related tags found
No related merge requests found
......@@ -153,6 +153,15 @@ inline namespace windows {
std::string* UniversalSDK = nullptr,
bool Verbose = false);
///\brief Runtime override for _CxxThrowException in Interpreter.
//
__declspec(noreturn) void __stdcall ClingRaiseSEHException(void*, void*);
void RegisterEHFrames(uint8_t* Addr, size_t Size, uintptr_t BaseAddr,
bool Block);
void DeRegisterEHFrames(uint8_t* Addr, size_t Size);
} // namespace windows
#endif // LLVM_ON_WIN32
......
......@@ -347,24 +347,6 @@ namespace {
static void SetClingCustomLangOpts(LangOptions& Opts) {
Opts.EmitAllDecls = 0; // Otherwise if PCH attached will codegen all decls.
#ifdef _MSC_VER
#if 0 //_HAS_EXCEPTIONS
// FIXME: Disable exceptions on Windows for the time being.
// Enabling exceptions here makes 42 more tests failing.
// For example, CodeGeneration\RecursiveInit.C raises a unhandled exception in
// WinEHPrepare.cpp, at:
// if (UserI->isEHPad())
// report_fatal_error("Cleanup funclets for the MSVC++ personality cannot "
// "contain exceptional actions");
Opts.Exceptions = 1;
if (Opts.CPlusPlus) {
Opts.CXXExceptions = 1;
}
#else
Opts.Exceptions = 0;
if (Opts.CPlusPlus) {
Opts.CXXExceptions = 0;
}
#endif // _HAS_EXCEPTIONS
#ifdef _DEBUG
// FIXME: This requires bufferoverflowu.lib, but adding:
// #pragma comment(lib, "bufferoverflowu.lib") still gives errors!
......@@ -378,16 +360,17 @@ namespace {
Opts.Trigraphs = 0;
Opts.setDefaultCallingConv(clang::LangOptions::DCC_CDecl);
#else // !_MSC_VER
Opts.Exceptions = 1;
if (Opts.CPlusPlus) {
Opts.CXXExceptions = 1;
}
Opts.Trigraphs = 1;
// Opts.RTTIData = 1;
// Opts.DefaultCallingConventions = 1;
// Opts.StackProtector = 0;
#endif // _MSC_VER
Opts.Exceptions = 1;
if (Opts.CPlusPlus) {
Opts.CXXExceptions = 1;
}
Opts.Deprecated = 1;
//Opts.Modules = 1;
......
......@@ -127,6 +127,23 @@ class Azog: public RTDyldMemoryManager {
AllocInfo m_ROData;
AllocInfo m_RWData;
#ifdef LLVM_ON_WIN32
uintptr_t getBaseAddr() const {
if (LLVM_LIKELY(m_Code.m_Start && m_ROData.m_Start && m_RWData.m_Start)) {
return uintptr_t(std::min(std::min(m_Code.m_Start, m_ROData.m_Start),
m_RWData.m_Start));
}
if (LLVM_LIKELY(m_Code.m_Start)) {
return uintptr_t(m_ROData.m_Start
? std::min(m_Code.m_Start, m_ROData.m_Start)
: std::min(m_Code.m_Start, m_RWData.m_Start));
}
return uintptr_t(m_ROData.m_Start && m_RWData.m_Start
? std::min(m_ROData.m_Start, m_RWData.m_Start)
: std::max(m_ROData.m_Start, m_RWData.m_Start));
}
#endif
public:
Azog(cling::IncrementalJIT& Jit): m_jit(Jit) {}
......@@ -183,12 +200,20 @@ public:
void registerEHFrames(uint8_t *Addr, uint64_t LoadAddr,
size_t Size) override {
#ifdef LLVM_ON_WIN32
platform::RegisterEHFrames(Addr, Size, getBaseAddr(), true);
#else
return getExeMM()->registerEHFrames(Addr, LoadAddr, Size);
#endif
}
void deregisterEHFrames(uint8_t *Addr, uint64_t LoadAddr,
size_t Size) override {
#ifdef LLVM_ON_WIN32
platform::DeRegisterEHFrames(Addr, Size);
#else
return getExeMM()->deregisterEHFrames(Addr, LoadAddr, Size);
#endif
}
uint64_t getSymbolAddress(const std::string &Name) override {
......
......@@ -9,6 +9,9 @@
#include "cling/Interpreter/Interpreter.h"
#include "cling/Utils/Paths.h"
#ifdef LLVM_ON_WIN32
#include "cling/Utils/Platform.h"
#endif
#include "ClingUtils.h"
#include "DynamicLookup.h"
......@@ -431,6 +434,12 @@ namespace cling {
utils::FunctionToVoidPtr(&local_cxa_atexit), true);
// __dso_handle is inserted for the link phase, as macro is useless then
m_Executor->addSymbol("__dso_handle", this, true);
#ifdef LLVM_ON_WIN32
// Windows C++ SEH handler
m_Executor->addSymbol("_CxxThrowException",
utils::FunctionToVoidPtr(&platform::ClingRaiseSEHException), true);
#endif
}
if (m_Opts.Verbose())
......
......@@ -20,8 +20,10 @@
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Path.h"
#include <map>
#include <sstream>
#include <stdlib.h>
#include <vector>
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
......@@ -699,6 +701,192 @@ std::string Demangle(const std::string& Symbol) {
return af.Str ? std::string(af.Str) : std::string();
}
namespace windows {
// Use less memory and store the function ranges to watch as a mapping
// between of BaseAddr to Ranges watched.
//
typedef std::vector<std::pair<DWORD, DWORD>> ImageRanges;
typedef std::map<uintptr_t, ImageRanges> ImageBaseMap;
ImageBaseMap& getImageBaseMap() {
static ImageBaseMap sMap;
return sMap;
}
// Merge overlaping ranges
static void MergeRanges(ImageRanges &Ranges) {
std::sort(Ranges.begin(), Ranges.end());
ImageRanges Merged;
ImageRanges::iterator It = Ranges.begin();
auto Current = *(It)++;
while (It != Ranges.end()) {
if (Current.second+1 < It->first) {
Merged.push_back(Current);
Current = *(It);
} else
Current.second = std::max(Current.second, It->second);
++It;
}
Merged.emplace_back(Current);
Ranges.swap(Merged);
}
static uintptr_t FindEHFrame(uintptr_t Caller) {
ImageBaseMap& Unwind = getImageBaseMap();
for (auto&& Itr : Unwind) {
const uintptr_t CurAddr = Itr.first;
for (auto&& Rng : Itr.second) {
if (Caller >=(CurAddr + Rng.first) &&
Caller <= (CurAddr + Rng.second)) {
return CurAddr;
}
}
}
return 0;
}
void RegisterEHFrames(uint8_t* A, size_t Size, uintptr_t BaseAddr, bool Block) {
assert(getImageBaseMap().find(DWORD64(A)) == getImageBaseMap().end());
PRUNTIME_FUNCTION RFunc = reinterpret_cast<PRUNTIME_FUNCTION>(A);
const size_t N = Size / sizeof(RUNTIME_FUNCTION);
ImageBaseMap::mapped_type &Ranges = getImageBaseMap()[BaseAddr];
if (Block) {
// Merge all unwind adresses into a single contiguous block
Ranges.emplace_back(std::numeric_limits<DWORD>::max(),
std::numeric_limits<DWORD>::min());
ImageRanges::value_type& Range = Ranges.front();
for (PRUNTIME_FUNCTION It = RFunc, End = RFunc +N; It < End; ++It) {
Range.first = std::min(Range.first, It->BeginAddress);
Range.second = std::max(Range.second, It->EndAddress);
}
} else {
for (PRUNTIME_FUNCTION It = RFunc, End = RFunc +N; It < End; ++It)
Ranges.emplace_back(It->BeginAddress, It->EndAddress);
// Initial sort and merge
MergeRanges(Ranges);
}
::RtlAddFunctionTable(RFunc, N, BaseAddr);
}
void DeRegisterEHFrames(uint8_t* Addr, size_t Size) {
assert(getImageBaseMap().find(DWORD64(Addr)) != getImageBaseMap().end());
ImageBaseMap& Unwind = getImageBaseMap();
Unwind.erase(Unwind.find(DWORD64(Addr)));
::RtlDeleteFunctionTable(reinterpret_cast<PRUNTIME_FUNCTION>(Addr));
}
// Adapted from VisualStudio/VC/crt/src/vcruntime/throw.cpp
#ifdef _WIN64
#define _EH_RELATIVE_OFFSETS 1
#endif
// The NT Exception # that we use
#define EH_EXCEPTION_NUMBER ('msc' | 0xE0000000)
// The magic # identifying this version
#define EH_MAGIC_NUMBER1 0x19930520
#define EH_PURE_MAGIC_NUMBER1 0x01994000
// Number of parameters in exception record
#define EH_EXCEPTION_PARAMETERS 4
// A generic exception record
struct EHExceptionRecord {
DWORD ExceptionCode;
DWORD ExceptionFlags; // Flags determined by NT
_EXCEPTION_RECORD* ExceptionRecord; // Extra exception record (unused)
void* ExceptionAddress; // Address at which exception occurred
DWORD NumberParameters; // No. of parameters = EH_EXCEPTION_PARAMETERS
struct EHParameters {
DWORD magicNumber; // = EH_MAGIC_NUMBER1
void *pExceptionObject; // Pointer to the actual object thrown
struct ThrowInfo *pThrowInfo; // Description of thrown object
#if _EH_RELATIVE_OFFSETS
DWORD64 pThrowImageBase; // Image base of thrown object
#endif
} params;
};
__declspec(noreturn) void
__stdcall ClingRaiseSEHException(void* CxxExcept, void* Info) {
uintptr_t Caller;
static_assert(sizeof(Caller) == sizeof(PVOID), "Size mismatch");
USHORT Frames = CaptureStackBackTrace(1, 1,(PVOID*)&Caller, NULL);
assert(Frames && "No frames captured");
const DWORD64 BaseAddr = FindEHFrame(Caller);
if (BaseAddr == 0)
_CxxThrowException(CxxExcept, (_ThrowInfo*) Info);
// A generic exception record
EHExceptionRecord Exception = {
EH_EXCEPTION_NUMBER, // Exception number
EXCEPTION_NONCONTINUABLE, // Exception flags (we don't do resume)
nullptr, // Additional record (none)
nullptr, // Address of exception (OS fills in)
EH_EXCEPTION_PARAMETERS, // Number of parameters
{
EH_MAGIC_NUMBER1,
CxxExcept,
(struct ThrowInfo*)Info,
#if _EH_RELATIVE_OFFSETS
BaseAddr
#endif
}
};
// const ThrowInfo* pTI = (const ThrowInfo*)Info;
#ifdef THROW_ISWINRT
if (pTI && (THROW_ISWINRT((*pTI)))) {
// The pointer to the ExceptionInfo structure is stored sizeof(void*)
// infront of each WinRT Exception Info.
ULONG_PTR* EPtr = *reinterpret_cast<ULONG_PTR**>(CxxExcept);
EPtr--;
WINRTEXCEPTIONINFO** ppWei = reinterpret_cast<WINRTEXCEPTIONINFO**>(EPtr);
pTI = (*ppWei)->throwInfo;
(*ppWei)->PrepareThrow(ppWei);
}
#endif
// If the throw info indicates this throw is from a pure region,
// set the magic number to the Pure one, so only a pure-region
// catch will see it.
//
// Also use the Pure magic number on Win64 if we were unable to
// determine an image base, since that was the old way to determine
// a pure throw, before the TI_IsPure bit was added to the FuncInfo
// attributes field.
if (Info != nullptr) {
#ifdef THROW_ISPURE
if (THROW_ISPURE(*pTI))
Exception.params.magicNumber = EH_PURE_MAGIC_NUMBER1;
#if _EH_RELATIVE_OFFSETS
else
#endif // _EH_RELATIVE_OFFSETS
#endif // THROW_ISPURE
#if 0 && _EH_RELATIVE_OFFSETS
if (Exception.params.pThrowImageBase == 0)
Exception.params.magicNumber = EH_PURE_MAGIC_NUMBER1;
#endif // _EH_RELATIVE_OFFSETS
}
// Hand it off to the OS:
#if defined(_M_X64) && defined(_NTSUBSET_)
RtlRaiseException((PEXCEPTION_RECORD)&Exception);
#else
RaiseException(Exception.ExceptionCode, Exception.ExceptionFlags,
Exception.NumberParameters, (PULONG_PTR)&Exception.params);
#endif
}
} // namespace windows
} // namespace platform
} // namespace utils
} // namespace cling
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment