From a66a18505e07a4e72d6fa7e85663937a257577f3 Mon Sep 17 00:00:00 2001 From: Eli Bendersky Date: Mon, 16 Jan 2012 08:56:09 +0000 Subject: Adding a basic ELF dynamic loader and MC-JIT for ELF. Functionality is currently basic and will be enhanced with future patches. Patch developed by Andy Kaylor and Daniel Malea. Reviewed on llvm-commits. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@148231 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/ExecutionEngine/RuntimeDyld/CMakeLists.txt | 1 + lib/ExecutionEngine/RuntimeDyld/RuntimeDyld.cpp | 35 ++- lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.cpp | 282 +++++++++++++++++++++ lib/ExecutionEngine/RuntimeDyld/RuntimeDyldImpl.h | 60 +++++ test/ExecutionEngine/2002-12-16-ArgTest.ll | 3 +- test/ExecutionEngine/2003-01-04-ArgumentBug.ll | 3 +- test/ExecutionEngine/2003-01-04-LoopTest.ll | 3 +- test/ExecutionEngine/2003-01-04-PhiTest.ll | 2 +- test/ExecutionEngine/2003-01-09-SARTest.ll | 2 +- test/ExecutionEngine/2003-01-10-FUCOM.ll | 2 +- test/ExecutionEngine/2003-01-15-AlignmentTest.ll | 2 +- test/ExecutionEngine/2003-05-06-LivenessClobber.ll | 3 +- test/ExecutionEngine/2003-05-07-ArgumentTest.ll | 3 +- test/ExecutionEngine/2003-05-11-PHIRegAllocBug.ll | 2 +- test/ExecutionEngine/2003-06-04-bzip2-bug.ll | 2 +- test/ExecutionEngine/2003-06-05-PHIBug.ll | 2 +- test/ExecutionEngine/2003-08-15-AllocaAssertion.ll | 2 +- test/ExecutionEngine/2003-08-21-EnvironmentTest.ll | 3 +- .../2003-08-23-RegisterAllocatePhysReg.ll | 2 +- ...-10-18-PHINode-ConstantExpr-CondCode-Failure.ll | 2 +- test/ExecutionEngine/2005-12-02-TailCallBug.ll | 3 +- test/ExecutionEngine/2007-12-10-APIntLoadStore.ll | 2 +- test/ExecutionEngine/2008-06-05-APInt-OverAShr.ll | 2 +- test/ExecutionEngine/2010-01-15-UndefValue.ll | 2 +- test/ExecutionEngine/fpbitcast.ll | 2 +- test/ExecutionEngine/hello.ll | 3 +- test/ExecutionEngine/hello2.ll | 3 +- test/ExecutionEngine/simplesttest.ll | 2 +- test/ExecutionEngine/simpletest.ll | 2 +- test/ExecutionEngine/stubs.ll | 3 +- test/ExecutionEngine/test-arith.ll | 2 +- test/ExecutionEngine/test-branch.ll | 2 +- test/ExecutionEngine/test-call.ll | 3 +- test/ExecutionEngine/test-cast.ll | 2 +- test/ExecutionEngine/test-constantexpr.ll | 2 +- test/ExecutionEngine/test-fp.ll | 3 +- test/ExecutionEngine/test-loadstore.ll | 3 +- test/ExecutionEngine/test-logical.ll | 2 +- test/ExecutionEngine/test-loop.ll | 2 +- test/ExecutionEngine/test-phi.ll | 2 +- test/ExecutionEngine/test-ret.ll | 2 +- test/ExecutionEngine/test-setcond-fp.ll | 2 +- test/ExecutionEngine/test-setcond-int.ll | 2 +- test/ExecutionEngine/test-shift.ll | 2 +- test/lit.cfg | 30 ++- tools/lli/lli.cpp | 26 +- 46 files changed, 463 insertions(+), 64 deletions(-) create mode 100644 lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.cpp diff --git a/lib/ExecutionEngine/RuntimeDyld/CMakeLists.txt b/lib/ExecutionEngine/RuntimeDyld/CMakeLists.txt index 59bdfee3db..002e63cd3b 100644 --- a/lib/ExecutionEngine/RuntimeDyld/CMakeLists.txt +++ b/lib/ExecutionEngine/RuntimeDyld/CMakeLists.txt @@ -1,4 +1,5 @@ add_llvm_library(LLVMRuntimeDyld RuntimeDyld.cpp RuntimeDyldMachO.cpp + RuntimeDyldELF.cpp ) diff --git a/lib/ExecutionEngine/RuntimeDyld/RuntimeDyld.cpp b/lib/ExecutionEngine/RuntimeDyld/RuntimeDyld.cpp index 33dd705027..b017ebb2dc 100644 --- a/lib/ExecutionEngine/RuntimeDyld/RuntimeDyld.cpp +++ b/lib/ExecutionEngine/RuntimeDyld/RuntimeDyld.cpp @@ -13,6 +13,7 @@ #define DEBUG_TYPE "dyld" #include "RuntimeDyldImpl.h" +#include "llvm/Support/Path.h" using namespace llvm; using namespace llvm::object; @@ -64,12 +65,36 @@ RuntimeDyld::~RuntimeDyld() { bool RuntimeDyld::loadObject(MemoryBuffer *InputBuffer) { if (!Dyld) { - if (RuntimeDyldMachO::isKnownFormat(InputBuffer)) - Dyld = new RuntimeDyldMachO(MM); - else - report_fatal_error("Unknown object format!"); + sys::LLVMFileType type = sys::IdentifyFileType( + InputBuffer->getBufferStart(), + static_cast(InputBuffer->getBufferSize())); + switch (type) { + case sys::ELF_Relocatable_FileType: + case sys::ELF_Executable_FileType: + case sys::ELF_SharedObject_FileType: + case sys::ELF_Core_FileType: + Dyld = new RuntimeDyldELF(MM); + break; + case sys::Mach_O_Object_FileType: + case sys::Mach_O_Executable_FileType: + case sys::Mach_O_FixedVirtualMemorySharedLib_FileType: + case sys::Mach_O_Core_FileType: + case sys::Mach_O_PreloadExecutable_FileType: + case sys::Mach_O_DynamicallyLinkedSharedLib_FileType: + case sys::Mach_O_DynamicLinker_FileType: + case sys::Mach_O_Bundle_FileType: + case sys::Mach_O_DynamicallyLinkedSharedLibStub_FileType: + case sys::Mach_O_DSYMCompanion_FileType: + Dyld = new RuntimeDyldMachO(MM); + break; + case sys::Unknown_FileType: + case sys::Bitcode_FileType: + case sys::Archive_FileType: + case sys::COFF_FileType: + report_fatal_error("Incompatible object format!"); + } } else { - if(!Dyld->isCompatibleFormat(InputBuffer)) + if (!Dyld->isCompatibleFormat(InputBuffer)) report_fatal_error("Incompatible object format!"); } diff --git a/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.cpp b/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.cpp new file mode 100644 index 0000000000..9ff95ff8b6 --- /dev/null +++ b/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.cpp @@ -0,0 +1,282 @@ +//===-- RuntimeDyldELF.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 ELF support for the MC-JIT runtime dynamic linker. +// +//===----------------------------------------------------------------------===// + +#define DEBUG_TYPE "dyld" +#include "llvm/ADT/OwningPtr.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/IntervalMap.h" +#include "RuntimeDyldImpl.h" +#include "llvm/Object/ObjectFile.h" +#include "llvm/Support/ELF.h" +#include "llvm/ADT/Triple.h" +using namespace llvm; +using namespace llvm::object; + +namespace llvm { + +namespace { + +// FIXME: this function should probably not live here... +// +// Returns the name and address of an unrelocated symbol in an ELF section +void getSymbolInfo(symbol_iterator Sym, uint64_t &Addr, StringRef &Name) { + //FIXME: error checking here required to catch corrupt ELF objects... + error_code Err = Sym->getName(Name); + + uint64_t AddrInSection; + Err = Sym->getAddress(AddrInSection); + + SectionRef empty_section; + section_iterator Section(empty_section); + Err = Sym->getSection(Section); + + StringRef SectionContents; + Section->getContents(SectionContents); + + Addr = reinterpret_cast(SectionContents.data()) + AddrInSection; +} + +} + +bool RuntimeDyldELF::loadObject(MemoryBuffer *InputBuffer) { + if (!isCompatibleFormat(InputBuffer)) + return true; + + OwningPtr Obj(ObjectFile::createELFObjectFile(InputBuffer)); + + Arch = Obj->getArch(); + + // Map address in the Object file image to function names + IntervalMap::Allocator A; + IntervalMap FuncMap(A); + + // This is a bit of a hack. The ObjectFile we've just loaded reports + // section addresses as 0 and doesn't provide access to the section + // offset (from which we could calculate the address. Instead, + // we're storing the address when it comes up in the ST_Debug case + // below. + // + StringMap DebugSymbolMap; + + symbol_iterator SymEnd = Obj->end_symbols(); + error_code Err; + for (symbol_iterator Sym = Obj->begin_symbols(); + Sym != SymEnd; Sym.increment(Err)) { + SymbolRef::Type Type; + Sym->getType(Type); + if (Type == SymbolRef::ST_Function) { + StringRef Name; + uint64_t Addr; + getSymbolInfo(Sym, Addr, Name); + + uint64_t Size; + Err = Sym->getSize(Size); + + uint8_t *Start; + uint8_t *End; + Start = reinterpret_cast(Addr); + End = reinterpret_cast(Addr + Size - 1); + + extractFunction(Name, Start, End); + FuncMap.insert(Addr, Addr + Size - 1, Name); + } else if (Type == SymbolRef::ST_Debug) { + // This case helps us find section addresses + StringRef Name; + uint64_t Addr; + getSymbolInfo(Sym, Addr, Name); + DebugSymbolMap[Name] = Addr; + } + } + + // Iterate through the relocations for this object + section_iterator SecEnd = Obj->end_sections(); + for (section_iterator Sec = Obj->begin_sections(); + Sec != SecEnd; Sec.increment(Err)) { + StringRef SecName; + uint64_t SecAddr; + Sec->getName(SecName); + // Ignore sections that aren't in our map + if (DebugSymbolMap.find(SecName) == DebugSymbolMap.end()) { + continue; + } + SecAddr = DebugSymbolMap[SecName]; + relocation_iterator RelEnd = Sec->end_relocations(); + for (relocation_iterator Rel = Sec->begin_relocations(); + Rel != RelEnd; Rel.increment(Err)) { + uint64_t RelOffset; + uint64_t RelType; + int64_t RelAddend; + SymbolRef RelSym; + StringRef SymName; + uint64_t SymAddr; + uint64_t SymOffset; + + Rel->getAddress(RelOffset); + Rel->getType(RelType); + Rel->getAdditionalInfo(RelAddend); + Rel->getSymbol(RelSym); + RelSym.getName(SymName); + RelSym.getAddress(SymAddr); + RelSym.getFileOffset(SymOffset); + + // If this relocation is inside a function, we want to store the + // function name and a function-relative offset + IntervalMap::iterator ContainingFunc + = FuncMap.find(SecAddr + RelOffset); + if (ContainingFunc.valid()) { + // Re-base the relocation to make it relative to the target function + RelOffset = (SecAddr + RelOffset) - ContainingFunc.start(); + Relocations[SymName].push_back(RelocationEntry(ContainingFunc.value(), + RelOffset, + RelType, + RelAddend, + true)); + } else { + Relocations[SymName].push_back(RelocationEntry(SecName, + RelOffset, + RelType, + RelAddend, + false)); + } + } + } + return false; +} + +void RuntimeDyldELF::resolveX86_64Relocation(StringRef Name, + uint8_t *Addr, + const RelocationEntry &RE) { + uint8_t *TargetAddr; + if (RE.IsFunctionRelative) { + StringMap::iterator ContainingFunc + = Functions.find(RE.Target); + assert(ContainingFunc != Functions.end() + && "Function for relocation not found"); + TargetAddr = reinterpret_cast(ContainingFunc->getValue().base()) + + RE.Offset; + } else { + // FIXME: Get the address of the target section and add that to RE.Offset + assert(0 && ("Non-function relocation not implemented yet!")); + } + + switch (RE.Type) { + default: + assert(0 && ("Relocation type not implemented yet!")); + break; + case ELF::R_X86_64_64: { + uint8_t **Target = reinterpret_cast(TargetAddr); + *Target = Addr + RE.Addend; + break; + } + case ELF::R_X86_64_32: + case ELF::R_X86_64_32S: { + uint64_t Value = reinterpret_cast(Addr) + RE.Addend; + // FIXME: Handle the possibility of this assertion failing + assert((RE.Type == ELF::R_X86_64_32 && !(Value & 0xFFFFFFFF00000000)) || + (RE.Type == ELF::R_X86_64_32S && + (Value & 0xFFFFFFFF00000000) == 0xFFFFFFFF00000000)); + uint32_t TruncatedAddr = (Value & 0xFFFFFFFF); + uint32_t *Target = reinterpret_cast(TargetAddr); + *Target = TruncatedAddr; + break; + } + case ELF::R_X86_64_PC32: { + uint32_t *Placeholder = reinterpret_cast(TargetAddr); + uint64_t RealOffset = *Placeholder + + reinterpret_cast(Addr) + + RE.Addend - reinterpret_cast(TargetAddr); + assert((RealOffset & 0xFFFFFFFF) == RealOffset); + uint32_t TruncOffset = (RealOffset & 0xFFFFFFFF); + *Placeholder = TruncOffset; + break; + } + } +} + +void RuntimeDyldELF::resolveX86Relocation(StringRef Name, + uint8_t *Addr, + const RelocationEntry &RE) { + uint8_t *TargetAddr; + if (RE.IsFunctionRelative) { + StringMap::iterator ContainingFunc + = Functions.find(RE.Target); + assert(ContainingFunc != Functions.end() + && "Function for relocation not found"); + TargetAddr = reinterpret_cast( + ContainingFunc->getValue().base()) + RE.Offset; + } else { + // FIXME: Get the address of the target section and add that to RE.Offset + assert(0 && ("Non-function relocation not implemented yet!")); + } + + switch (RE.Type) { + case ELF::R_386_32: { + uint8_t **Target = reinterpret_cast(TargetAddr); + *Target = Addr + RE.Addend; + break; + } + case ELF::R_386_PC32: { + uint32_t *Placeholder = reinterpret_cast(TargetAddr); + uint32_t RealOffset = *Placeholder + reinterpret_cast(Addr) + + RE.Addend - reinterpret_cast(TargetAddr); + *Placeholder = RealOffset; + break; + } + default: + // There are other relocation types, but it appears these are the + // only ones currently used by the LLVM ELF object writer + assert(0 && ("Relocation type not implemented yet!")); + break; + } +} + +void RuntimeDyldELF::resolveArmRelocation(StringRef Name, + uint8_t *Addr, + const RelocationEntry &RE) { +} + +void RuntimeDyldELF::resolveRelocation(StringRef Name, + uint8_t *Addr, + const RelocationEntry &RE) { + switch (Arch) { + case Triple::x86_64: + resolveX86_64Relocation(Name, Addr, RE); + break; + case Triple::x86: + resolveX86Relocation(Name, Addr, RE); + break; + case Triple::arm: + resolveArmRelocation(Name, Addr, RE); + break; + default: + assert(0 && "Unsupported CPU type!"); + break; + } +} + +void RuntimeDyldELF::reassignSymbolAddress(StringRef Name, uint8_t *Addr) { + SymbolTable[Name] = Addr; + + RelocationList &Relocs = Relocations[Name]; + for (unsigned i = 0, e = Relocs.size(); i != e; ++i) { + RelocationEntry &RE = Relocs[i]; + resolveRelocation(Name, Addr, RE); + } +} + +bool RuntimeDyldELF::isCompatibleFormat(const MemoryBuffer *InputBuffer) const { + StringRef Magic = InputBuffer->getBuffer().slice(0, ELF::EI_NIDENT); + return (memcmp(Magic.data(), ELF::ElfMagic, strlen(ELF::ElfMagic))) == 0; +} +} // namespace llvm diff --git a/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldImpl.h b/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldImpl.h index 7190a3c36f..cff7cbdf28 100644 --- a/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldImpl.h +++ b/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldImpl.h @@ -94,6 +94,66 @@ public: virtual bool isCompatibleFormat(const MemoryBuffer *InputBuffer) const = 0; }; +class RuntimeDyldELF : public RuntimeDyldImpl { + // For each symbol, keep a list of relocations based on it. Anytime + // its address is reassigned (the JIT re-compiled the function, e.g.), + // the relocations get re-resolved. + struct RelocationEntry { + // Function or section this relocation is contained in. + std::string Target; + // Offset into the target function or section for the relocation. + uint32_t Offset; + // Relocation type + uint32_t Type; + // Addend encoded in the instruction itself, if any. + int32_t Addend; + // Has the relocation been recalcuated as an offset within a function? + bool IsFunctionRelative; + // Has this relocation been resolved previously? + bool isResolved; + + RelocationEntry(StringRef t, + uint32_t offset, + uint32_t type, + int32_t addend, + bool isFunctionRelative) + : Target(t) + , Offset(offset) + , Type(type) + , Addend(addend) + , IsFunctionRelative(isFunctionRelative) + , isResolved(false) { } + }; + typedef SmallVector RelocationList; + StringMap Relocations; + unsigned Arch; + + void resolveX86_64Relocation(StringRef Name, + uint8_t *Addr, + const RelocationEntry &RE); + + void resolveX86Relocation(StringRef Name, + uint8_t *Addr, + const RelocationEntry &RE); + + void resolveArmRelocation(StringRef Name, + uint8_t *Addr, + const RelocationEntry &RE); + + void resolveRelocation(StringRef Name, + uint8_t *Addr, + const RelocationEntry &RE); + +public: + RuntimeDyldELF(RTDyldMemoryManager *mm) : RuntimeDyldImpl(mm) {} + + bool loadObject(MemoryBuffer *InputBuffer); + + void reassignSymbolAddress(StringRef Name, uint8_t *Addr); + + bool isCompatibleFormat(const MemoryBuffer *InputBuffer) const; +}; + class RuntimeDyldMachO : public RuntimeDyldImpl { diff --git a/test/ExecutionEngine/2002-12-16-ArgTest.ll b/test/ExecutionEngine/2002-12-16-ArgTest.ll index eba58ccca4..b36feee516 100644 --- a/test/ExecutionEngine/2002-12-16-ArgTest.ll +++ b/test/ExecutionEngine/2002-12-16-ArgTest.ll @@ -1,4 +1,5 @@ -; RUN: lli %s > /dev/null +; RUN: %lli %s > /dev/null +; XFAIL: mcjit @.LC0 = internal global [10 x i8] c"argc: %d\0A\00" ; <[10 x i8]*> [#uses=1] diff --git a/test/ExecutionEngine/2003-01-04-ArgumentBug.ll b/test/ExecutionEngine/2003-01-04-ArgumentBug.ll index 577226b531..0cc0efd7ea 100644 --- a/test/ExecutionEngine/2003-01-04-ArgumentBug.ll +++ b/test/ExecutionEngine/2003-01-04-ArgumentBug.ll @@ -1,5 +1,6 @@ -; RUN: lli %s > /dev/null +; RUN: %lli %s > /dev/null ; XFAIL: arm +; XFAIL: mcjit define i32 @foo(i32 %X, i32 %Y, double %A) { %cond212 = fcmp une double %A, 1.000000e+00 ; [#uses=1] diff --git a/test/ExecutionEngine/2003-01-04-LoopTest.ll b/test/ExecutionEngine/2003-01-04-LoopTest.ll index 61b0a1bd58..e4049a76e0 100644 --- a/test/ExecutionEngine/2003-01-04-LoopTest.ll +++ b/test/ExecutionEngine/2003-01-04-LoopTest.ll @@ -1,5 +1,6 @@ -; RUN: lli %s > /dev/null +; RUN: %lli %s > /dev/null ; XFAIL: arm +; XFAIL: mcjit define i32 @main() { call i32 @mylog( i32 4 ) ; :1 [#uses=0] diff --git a/test/ExecutionEngine/2003-01-04-PhiTest.ll b/test/ExecutionEngine/2003-01-04-PhiTest.ll index 2bc70d749f..48576e7c83 100644 --- a/test/ExecutionEngine/2003-01-04-PhiTest.ll +++ b/test/ExecutionEngine/2003-01-04-PhiTest.ll @@ -1,4 +1,4 @@ -; RUN: lli %s > /dev/null +; RUN: %lli %s > /dev/null define i32 @main() { ;