//==-- 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 #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> ImageRanges; typedef std::map 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::max(), std::numeric_limits::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(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(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(CxxExcept); EPtr--; WINRTEXCEPTIONINFO **ppWei = reinterpret_cast(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