diff options
Diffstat (limited to 'tools/driver/EmbeddedRunner.cc')
| -rw-r--r-- | tools/driver/EmbeddedRunner.cc | 335 |
1 files changed, 4 insertions, 331 deletions
diff --git a/tools/driver/EmbeddedRunner.cc b/tools/driver/EmbeddedRunner.cc index 49375e1..587d664 100644 --- a/tools/driver/EmbeddedRunner.cc +++ b/tools/driver/EmbeddedRunner.cc @@ -1,11 +1,11 @@ #include "EmbeddedRunner.h" +#include "MinGWJIT.h" #include "NativeCompiler.h" #include "llvm/Object/ObjectFile.h" #include "cps/CodeGen.h" #include "llvm/ExecutionEngine/Orc/AbsoluteSymbols.h" #include "llvm/ExecutionEngine/Orc/ExecutionUtils.h" #include "llvm/ExecutionEngine/Orc/LLJIT.h" -#include "llvm/ExecutionEngine/Orc/ObjectFileInterface.h" #include "llvm/ExecutionEngine/Orc/ObjectLinkingLayer.h" #include "llvm/ExecutionEngine/Orc/ThreadSafeModule.h" #include "llvm/Support/Error.h" @@ -17,323 +17,12 @@ #include <cxxabi.h> #include <typeinfo> #include <unwind.h> -#if defined(_WIN32) && defined(__x86_64__) -#ifndef NOMINMAX -#define NOMINMAX -#endif -#include <algorithm> -#include <cstring> -#include <limits> -#include "llvm/ExecutionEngine/JITLink/JITLink.h" -#include "llvm/ExecutionEngine/JITLink/x86_64.h" -#include "llvm/ExecutionEngine/Orc/LinkGraphLinkingLayer.h" -#include "llvm/ExecutionEngine/Orc/Shared/WrapperFunctionUtils.h" -#include "llvm/Support/Endian.h" -#include "MinGWCOFF.h" -// Windows IMAGE_* macros conflict with LLVM COFF enums and inline helpers. -// Parse all LLVM COFF declarations and helpers before introducing those macros. -#include <windows.h> -#endif #ifdef __MINGW32__ #include <io.h> #endif namespace { -#if defined(_WIN32) && defined(__x86_64__) -// In-process COFF/SEH support for the MinGW x86_64 JIT. -// -// The full upstream COFFPlatform starts the VC runtime loading machinery, -// which does not apply to the current MinGW build. This plugin provides only -// the linking and registration pieces needed for in-process execution: -// a per-graph __ImageBase shared by relocations and SEH registration, -// JITLink stubs for external direct branches and GNU personalities in .xdata, -// and RtlAddFunctionTable/RtlDeleteFunctionTable via JITLink allocation -// actions. Exception propagation still uses the existing C++ ABI, LLVM -// exception instructions and platform unwinder. - -static constexpr llvm::StringLiteral COFFSEHTableSection = "$__cpsc_seh"; - -static llvm::Error addCOFFJumpStubs(llvm::jitlink::LinkGraph &G) { - using namespace llvm; - using namespace llvm::jitlink; - using namespace llvm::jitlink::x86_64; - GOTTableManager GOT(G); - PLTTableManager PLT(G, GOT); - // Creating a stub may add sections/blocks. Visit only the original blocks. - std::vector<Block *> Blocks(G.blocks().begin(), G.blocks().end()); - for (auto *B : Blocks) { - bool Executable = (B->getSection().getMemProt() & orc::MemProt::Exec) != - orc::MemProt::None; - for (auto &E : B->edges()) { - Symbol &Target = E.getTarget(); - if (!Target.isExternal()) - continue; - bool Personality = false; - if (isCOFFUnwindSection(B->getSection().getName(), ".xdata") && - Target.hasName()) { - StringRef Name = *Target.getName(); - Personality = Name == "__gxx_personality_seh0" || - Name == "__gxx_personality_sj0" || - Name == "__gxx_personality_v0"; - } - // COFF's private PCRel32 kind is lowered only in PreFixup. - // Use its name rather than confusing it with x86_64::PCRel32. - // Only E8/E9 direct branches with a plain symbol target need a - // jump stub; RIP-relative data and indirect calls must stay intact. - bool DirectBranch = false; - if (Executable && !B->isZeroFill() && E.getAddend() == 0 && - StringRef(G.getEdgeKindName(E.getKind())) == "PCRel32") { - auto Content = B->getContent(); - size_t Offset = E.getOffset(); - if (Offset > 0 && Offset <= Content.size() && - Content.size() - Offset >= 4) { - unsigned char Opcode = Content[Offset - 1]; - DirectBranch = Opcode == 0xe8 || Opcode == 0xe9; - } - } - if (Personality || DirectBranch) - E.setTarget(PLT.getEntryForTarget(G, Target)); - } - } - return Error::success(); -} - -static llvm::Error registerSEHTable(llvm::orc::ExecutorAddrRange TableRange, - llvm::orc::ExecutorAddr ImageBase) { - using llvm::make_error; - using llvm::StringError; - using llvm::inconvertibleErrorCode; - size_t Size = TableRange.size(); - if (Size == 0 || ImageBase.isNull()) - return make_error<StringError>("invalid embedded SEH function table", - inconvertibleErrorCode()); - if (Size % sizeof(RUNTIME_FUNCTION)) - return make_error<StringError>("invalid embedded SEH function table", - inconvertibleErrorCode()); - uint64_t Count64 = Size / sizeof(RUNTIME_FUNCTION); - if (Count64 == 0 || Count64 > static_cast<uint64_t>(std::numeric_limits<DWORD>::max())) - return make_error<StringError>("invalid embedded SEH function table", - inconvertibleErrorCode()); - auto *Table = TableRange.Start.toPtr<const RUNTIME_FUNCTION *>(); - DWORD PrevBegin = 0; - for (uint64_t I = 0; I < Count64; ++I) { - DWORD Begin = llvm::support::endian::read32le( - reinterpret_cast<const char *>(Table + I) + 0); - DWORD End = llvm::support::endian::read32le( - reinterpret_cast<const char *>(Table + I) + 4); - if (!(Begin < End)) - return make_error<StringError>("invalid embedded SEH function table", - inconvertibleErrorCode()); - if (I > 0 && !(PrevBegin < Begin)) - return make_error<StringError>("invalid embedded SEH function table", - inconvertibleErrorCode()); - PrevBegin = Begin; - } - auto *MutableTable = const_cast<PRUNTIME_FUNCTION>(Table); - DWORD Count = static_cast<DWORD>(Count64); - DWORD64 Base = static_cast<DWORD64>(ImageBase.getValue()); - if (!RtlAddFunctionTable(MutableTable, Count, Base)) - return make_error<StringError>("cannot register embedded SEH function table", - inconvertibleErrorCode()); - return llvm::Error::success(); -} - -static llvm::Error deregisterSEHTable(llvm::orc::ExecutorAddrRange TableRange, - llvm::orc::ExecutorAddr ImageBase) { - (void)ImageBase; - auto *Table = TableRange.Start.toPtr<PRUNTIME_FUNCTION>(); - if (TableRange.size() == 0 || !Table) - return llvm::Error::success(); - RtlDeleteFunctionTable(Table); - return llvm::Error::success(); -} - -extern "C" llvm::orc::shared::CWrapperFunctionBuffer -cps_register_seh_table_alloc_action(const char *ArgData, size_t ArgSize) { - using namespace llvm::orc::shared; - return WrapperFunction<SPSError(SPSExecutorAddrRange, SPSExecutorAddr)>::handle( - ArgData, ArgSize, registerSEHTable) - .release(); -} - -extern "C" llvm::orc::shared::CWrapperFunctionBuffer -cps_deregister_seh_table_alloc_action(const char *ArgData, size_t ArgSize) { - using namespace llvm::orc::shared; - return WrapperFunction<SPSError(SPSExecutorAddrRange, SPSExecutorAddr)>::handle( - ArgData, ArgSize, deregisterSEHTable) - .release(); -} - -class MinGWSEHRegistrationPlugin final - : public llvm::orc::LinkGraphLinkingLayer::Plugin { -public: - void modifyPassConfig(llvm::orc::MaterializationResponsibility &MR, - llvm::jitlink::LinkGraph &G, - llvm::jitlink::PassConfiguration &Config) override { - using namespace llvm; - using namespace llvm::orc; - using namespace llvm::jitlink; - const Triple &TT = G.getTargetTriple(); - if (!TT.isOSBinFormatCOFF() || TT.getArch() != Triple::x86_64) - return; - - // SEH table liveness relies on the COFF backend's default - // SEHFrameKeepAlivePass(".pdata"), which keeps .pdata alive from its - // referenced function/unwind blocks. Normalized .pdata$/.xdata$ - // sections are kept alive by their associative COMDAT code edges. - // COFF unwind tables address the personality by 32-bit RVA, so it - // must live near JIT code. Retarget .xdata personality references - // to this graph's anonymous pointer jump stubs; the stub pointer - // itself uses a 64-bit relocation to reach the far host runtime. - // External direct calls/jumps from user objects also need nearby - // stubs: their PCRel32 displacement cannot reach a distant CRT DLL. - // The same target reuses the same stub, including personality uses. - Config.PostPrunePasses.push_back(addCOFFJumpStubs); - - // Tables sharing an image base have overlapping lookup ranges in the - // Windows unwinder. Reserve one table for all retained .pdata sections - // in this graph, then fill and sort its entries after relocations. - Config.PostPrunePasses.push_back([](LinkGraph &G) -> Error { - size_t Size = 0; - for (auto &Sec : G.sections()) { - if (!isCOFFUnwindSection(Sec.getName(), ".pdata")) - continue; - for (auto *B : Sec.blocks()) { - if (B->isZeroFill() || B->getSize() % sizeof(RUNTIME_FUNCTION)) - return make_error<JITLinkError>( - "invalid embedded SEH function table"); - Size += B->getSize(); - } - } - if (Size) { - auto &Sec = G.createSection(COFFSEHTableSection, MemProt::Read); - G.createMutableContentBlock(Sec, Size, ExecutorAddr(), - alignof(RUNTIME_FUNCTION), 0); - } - return Error::success(); - }); - - // COFF unwind tables use 32-bit image-relative addresses, so the - // image base must sit at or below the lowest JIT address. Alias the - // external __ImageBase to the lowest defined block once addresses - // are final; a trailing zero block would push RVAs negative. - Config.PostAllocationPasses.push_back([](LinkGraph &G) -> Error { - // A graph-local alias never collides at materialization time, - // no matter how many graphs define the same base name. - if (G.findDefinedSymbolByName(G.intern("__ImageBase"))) - return Error::success(); - auto *ImageBaseExt = - G.findExternalSymbolByName(G.intern("__ImageBase")); - if (!ImageBaseExt) - return Error::success(); - jitlink::Block *Low = nullptr; - for (auto &Sec : G.sections()) { - for (auto *B : Sec.blocks()) { - if (!Low || B->getAddress() < Low->getAddress()) - Low = B; - } - } - if (!Low) - return make_error<JITLinkError>( - "missing embedded __ImageBase content"); - G.makeDefined(*ImageBaseExt, *Low, 0, 8, Linkage::Strong, - Scope::Local, true); - return Error::success(); - }); - - // Copy fixed-up entries into one sorted, allocation-owned table. - // Register it once per graph, with deregistration on link failure or - // teardown before the table and its code are released. - Config.PostFixupPasses.push_back( - [](LinkGraph &G) -> Error { - using namespace llvm::orc::shared; - auto *TableSec = G.findSectionByName(COFFSEHTableSection); - if (!TableSec) - return Error::success(); - auto *Table = *TableSec->blocks().begin(); - std::vector<RUNTIME_FUNCTION> Entries; - Entries.reserve(Table->getSize() / sizeof(RUNTIME_FUNCTION)); - for (auto &Sec : G.sections()) { - if (!isCOFFUnwindSection(Sec.getName(), ".pdata")) - continue; - for (auto *B : Sec.blocks()) { - size_t Start = Entries.size(); - Entries.resize(Start + B->getSize() / sizeof(RUNTIME_FUNCTION)); - if (B->getSize()) - std::memcpy(Entries.data() + Start, B->getContent().data(), - B->getSize()); - } - } - std::sort(Entries.begin(), Entries.end(), - [](const RUNTIME_FUNCTION &L, const RUNTIME_FUNCTION &R) { - return L.BeginAddress < R.BeginAddress; - }); - std::memcpy(Table->getAlreadyMutableContent().data(), Entries.data(), - Table->getSize()); - ExecutorAddrRange TableRange(Table->getAddress(), Table->getSize()); - auto *ImageBaseSym = - G.findDefinedSymbolByName(G.intern("__ImageBase")); - if (!ImageBaseSym) - return make_error<JITLinkError>( - "missing embedded __ImageBase for SEH registration"); - ExecutorAddr ImageBase = ImageBaseSym->getAddress(); - if (ImageBase.isNull()) - return make_error<JITLinkError>("invalid embedded SEH image base"); - auto RegisterAction = - WrapperFunctionCall::Create<SPSArgList<SPSExecutorAddrRange, - SPSExecutorAddr>>( - ExecutorAddr::fromPtr(&cps_register_seh_table_alloc_action), - TableRange, ImageBase); - if (!RegisterAction) - return RegisterAction.takeError(); - auto DeregisterAction = - WrapperFunctionCall::Create<SPSArgList<SPSExecutorAddrRange, - SPSExecutorAddr>>( - ExecutorAddr::fromPtr(&cps_deregister_seh_table_alloc_action), - TableRange, ImageBase); - if (!DeregisterAction) - return DeregisterAction.takeError(); - G.allocActions().push_back( - {std::move(*RegisterAction), std::move(*DeregisterAction)}); - return Error::success(); - }); - (void)MR; - } - - llvm::Error notifyFailed(llvm::orc::MaterializationResponsibility &) override { - return llvm::Error::success(); - } - - llvm::Error notifyRemovingResources(llvm::orc::JITDylib &, - llvm::orc::ResourceKey) override { - return llvm::Error::success(); - } - - void notifyTransferringResources(llvm::orc::JITDylib &, - llvm::orc::ResourceKey, - llvm::orc::ResourceKey) override {} -}; - -// Both user objects and lazily extracted runtime archive members pass through -// this layer, before JITLink builds their graphs or runs plugin passes. -class MinGWObjectLinkingLayer final : public llvm::orc::ObjectLinkingLayer { -public: - using ObjectLinkingLayer::ObjectLinkingLayer; - - void emit(std::unique_ptr<llvm::orc::MaterializationResponsibility> R, - std::unique_ptr<llvm::MemoryBuffer> O) override { - auto Normalized = normalizeMinGWObject(std::move(O)); - if (!Normalized) { - getExecutionSession().reportError(Normalized.takeError()); - R->failMaterialization(); - return; - } - ObjectLinkingLayer::emit(std::move(R), std::move(*Normalized)); - } -}; -#endif - // Embedded JITLink object layer. Created before LLJIT takes ownership so // runEmbedded keeps only the execution flow. COFF flags stay conditional on // the object format; other formats use LLVM defaults. @@ -341,21 +30,15 @@ static std::unique_ptr<llvm::orc::ObjectLinkingLayer> createEmbeddedObjectLayer(llvm::orc::ExecutionSession &ES, llvm::jitlink::JITLinkMemoryManager &MM, const llvm::Triple &TT) { - std::unique_ptr<llvm::orc::ObjectLinkingLayer> Layer; #if defined(_WIN32) && defined(__x86_64__) if (TT.isOSBinFormatCOFF() && TT.getArch() == llvm::Triple::x86_64) - Layer = std::make_unique<MinGWObjectLinkingLayer>(ES, MM); - else + return createMinGWObjectLinkingLayer(ES, MM); #endif - Layer = std::make_unique<llvm::orc::ObjectLinkingLayer>(ES, MM); + auto Layer = std::make_unique<llvm::orc::ObjectLinkingLayer>(ES, MM); if (TT.isOSBinFormatCOFF()) { Layer->setOverrideObjectFlagsWithResponsibilityFlags(true); Layer->setAutoClaimResponsibilityForObjectSymbols(true); } -#if defined(_WIN32) && defined(__x86_64__) - if (TT.isOSBinFormatCOFF() && TT.getArch() == llvm::Triple::x86_64) - Layer->addPlugin(std::make_shared<MinGWSEHRegistrationPlugin>()); -#endif return Layer; } @@ -459,8 +142,6 @@ static llvm::Error defineRuntimeSymbols(llvm::orc::LLJIT &J) { // msvcrt DLL only exports __iob_func, so bind the host's shim directly. Symbols[Intern("__acrt_iob_func")] = ExecutorSymbolDef::fromPtr(&__acrt_iob_func); -#endif -#ifdef _WIN32 // The JIT-generated main references __main on MinGW; provide a no-op so // the host CRT is not initialized a second time. Symbols[Intern("__main")] = @@ -481,15 +162,7 @@ static llvm::Error loadCxxRuntime(llvm::orc::LLJIT &J) { for (auto &Archive : Archives) { llvm::orc::StaticLibraryDefinitionGenerator::GetObjectFileInterface GetInterface; #if defined(_WIN32) && defined(__x86_64__) - // Match the normalized object that MinGWObjectLinkingLayer::emit links. - GetInterface = [](llvm::orc::ExecutionSession &ES, llvm::MemoryBufferRef Ref) - -> llvm::Expected<llvm::orc::MaterializationUnit::Interface> { - auto Normalized = normalizeMinGWObject( - llvm::MemoryBuffer::getMemBuffer(Ref, false)); - if (!Normalized) - return Normalized.takeError(); - return llvm::orc::getObjectFileInterface(ES, (*Normalized)->getMemBufferRef()); - }; + GetInterface = getMinGWObjectFileInterface; #endif auto Generator = llvm::orc::StaticLibraryDefinitionGenerator::Create( J.getObjLinkingLayer(), std::move(Archive), |
