| //===-------------- COFF.cpp - JIT linker function for COFF -------------===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| // | |
| // COFF jit-link function. | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "llvm/ExecutionEngine/JITLink/COFF.h" | |
| #include "llvm/BinaryFormat/COFF.h" | |
| #include "llvm/ExecutionEngine/JITLink/COFF_x86_64.h" | |
| #include "llvm/Object/COFF.h" | |
| #include <cstring> | |
| using namespace llvm; | |
| #define DEBUG_TYPE "jitlink" | |
| namespace llvm { | |
| namespace jitlink { | |
| static StringRef getMachineName(uint16_t Machine) { | |
| switch (Machine) { | |
| case COFF::IMAGE_FILE_MACHINE_I386: | |
| return "i386"; | |
| case COFF::IMAGE_FILE_MACHINE_AMD64: | |
| return "x86_64"; | |
| case COFF::IMAGE_FILE_MACHINE_ARMNT: | |
| return "ARM"; | |
| case COFF::IMAGE_FILE_MACHINE_ARM64: | |
| return "ARM64"; | |
| default: | |
| return "unknown"; | |
| } | |
| } | |
| Expected<std::unique_ptr<LinkGraph>> | |
| createLinkGraphFromCOFFObject(MemoryBufferRef ObjectBuffer, | |
| std::shared_ptr<orc::SymbolStringPool> SSP) { | |
| StringRef Data = ObjectBuffer.getBuffer(); | |
| // Check magic | |
| auto Magic = identify_magic(ObjectBuffer.getBuffer()); | |
| if (Magic != file_magic::coff_object) | |
| return make_error<JITLinkError>("Invalid COFF buffer"); | |
| if (Data.size() < sizeof(object::coff_file_header)) | |
| return make_error<JITLinkError>("Truncated COFF buffer"); | |
| uint64_t CurPtr = 0; | |
| bool IsPE = false; | |
| // Check if this is a PE/COFF file. | |
| if (Data.size() >= sizeof(object::dos_header) + sizeof(COFF::PEMagic)) { | |
| const auto *DH = | |
| reinterpret_cast<const object::dos_header *>(Data.data() + CurPtr); | |
| if (DH->Magic[0] == 'M' && DH->Magic[1] == 'Z') { | |
| // Check the PE magic bytes. ("PE\0\0") | |
| CurPtr = DH->AddressOfNewExeHeader; | |
| if (memcmp(Data.data() + CurPtr, COFF::PEMagic, sizeof(COFF::PEMagic)) != | |
| 0) { | |
| return make_error<JITLinkError>("Incorrect PE magic"); | |
| } | |
| CurPtr += sizeof(COFF::PEMagic); | |
| IsPE = true; | |
| } | |
| } | |
| if (Data.size() < CurPtr + sizeof(object::coff_file_header)) | |
| return make_error<JITLinkError>("Truncated COFF buffer"); | |
| const object::coff_file_header *COFFHeader = | |
| reinterpret_cast<const object::coff_file_header *>(Data.data() + CurPtr); | |
| const object::coff_bigobj_file_header *COFFBigObjHeader = nullptr; | |
| // Deal with bigobj file | |
| if (!IsPE && COFFHeader->Machine == COFF::IMAGE_FILE_MACHINE_UNKNOWN && | |
| COFFHeader->NumberOfSections == uint16_t(0xffff) && | |
| Data.size() >= sizeof(object::coff_bigobj_file_header)) { | |
| if (Data.size() < sizeof(object::coff_file_header)) { | |
| return make_error<JITLinkError>("Truncated COFF buffer"); | |
| } | |
| COFFBigObjHeader = | |
| reinterpret_cast<const object::coff_bigobj_file_header *>(Data.data() + | |
| CurPtr); | |
| // Verify that we are dealing with bigobj. | |
| if (COFFBigObjHeader->Version >= COFF::BigObjHeader::MinBigObjectVersion && | |
| std::memcmp(COFFBigObjHeader->UUID, COFF::BigObjMagic, | |
| sizeof(COFF::BigObjMagic)) == 0) { | |
| COFFHeader = nullptr; | |
| CurPtr += sizeof(object::coff_bigobj_file_header); | |
| } else | |
| COFFBigObjHeader = nullptr; | |
| } | |
| uint16_t Machine = | |
| COFFHeader ? COFFHeader->Machine : COFFBigObjHeader->Machine; | |
| LLVM_DEBUG({ | |
| dbgs() << "jitLink_COFF: PE = " << (IsPE ? "yes" : "no") | |
| << ", bigobj = " << (COFFBigObjHeader ? "yes" : "no") | |
| << ", identifier = \"" << ObjectBuffer.getBufferIdentifier() << "\" " | |
| << "machine = " << getMachineName(Machine) << "\n"; | |
| }); | |
| switch (Machine) { | |
| case COFF::IMAGE_FILE_MACHINE_AMD64: | |
| return createLinkGraphFromCOFFObject_x86_64(ObjectBuffer, std::move(SSP)); | |
| default: | |
| return make_error<JITLinkError>( | |
| "Unsupported target machine architecture in COFF object " + | |
| ObjectBuffer.getBufferIdentifier() + ": " + getMachineName(Machine)); | |
| } | |
| } | |
| void link_COFF(std::unique_ptr<LinkGraph> G, | |
| std::unique_ptr<JITLinkContext> Ctx) { | |
| switch (G->getTargetTriple().getArch()) { | |
| case Triple::x86_64: | |
| link_COFF_x86_64(std::move(G), std::move(Ctx)); | |
| return; | |
| default: | |
| Ctx->notifyFailed(make_error<JITLinkError>( | |
| "Unsupported target machine architecture in COFF link graph " + | |
| G->getName())); | |
| return; | |
| } | |
| } | |
| Symbol *GetImageBaseSymbol::operator()(LinkGraph &G) { | |
| if (ImageBase) | |
| return *ImageBase; | |
| auto IBN = G.intern(ImageBaseName); | |
| ImageBase = G.findExternalSymbolByName(IBN); | |
| if (*ImageBase) | |
| return *ImageBase; | |
| ImageBase = G.findAbsoluteSymbolByName(IBN); | |
| if (*ImageBase) | |
| return *ImageBase; | |
| ImageBase = G.findDefinedSymbolByName(IBN); | |
| if (*ImageBase) | |
| return *ImageBase; | |
| return nullptr; | |
| } | |
| } // end namespace jitlink | |
| } // end namespace llvm |