ExecutionEngine: Preliminary support for dynamically loadable coff objects

Provide basic support for dynamically loadable coff objects. Only handles a subset of x64 currently.

Patch by Andy Ayers!

Differential Revision: http://reviews.llvm.org/D7793

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@231574 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
David Majnemer 2015-03-07 20:21:27 +00:00
parent ed0266d8ee
commit 2c08439cf9
10 changed files with 475 additions and 14 deletions

View File

@ -2,6 +2,8 @@ add_llvm_library(LLVMRuntimeDyld
RTDyldMemoryManager.cpp
RuntimeDyld.cpp
RuntimeDyldChecker.cpp
RuntimeDyldCOFF.cpp
RuntimeDyldELF.cpp
RuntimeDyldMachO.cpp
Targets/RuntimeDyldCOFFX86_64.cpp
)

View File

@ -13,10 +13,12 @@
#include "llvm/ExecutionEngine/RuntimeDyld.h"
#include "RuntimeDyldCheckerImpl.h"
#include "RuntimeDyldCOFF.h"
#include "RuntimeDyldELF.h"
#include "RuntimeDyldImpl.h"
#include "RuntimeDyldMachO.h"
#include "llvm/Object/ELFObjectFile.h"
#include "llvm/Object/COFF.h"
#include "llvm/Support/MathExtras.h"
#include "llvm/Support/MutexGuard.h"
@ -264,6 +266,20 @@ static bool isRequiredForExecution(const SectionRef &Section) {
const ObjectFile *Obj = Section.getObject();
if (auto *ELFObj = dyn_cast<object::ELFObjectFileBase>(Obj))
return ELFObj->getSectionFlags(Section) & ELF::SHF_ALLOC;
if (auto *COFFObj = dyn_cast<object::COFFObjectFile>(Obj)) {
const coff_section *CoffSection = COFFObj->getCOFFSection(Section);
// Avoid loading zero-sized COFF sections.
// In PE files, VirtualSize gives the section size, and SizeOfRawData
// may be zero for sections with content. In Obj files, SizeOfRawData
// gives the section size, and VirtualSize is always zero. Hence
// the need to check for both cases below.
bool HasContent = (CoffSection->VirtualSize > 0)
|| (CoffSection->SizeOfRawData > 0);
bool IsDiscardable = CoffSection->Characteristics &
(COFF::IMAGE_SCN_MEM_DISCARDABLE | COFF::IMAGE_SCN_LNK_INFO);
return HasContent && !IsDiscardable;
}
assert(isa<MachOObjectFile>(Obj));
return true;
}
@ -273,6 +289,15 @@ static bool isReadOnlyData(const SectionRef &Section) {
if (auto *ELFObj = dyn_cast<object::ELFObjectFileBase>(Obj))
return !(ELFObj->getSectionFlags(Section) &
(ELF::SHF_WRITE | ELF::SHF_EXECINSTR));
if (auto *COFFObj = dyn_cast<object::COFFObjectFile>(Obj))
return ((COFFObj->getCOFFSection(Section)->Characteristics &
(COFF::IMAGE_SCN_CNT_INITIALIZED_DATA
| COFF::IMAGE_SCN_MEM_READ
| COFF::IMAGE_SCN_MEM_WRITE))
==
(COFF::IMAGE_SCN_CNT_INITIALIZED_DATA
| COFF::IMAGE_SCN_MEM_READ));
assert(isa<MachOObjectFile>(Obj));
return false;
}
@ -281,6 +306,9 @@ static bool isZeroInit(const SectionRef &Section) {
const ObjectFile *Obj = Section.getObject();
if (auto *ELFObj = dyn_cast<object::ELFObjectFileBase>(Obj))
return ELFObj->getSectionType(Section) == ELF::SHT_NOBITS;
if (auto *COFFObj = dyn_cast<object::COFFObjectFile>(Obj))
return COFFObj->getCOFFSection(Section)->Characteristics &
COFF::IMAGE_SCN_CNT_UNINITIALIZED_DATA;
auto *MachO = cast<MachOObjectFile>(Obj);
unsigned SectionType = MachO->getSectionType(Section);
@ -512,7 +540,6 @@ unsigned RuntimeDyldImpl::emitSection(const ObjectFile &Obj,
const SectionRef &Section, bool IsCode) {
StringRef data;
Check(Section.getContents(data));
uint64_t Alignment64 = Section.getAlignment();
unsigned Alignment = (unsigned)Alignment64 & 0xffffffffL;
@ -542,6 +569,7 @@ unsigned RuntimeDyldImpl::emitSection(const ObjectFile &Obj,
// Some sections, such as debug info, don't need to be loaded for execution.
// Leave those where they are.
if (IsRequired) {
Check(Section.getContents(data));
Allocate = DataSize + PaddingSize + StubBufSize;
Addr = IsCode ? MemMgr->allocateCodeSection(Allocate, Alignment, SectionID,
Name)
@ -816,6 +844,15 @@ RuntimeDyld::RuntimeDyld(RTDyldMemoryManager *mm) {
RuntimeDyld::~RuntimeDyld() {}
static std::unique_ptr<RuntimeDyldCOFF>
createRuntimeDyldCOFF(Triple::ArchType Arch, RTDyldMemoryManager *MM,
bool ProcessAllSections, RuntimeDyldCheckerImpl *Checker) {
std::unique_ptr<RuntimeDyldCOFF> Dyld(RuntimeDyldCOFF::create(Arch, MM));
Dyld->setProcessAllSections(ProcessAllSections);
Dyld->setRuntimeDyldChecker(Checker);
return Dyld;
}
static std::unique_ptr<RuntimeDyldELF>
createRuntimeDyldELF(RTDyldMemoryManager *MM, bool ProcessAllSections,
RuntimeDyldCheckerImpl *Checker) {
@ -843,6 +880,10 @@ RuntimeDyld::loadObject(const ObjectFile &Obj) {
Dyld = createRuntimeDyldMachO(
static_cast<Triple::ArchType>(Obj.getArch()), MM,
ProcessAllSections, Checker);
else if (Obj.isCOFF())
Dyld = createRuntimeDyldCOFF(
static_cast<Triple::ArchType>(Obj.getArch()), MM,
ProcessAllSections, Checker);
else
report_fatal_error("Incompatible object format!");
}

View File

@ -0,0 +1,85 @@
//===-- RuntimeDyldCOFF.cpp - Run-time dynamic linker for MC-JIT -*- C++ -*-==//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// Implementation of COFF support for the MC-JIT runtime dynamic linker.
//
//===----------------------------------------------------------------------===//
#include "RuntimeDyldCOFF.h"
#include "Targets/RuntimeDyldCOFFX86_64.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/Triple.h"
#include "llvm/Object/ObjectFile.h"
using namespace llvm;
using namespace llvm::object;
#define DEBUG_TYPE "dyld"
namespace {
class LoadedCOFFObjectInfo : public RuntimeDyld::LoadedObjectInfo {
public:
LoadedCOFFObjectInfo(RuntimeDyldImpl &RTDyld, unsigned BeginIdx,
unsigned EndIdx)
: RuntimeDyld::LoadedObjectInfo(RTDyld, BeginIdx, EndIdx) {}
OwningBinary<ObjectFile>
getObjectForDebug(const ObjectFile &Obj) const override {
return OwningBinary<ObjectFile>();
}
};
}
namespace llvm {
std::unique_ptr<RuntimeDyldCOFF>
llvm::RuntimeDyldCOFF::create(Triple::ArchType Arch, RTDyldMemoryManager *MM) {
switch (Arch) {
default:
llvm_unreachable("Unsupported target for RuntimeDyldCOFF.");
break;
case Triple::x86_64:
return make_unique<RuntimeDyldCOFFX86_64>(MM);
}
}
std::unique_ptr<RuntimeDyld::LoadedObjectInfo>
RuntimeDyldCOFF::loadObject(const object::ObjectFile &O) {
unsigned SectionStartIdx, SectionEndIdx;
std::tie(SectionStartIdx, SectionEndIdx) = loadObjectImpl(O);
return llvm::make_unique<LoadedCOFFObjectInfo>(*this, SectionStartIdx,
SectionEndIdx);
}
uint64_t RuntimeDyldCOFF::getSymbolOffset(const SymbolRef &Sym) {
uint64_t Address;
if (std::error_code EC = Sym.getAddress(Address))
return UnknownAddressOrSize;
if (Address == UnknownAddressOrSize)
return UnknownAddressOrSize;
const ObjectFile *Obj = Sym.getObject();
section_iterator SecI(Obj->section_end());
if (std::error_code EC = Sym.getSection(SecI))
return UnknownAddressOrSize;
if (SecI == Obj->section_end())
return UnknownAddressOrSize;
uint64_t SectionAddress = SecI->getAddress();
return Address - SectionAddress;
}
bool RuntimeDyldCOFF::isCompatibleFile(const object::ObjectFile &Obj) const {
return Obj.isCOFF();
}
} // namespace llvm

View File

@ -0,0 +1,46 @@
//===-- RuntimeDyldCOFF.h - Run-time dynamic linker for MC-JIT ---*- C++ -*-==//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// COFF support for MC-JIT runtime dynamic linker.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_RUNTIME_DYLD_COFF_H
#define LLVM_RUNTIME_DYLD_COFF_H
#include "RuntimeDyldImpl.h"
#include "llvm/ADT/DenseMap.h"
#define DEBUG_TYPE "dyld"
using namespace llvm;
namespace llvm {
// Common base class for COFF dynamic linker support.
// Concrete subclasses for each target can be found in ./Targets.
class RuntimeDyldCOFF : public RuntimeDyldImpl {
public:
std::unique_ptr<RuntimeDyld::LoadedObjectInfo>
loadObject(const object::ObjectFile &Obj) override;
bool isCompatibleFile(const object::ObjectFile &Obj) const override;
static std::unique_ptr<RuntimeDyldCOFF> create(Triple::ArchType Arch,
RTDyldMemoryManager *MM);
protected:
RuntimeDyldCOFF(RTDyldMemoryManager *MM) : RuntimeDyldImpl(MM) {}
uint64_t getSymbolOffset(const SymbolRef &Sym);
};
} // end namespace llvm
#undef DEBUG_TYPE
#endif

View File

@ -20,16 +20,6 @@
using namespace llvm;
namespace llvm {
namespace {
// Helper for extensive error checking in debug builds.
std::error_code Check(std::error_code Err) {
if (Err) {
report_fatal_error(Err.message());
}
return Err;
}
} // end anonymous namespace
class RuntimeDyldELF : public RuntimeDyldImpl {

View File

@ -36,6 +36,16 @@ using namespace llvm::object;
namespace llvm {
namespace {
// Helper for extensive error checking in debug builds.
std::error_code Check(std::error_code Err) {
if (Err) {
report_fatal_error(Err.message());
}
return Err;
}
} // end anonymous namespace
class Twine;
/// SectionEntry - represents a section emitted into memory by the dynamic

View File

@ -0,0 +1,189 @@
//===-- RuntimeDyldCOFFX86_64.cpp - COFF/X86_64 specific code ---*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// COFF x86_x64 support for MC-JIT runtime dynamic linker.
//
//===----------------------------------------------------------------------===//
#include "RuntimeDyldCOFFX86_64.h"
#define DEBUG_TYPE "dyld"
namespace llvm {
void RuntimeDyldCOFFX86_64::registerEHFrames() {
if (!MemMgr)
return;
for (auto const &EHFrameSID : UnregisteredEHFrameSections) {
uint8_t *EHFrameAddr = Sections[EHFrameSID].Address;
uint64_t EHFrameLoadAddr = Sections[EHFrameSID].LoadAddress;
size_t EHFrameSize = Sections[EHFrameSID].Size;
MemMgr->registerEHFrames(EHFrameAddr, EHFrameLoadAddr, EHFrameSize);
RegisteredEHFrameSections.push_back(EHFrameSID);
}
UnregisteredEHFrameSections.clear();
}
void RuntimeDyldCOFFX86_64::deregisterEHFrames() {
// Stub
}
// The target location for the relocation is described by RE.SectionID and
// RE.Offset. RE.SectionID can be used to find the SectionEntry. Each
// SectionEntry has three members describing its location.
// SectionEntry::Address is the address at which the section has been loaded
// into memory in the current (host) process. SectionEntry::LoadAddress is the
// address that the section will have in the target process.
// SectionEntry::ObjAddress is the address of the bits for this section in the
// original emitted object image (also in the current address space).
//
// Relocations will be applied as if the section were loaded at
// SectionEntry::LoadAddress, but they will be applied at an address based
// on SectionEntry::Address. SectionEntry::ObjAddress will be used to refer to
// Target memory contents if they are required for value calculations.
//
// The Value parameter here is the load address of the symbol for the
// relocation to be applied. For relocations which refer to symbols in the
// current object Value will be the LoadAddress of the section in which
// the symbol resides (RE.Addend provides additional information about the
// symbol location). For external symbols, Value will be the address of the
// symbol in the target address space.
void RuntimeDyldCOFFX86_64::resolveRelocation(const RelocationEntry &RE,
uint64_t Value) {
const SectionEntry &Section = Sections[RE.SectionID];
uint8_t *Target = Section.Address + RE.Offset;
switch (RE.RelType) {
case COFF::IMAGE_REL_AMD64_REL32:
case COFF::IMAGE_REL_AMD64_REL32_1:
case COFF::IMAGE_REL_AMD64_REL32_2:
case COFF::IMAGE_REL_AMD64_REL32_3:
case COFF::IMAGE_REL_AMD64_REL32_4:
case COFF::IMAGE_REL_AMD64_REL32_5: {
uint32_t *TargetAddress = (uint32_t *)Target;
uint64_t FinalAddress = Section.LoadAddress + RE.Offset;
// Delta is the distance from the start of the reloc to the end of the
// instruction with the reloc.
uint64_t Delta = 4 + (RE.RelType - COFF::IMAGE_REL_AMD64_REL32);
Value -= FinalAddress + Delta;
uint64_t Result = Value + RE.Addend;
assert(((int64_t)Result <= INT32_MAX) && "Relocation overflow");
assert(((int64_t)Result >= INT32_MIN) && "Relocation underflow");
*TargetAddress = Result;
break;
}
case COFF::IMAGE_REL_AMD64_ADDR32NB: {
// Note ADDR32NB requires a well-established notion of
// image base. This address must be less than or equal
// to every section's load address, and all sections must be
// within a 32 bit offset from the base.
//
// For now we just set these to zero.
uint32_t *TargetAddress = (uint32_t *)Target;
*TargetAddress = 0;
break;
}
case COFF::IMAGE_REL_AMD64_ADDR64: {
uint64_t *TargetAddress = (uint64_t *)Target;
*TargetAddress = Value + RE.Addend;
break;
}
default:
llvm_unreachable("Relocation type not implemented yet!");
break;
}
}
relocation_iterator RuntimeDyldCOFFX86_64::processRelocationRef(
unsigned SectionID, relocation_iterator RelI, const ObjectFile &Obj,
ObjSectionToIDMap &ObjSectionToID, StubMap &Stubs) {
// Find the symbol referred to in the relocation, and
// get its section and offset.
//
// Insist for now that all symbols be resolvable within
// the scope of this object file.
symbol_iterator Symbol = RelI->getSymbol();
if (Symbol == Obj.symbol_end())
report_fatal_error("Unknown symbol in relocation");
unsigned TargetSectionID = 0;
uint64_t TargetOffset = UnknownAddressOrSize;
const COFFObjectFile *COFFObj = cast<COFFObjectFile>(&Obj);
section_iterator SecI(Obj.section_end());
Symbol->getSection(SecI);
if (SecI == Obj.section_end())
report_fatal_error("Unknown section in relocation");
bool IsCode = SecI->isText();
TargetSectionID = findOrEmitSection(Obj, *SecI, IsCode, ObjSectionToID);
TargetOffset = getSymbolOffset(*Symbol);
// Determine the Addend used to adjust the relocation value.
uint64_t RelType;
Check(RelI->getType(RelType));
uint64_t Offset;
Check(RelI->getOffset(Offset));
uint64_t Addend = 0;
SectionEntry &Section = Sections[SectionID];
uintptr_t ObjTarget = Section.ObjAddress + Offset;
switch (RelType) {
case COFF::IMAGE_REL_AMD64_REL32:
case COFF::IMAGE_REL_AMD64_REL32_1:
case COFF::IMAGE_REL_AMD64_REL32_2:
case COFF::IMAGE_REL_AMD64_REL32_3:
case COFF::IMAGE_REL_AMD64_REL32_4:
case COFF::IMAGE_REL_AMD64_REL32_5:
case COFF::IMAGE_REL_AMD64_ADDR32NB: {
uint32_t *Displacement = (uint32_t *)ObjTarget;
Addend = *Displacement;
break;
}
case COFF::IMAGE_REL_AMD64_ADDR64: {
uint64_t *Displacement = (uint64_t *)ObjTarget;
Addend = *Displacement;
break;
}
default:
break;
}
StringRef TargetName;
Symbol->getName(TargetName);
DEBUG(dbgs() << "\t\tIn Section " << SectionID << " Offset " << Offset
<< " RelType: " << RelType << " TargetName: " << TargetName
<< " Addend " << Addend << "\n");
RelocationEntry RE(SectionID, Offset, RelType, TargetOffset + Addend);
addRelocationForSection(RE, TargetSectionID);
return ++RelI;
}
void RuntimeDyldCOFFX86_64::finalizeLoad(const ObjectFile &Obj,
ObjSectionToIDMap &SectionMap) {
// Look for and record the EH frame section IDs.
for (const auto &SectionPair : SectionMap) {
const SectionRef &Section = SectionPair.first;
StringRef Name;
Check(Section.getName(Name));
// Note unwind info is split across .pdata and .xdata, so this
// may not be sufficiently general for all users.
if (Name == ".xdata") {
UnregisteredEHFrameSections.push_back(SectionPair.second);
}
}
}
}

View File

@ -0,0 +1,60 @@
//===-- RuntimeDyldCOFFX86_64.h --- COFF/X86_64 specific code ---*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// COFF x86_x64 support for MC-JIT runtime dynamic linker.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_LIB_EXECUTIONENGINE_RUNTIMEDYLD_TARGETS_RUNTIMEDYLDCOFF86_64_H
#define LLVM_LIB_EXECUTIONENGINE_RUNTIMEDYLD_TARGETS_RUNTIMEDYLDCOFF86_64_H
#include "llvm/Object/COFF.h"
#include "llvm/Support/COFF.h"
#include "../RuntimeDyldCOFF.h"
using namespace llvm;
namespace llvm {
class RuntimeDyldCOFFX86_64 : public RuntimeDyldCOFF {
private:
// When a module is loaded we save the SectionID of the unwind
// sections in a table until we receive a request to register all
// unregisteredEH frame sections with the memory manager.
SmallVector<SID, 2> UnregisteredEHFrameSections;
SmallVector<SID, 2> RegisteredEHFrameSections;
public:
RuntimeDyldCOFFX86_64(RTDyldMemoryManager *MM) : RuntimeDyldCOFF(MM) {}
unsigned getMaxStubSize() override {
return 6; // 2-byte jmp instruction + 32-bit relative address
}
void resolveRelocation(const RelocationEntry &RE, uint64_t Value) override;
relocation_iterator processRelocationRef(unsigned SectionID,
relocation_iterator RelI,
const ObjectFile &Obj,
ObjSectionToIDMap &ObjSectionToID,
StubMap &Stubs) override;
unsigned getStubAlignment() override { return 1; }
void registerEHFrames() override;
void deregisterEHFrames() override;
void finalizeLoad(const ObjectFile &Obj,
ObjSectionToIDMap &SectionMap) override;
};
} // end namespace llvm
#undef DEBUG_TYPE
#endif

View File

@ -190,7 +190,9 @@ std::error_code COFFObjectFile::getSymbolType(DataRefImpl Ref,
Result = SymbolRef::ST_Data;
} else if (Symb.isFileRecord()) {
Result = SymbolRef::ST_File;
} else if (SectionNumber == COFF::IMAGE_SYM_DEBUG) {
} else if (SectionNumber == COFF::IMAGE_SYM_DEBUG ||
Symb.isSectionDefinition()) {
// TODO: perhaps we need a new symbol type ST_Section.
Result = SymbolRef::ST_Debug;
} else if (!COFF::isReservedSectionNumber(SectionNumber)) {
const coff_section *Section = nullptr;
@ -359,12 +361,17 @@ bool COFFObjectFile::isSectionData(DataRefImpl Ref) const {
bool COFFObjectFile::isSectionBSS(DataRefImpl Ref) const {
const coff_section *Sec = toSec(Ref);
return Sec->Characteristics & COFF::IMAGE_SCN_CNT_UNINITIALIZED_DATA;
const uint32_t BssFlags = COFF::IMAGE_SCN_CNT_UNINITIALIZED_DATA |
COFF::IMAGE_SCN_MEM_READ |
COFF::IMAGE_SCN_MEM_WRITE;
return (Sec->Characteristics & BssFlags) == BssFlags;
}
bool COFFObjectFile::isSectionVirtual(DataRefImpl Ref) const {
const coff_section *Sec = toSec(Ref);
return Sec->Characteristics & COFF::IMAGE_SCN_CNT_UNINITIALIZED_DATA;
// In COFF, a virtual section won't have any in-file
// content, so the file pointer to the content will be zero.
return Sec->PointerToRawData == 0;
}
bool COFFObjectFile::sectionContainsSymbol(DataRefImpl SecRef,

View File

@ -0,0 +1,31 @@
# RUN: llvm-mc -triple=x86_64-pc-win32 -filetype=obj -o %T/COFF_x86_64.o %s
# RUN: llvm-rtdyld -triple=x86_64-pc-win32 -verify -check=%s %/T/COFF_x86_64.o
.text
.def F;
.scl 2;
.type 32;
.endef
.globl __real400921f9f01b866e
.section .rdata,"dr",discard,__real400921f9f01b866e
.align 8
__real400921f9f01b866e:
.quad 4614256650576692846 # double 3.1415899999999999
.text
.globl F
.global inst1
.align 16, 0x90
F: # @F
.Ltmp0:
.seh_proc F
# BB#0: # %entry
.Ltmp1:
.seh_endprologue
# rtdyld-check: decode_operand(inst1, 4) = __real400921f9f01b866e - next_pc(inst1)
inst1:
movsd __real400921f9f01b866e(%rip), %xmm0 # xmm0 = mem[0],zero
retq
.Leh_func_end0:
.Ltmp2:
.seh_endproc