forked from OSchip/llvm-project
329 lines
11 KiB
C++
329 lines
11 KiB
C++
//==-- examples/clang-interpreter/Manager.cpp - Clang C Interpreter Example -=//
|
|
//
|
|
// The LLVM Compiler Infrastructure
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "Manager.h"
|
|
|
|
#ifdef CLANG_INTERPRETER_WIN_EXCEPTIONS
|
|
#include "llvm/Support/DynamicLibrary.h"
|
|
|
|
#define WIN32_LEAN_AND_MEAN
|
|
#define NOGDI
|
|
#define NOMINMAX
|
|
#include <windows.h>
|
|
#endif
|
|
|
|
namespace interpreter {
|
|
|
|
using namespace llvm;
|
|
|
|
void SingleSectionMemoryManager::Block::Reset(uint8_t *Ptr, uintptr_t Size) {
|
|
assert(Ptr != nullptr && "Bad allocation");
|
|
Addr = Ptr;
|
|
End = Ptr ? Ptr + Size : nullptr;
|
|
}
|
|
|
|
uint8_t *SingleSectionMemoryManager::Block::Next(uintptr_t Size,
|
|
unsigned Alignment) {
|
|
uintptr_t Out = (uintptr_t)Addr;
|
|
|
|
// Align the out pointer properly
|
|
if (!Alignment)
|
|
Alignment = 16;
|
|
Out = (Out + Alignment - 1) & ~(uintptr_t)(Alignment - 1);
|
|
|
|
// RuntimeDyld should have called reserveAllocationSpace with an amount that
|
|
// will fit all required alignemnts...but assert on this to make sure.
|
|
assert((Out + Size) <= (uintptr_t)End && "Out of bounds");
|
|
|
|
// Set the next Addr to deliver at the end of this one.
|
|
Addr = (uint8_t *)(Out + Size);
|
|
return (uint8_t *)Out;
|
|
}
|
|
|
|
uint8_t *SingleSectionMemoryManager::allocateCodeSection(uintptr_t Size,
|
|
unsigned Align,
|
|
unsigned ID,
|
|
StringRef Name) {
|
|
return Code.Next(Size, Align);
|
|
}
|
|
|
|
uint8_t *SingleSectionMemoryManager::allocateDataSection(
|
|
uintptr_t Size, unsigned Align, unsigned ID, StringRef Name, bool RO) {
|
|
return RO ? ROData.Next(Size, Align) : RWData.Next(Size, Align);
|
|
}
|
|
|
|
void SingleSectionMemoryManager::reserveAllocationSpace(
|
|
uintptr_t CodeSize, uint32_t CodeAlign, uintptr_t ROSize, uint32_t ROAlign,
|
|
uintptr_t RWSize, uint32_t RWAlign) {
|
|
// FIXME: Ideally this should be one contiguous block, with Code, ROData,
|
|
// and RWData pointing to sub-blocks within, but setting the correct
|
|
// permissions for that wouldn't work unless we over-allocated to have each
|
|
// Block.Base aligned on a page boundary.
|
|
const unsigned SecID = 0;
|
|
Code.Reset(SectionMemoryManager::allocateCodeSection(CodeSize, CodeAlign,
|
|
SecID, "code"),
|
|
CodeSize);
|
|
|
|
ROData.Reset(SectionMemoryManager::allocateDataSection(ROSize, ROAlign, SecID,
|
|
"rodata", true/*RO*/),
|
|
ROSize);
|
|
|
|
RWData.Reset(SectionMemoryManager::allocateDataSection(RWSize, RWAlign, SecID,
|
|
"rwdata", false/*RO*/),
|
|
RWSize);
|
|
|
|
#ifdef CLANG_INTERPRETER_WIN_EXCEPTIONS
|
|
ImageBase =
|
|
(uintptr_t)std::min(std::min(Code.Addr, ROData.Addr), RWData.Addr);
|
|
#endif
|
|
}
|
|
|
|
#ifdef CLANG_INTERPRETER_WIN_EXCEPTIONS
|
|
|
|
// Map an "ImageBase" to a range of adresses that can throw.
|
|
//
|
|
class SEHFrameHandler {
|
|
typedef SingleSectionMemoryManager::EHFrameInfos EHFrameInfos;
|
|
typedef std::vector<std::pair<DWORD, DWORD>> ImageRanges;
|
|
typedef std::map<uintptr_t, ImageRanges> ImageBaseMap;
|
|
ImageBaseMap m_Map;
|
|
|
|
static void MergeRanges(ImageRanges &Ranges);
|
|
uintptr_t FindEHFrame(uintptr_t Caller);
|
|
|
|
public:
|
|
static __declspec(noreturn) void __stdcall RaiseSEHException(void *, void *);
|
|
void RegisterEHFrames(uintptr_t ImageBase, const EHFrameInfos &Frames,
|
|
bool Block = true);
|
|
void DeRegisterEHFrames(uintptr_t ImageBase, const EHFrameInfos &Frames);
|
|
};
|
|
|
|
// Merge overlaping ranges for faster searching with throwing PC
|
|
void SEHFrameHandler::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);
|
|
}
|
|
|
|
// Find the "ImageBase" for Caller/PC who is throwing an exception
|
|
uintptr_t SEHFrameHandler::FindEHFrame(uintptr_t Caller) {
|
|
for (auto &&Itr : m_Map) {
|
|
const uintptr_t ImgBase = Itr.first;
|
|
for (auto &&Rng : Itr.second) {
|
|
if (Caller >= (ImgBase + Rng.first) && Caller <= (ImgBase + Rng.second))
|
|
return ImgBase;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// Register a range of adresses for a single section that
|
|
void SEHFrameHandler::RegisterEHFrames(uintptr_t ImageBase,
|
|
const EHFrameInfos &Frames, bool Block) {
|
|
if (Frames.empty())
|
|
return;
|
|
assert(m_Map.find(ImageBase) == m_Map.end());
|
|
|
|
ImageBaseMap::mapped_type &Ranges = m_Map[ImageBase];
|
|
ImageRanges::value_type *BlockRange = nullptr;
|
|
if (Block) {
|
|
// Merge all unwind adresses into a single contiguous block for faster
|
|
// searching later.
|
|
Ranges.emplace_back(std::numeric_limits<DWORD>::max(),
|
|
std::numeric_limits<DWORD>::min());
|
|
BlockRange = &Ranges.back();
|
|
}
|
|
|
|
for (auto &&Frame : Frames) {
|
|
assert(m_Map.find(DWORD64(Frame.Addr)) == m_Map.end() &&
|
|
"Runtime function should not be a key!");
|
|
|
|
PRUNTIME_FUNCTION RFunc = reinterpret_cast<PRUNTIME_FUNCTION>(Frame.Addr);
|
|
const size_t N = Frame.Size / sizeof(RUNTIME_FUNCTION);
|
|
if (BlockRange) {
|
|
for (PRUNTIME_FUNCTION It = RFunc, End = RFunc + N; It < End; ++It) {
|
|
BlockRange->first = std::min(BlockRange->first, It->BeginAddress);
|
|
BlockRange->second = std::max(BlockRange->second, It->EndAddress);
|
|
}
|
|
} else {
|
|
for (PRUNTIME_FUNCTION It = RFunc, End = RFunc + N; It < End; ++It)
|
|
Ranges.emplace_back(It->BeginAddress, It->EndAddress);
|
|
}
|
|
|
|
::RtlAddFunctionTable(RFunc, N, ImageBase);
|
|
}
|
|
|
|
if (!Block)
|
|
MergeRanges(Ranges); // Initial sort and merge
|
|
}
|
|
|
|
void SEHFrameHandler::DeRegisterEHFrames(uintptr_t ImageBase,
|
|
const EHFrameInfos &Frames) {
|
|
if (Frames.empty())
|
|
return;
|
|
|
|
auto Itr = m_Map.find(ImageBase);
|
|
if (Itr != m_Map.end()) {
|
|
// Remove the ImageBase from lookup
|
|
m_Map.erase(Itr);
|
|
|
|
// Unregister all the PRUNTIME_FUNCTIONs
|
|
for (auto &&Frame : Frames)
|
|
::RtlDeleteFunctionTable(reinterpret_cast<PRUNTIME_FUNCTION>(Frame.Addr));
|
|
}
|
|
}
|
|
|
|
// FIXME: Rather than this static and overriding _CxxThrowException via
|
|
// DynamicLibrary::AddSymbol, a better route would be to transform the call
|
|
// to _CxxThrowException(Arg0, Arg1) -> RaiseSEHException(Arg0, Arg1, this)
|
|
// where 'this' is the SingleSectionMemoryManager instance. This could probably
|
|
// be done with clang, and definitely possible by injecting an llvm-IR function
|
|
// into the module with the name '_CxxThrowException'
|
|
//
|
|
static SEHFrameHandler sFrameHandler;
|
|
|
|
void SingleSectionMemoryManager::deregisterEHFrames() {
|
|
sFrameHandler.DeRegisterEHFrames(ImageBase, EHFrames);
|
|
EHFrameInfos().swap(EHFrames);
|
|
}
|
|
|
|
bool SingleSectionMemoryManager::finalizeMemory(std::string *ErrMsg) {
|
|
sFrameHandler.RegisterEHFrames(ImageBase, EHFrames);
|
|
ImageBase = 0;
|
|
return SectionMemoryManager::finalizeMemory(ErrMsg);
|
|
}
|
|
|
|
SingleSectionMemoryManager::SingleSectionMemoryManager() {
|
|
// Override Windows _CxxThrowException to call into our local version that
|
|
// can throw to and from the JIT.
|
|
sys::DynamicLibrary::AddSymbol(
|
|
"_CxxThrowException",
|
|
(void *)(uintptr_t)&SEHFrameHandler::RaiseSEHException);
|
|
}
|
|
|
|
// 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
|
|
SEHFrameHandler::RaiseSEHException(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");
|
|
(void)Frames;
|
|
|
|
const DWORD64 BaseAddr = sFrameHandler.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
|
|
|
|
// Not quite sure what this is about, but pThrowImageBase can never be 0
|
|
// here, as that is used to mark when an "ImageBase" was not found.
|
|
#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
|
|
}
|
|
|
|
#endif // CLANG_INTERPRETER_WIN_EXCEPTIONS
|
|
|
|
} // namespace interpreter
|