aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEli Bendersky <eli.bendersky@intel.com>2012-01-22 09:01:03 +0000
committerEli Bendersky <eli.bendersky@intel.com>2012-01-22 09:01:03 +0000
commit24973c1063bfb7ac353732a4e8eb801830336c5f (patch)
treea88cf6f0f24e0cac2b40062ec74ef433932958a0
parent76463fdeb603e1d89b05f094bfd6fe73b90d0b61 (diff)
downloadexternal_llvm-24973c1063bfb7ac353732a4e8eb801830336c5f.zip
external_llvm-24973c1063bfb7ac353732a4e8eb801830336c5f.tar.gz
external_llvm-24973c1063bfb7ac353732a4e8eb801830336c5f.tar.bz2
Basic runtime dynamic loading capabilities added to ELFObjectFile, implemented
in a subclass named DyldELFObject. This class supports rebasing the object file it represents by re-mapping section addresses to the actual memory addresses the object was placed in. This is required for MC-JIT implementation on ELF with debugging support. Patch reviewed on llvm-commits. Developed together with Ashok Thirumurthi and Andrew Kaylor. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@148653 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r--include/llvm/Object/ObjectFile.h4
-rw-r--r--include/llvm/Support/Endian.h12
-rw-r--r--lib/Object/ELFObjectFile.cpp258
3 files changed, 254 insertions, 20 deletions
diff --git a/include/llvm/Object/ObjectFile.h b/include/llvm/Object/ObjectFile.h
index 29b7077..e38c8c8 100644
--- a/include/llvm/Object/ObjectFile.h
+++ b/include/llvm/Object/ObjectFile.h
@@ -20,6 +20,7 @@
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/MemoryBuffer.h"
#include <cstring>
+#include <vector>
namespace llvm {
namespace object {
@@ -337,7 +338,8 @@ public:
public:
static ObjectFile *createCOFFObjectFile(MemoryBuffer *Object);
- static ObjectFile *createELFObjectFile(MemoryBuffer *Object);
+ static ObjectFile *createELFObjectFile(MemoryBuffer *Object,
+ bool doDyld = false, std::vector<uint8_t*> *MemoryMap = 0);
static ObjectFile *createMachOObjectFile(MemoryBuffer *Object);
};
diff --git a/include/llvm/Support/Endian.h b/include/llvm/Support/Endian.h
index af1b506..733ab75 100644
--- a/include/llvm/Support/Endian.h
+++ b/include/llvm/Support/Endian.h
@@ -98,6 +98,9 @@ public:
operator value_type() const {
return endian::read_le<value_type, unaligned>(Value);
}
+ void operator=(value_type newValue) {
+ endian::write_le<value_type, unaligned>((void *)&Value, newValue);
+ }
private:
uint8_t Value[sizeof(value_type)];
};
@@ -108,6 +111,9 @@ public:
operator value_type() const {
return endian::read_be<value_type, unaligned>(Value);
}
+ void operator=(value_type newValue) {
+ endian::write_be<value_type, unaligned>((void *)&Value, newValue);
+ }
private:
uint8_t Value[sizeof(value_type)];
};
@@ -118,6 +124,9 @@ public:
operator value_type() const {
return endian::read_le<value_type, aligned>(&Value);
}
+ void operator=(value_type newValue) {
+ endian::write_le<value_type, aligned>((void *)&Value, newValue);
+ }
private:
value_type Value;
};
@@ -128,6 +137,9 @@ public:
operator value_type() const {
return endian::read_be<value_type, aligned>(&Value);
}
+ void operator=(value_type newValue) {
+ endian::write_be<value_type, aligned>((void *)&Value, newValue);
+ }
private:
value_type Value;
};
diff --git a/lib/Object/ELFObjectFile.cpp b/lib/Object/ELFObjectFile.cpp
index a6c4c25..726f804 100644
--- a/lib/Object/ELFObjectFile.cpp
+++ b/lib/Object/ELFObjectFile.cpp
@@ -7,7 +7,7 @@
//
//===----------------------------------------------------------------------===//
//
-// This file defines the ELFObjectFile class.
+// This file defines the ELFObjectFile and DyldELFObject classes.
//
//===----------------------------------------------------------------------===//
@@ -16,6 +16,7 @@
#include "llvm/ADT/Triple.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/Object/ObjectFile.h"
+#include "llvm/Support/Casting.h"
#include "llvm/Support/ELF.h"
#include "llvm/Support/Endian.h"
#include "llvm/Support/ErrorHandling.h"
@@ -53,20 +54,22 @@ struct ELFDataTypeTypedefHelper;
template<support::endianness target_endianness>
struct ELFDataTypeTypedefHelper<target_endianness, false>
: ELFDataTypeTypedefHelperCommon<target_endianness> {
+ typedef uint32_t value_type;
typedef support::detail::packed_endian_specific_integral
- <uint32_t, target_endianness, support::aligned> Elf_Addr;
+ <value_type, target_endianness, support::aligned> Elf_Addr;
typedef support::detail::packed_endian_specific_integral
- <uint32_t, target_endianness, support::aligned> Elf_Off;
+ <value_type, target_endianness, support::aligned> Elf_Off;
};
/// ELF 64bit types.
template<support::endianness target_endianness>
struct ELFDataTypeTypedefHelper<target_endianness, true>
: ELFDataTypeTypedefHelperCommon<target_endianness>{
+ typedef uint64_t value_type;
typedef support::detail::packed_endian_specific_integral
- <uint64_t, target_endianness, support::aligned> Elf_Addr;
+ <value_type, target_endianness, support::aligned> Elf_Addr;
typedef support::detail::packed_endian_specific_integral
- <uint64_t, target_endianness, support::aligned> Elf_Off;
+ <value_type, target_endianness, support::aligned> Elf_Off;
};
}
@@ -263,6 +266,7 @@ class ELFObjectFile : public ObjectFile {
typedef Elf_Rel_Impl<target_endianness, is64Bits, false> Elf_Rel;
typedef Elf_Rel_Impl<target_endianness, is64Bits, true> Elf_Rela;
+protected:
struct Elf_Ehdr {
unsigned char e_ident[ELF::EI_NIDENT]; // ELF Identification bytes
Elf_Half e_type; // Type of file (see ET_*)
@@ -285,7 +289,12 @@ class ELFObjectFile : public ObjectFile {
unsigned char getFileClass() const { return e_ident[ELF::EI_CLASS]; }
unsigned char getDataEncoding() const { return e_ident[ELF::EI_DATA]; }
};
+ // This flag is used for classof, to distinguish ELFObjectFile from
+ // its subclass. If more subclasses will be created, this flag will
+ // have to become an enum.
+ bool isDyldELFObject;
+private:
typedef SmallVector<const Elf_Shdr*, 1> Sections_t;
typedef DenseMap<unsigned, unsigned> IndexMap_t;
typedef DenseMap<const Elf_Shdr*, SmallVector<uint32_t, 1> > RelocMap_t;
@@ -307,13 +316,11 @@ class ELFObjectFile : public ObjectFile {
return getSection(Rel.w.b);
}
- void validateSymbol(DataRefImpl Symb) const;
bool isRelocationHasAddend(DataRefImpl Rel) const;
template<typename T>
const T *getEntry(uint16_t Section, uint32_t Entry) const;
template<typename T>
const T *getEntry(const Elf_Shdr *Section, uint32_t Entry) const;
- const Elf_Sym *getSymbol(DataRefImpl Symb) const;
const Elf_Shdr *getSection(DataRefImpl index) const;
const Elf_Shdr *getSection(uint32_t index) const;
const Elf_Rel *getRel(DataRefImpl Rel) const;
@@ -323,6 +330,10 @@ class ELFObjectFile : public ObjectFile {
error_code getSymbolName(const Elf_Sym *Symb, StringRef &Res) const;
protected:
+ const Elf_Sym *getSymbol(DataRefImpl Symb) const; // FIXME: Should be private?
+ void validateSymbol(DataRefImpl Symb) const;
+
+protected:
virtual error_code getSymbolNext(DataRefImpl Symb, SymbolRef &Res) const;
virtual error_code getSymbolName(DataRefImpl Symb, StringRef &Res) const;
virtual error_code getSymbolFileOffset(DataRefImpl Symb, uint64_t &Res) const;
@@ -384,8 +395,10 @@ public:
ELF::Elf64_Word getSymbolTableIndex(const Elf_Sym *symb) const;
const Elf_Shdr *getSection(const Elf_Sym *symb) const;
+ // Methods for type inquiry through isa, cast, and dyn_cast
+ bool isDyldType() const { return isDyldELFObject; }
static inline bool classof(const Binary *v) {
- return v->getType() == isELF;
+ return v->getType() == Binary::isELF;
}
static inline bool classof(const ELFObjectFile *v) { return true; }
};
@@ -471,7 +484,7 @@ error_code ELFObjectFile<target_endianness, is64Bits>
const Elf_Shdr *Section;
switch (getSymbolTableIndex(symb)) {
case ELF::SHN_COMMON:
- // Undefined symbols have no address yet.
+ // Unintialized symbols have no offset in the object file
case ELF::SHN_UNDEF:
Result = UnknownAddressOrSize;
return object_error::success;
@@ -489,7 +502,7 @@ error_code ELFObjectFile<target_endianness, is64Bits>
case ELF::STT_OBJECT:
case ELF::STT_NOTYPE:
Result = symb->st_value +
- (Section ? Section->sh_offset - Section->sh_addr : 0);
+ (Section ? Section->sh_offset : 0);
return object_error::success;
default:
Result = UnknownAddressOrSize;
@@ -506,7 +519,6 @@ error_code ELFObjectFile<target_endianness, is64Bits>
const Elf_Shdr *Section;
switch (getSymbolTableIndex(symb)) {
case ELF::SHN_COMMON:
- // Undefined symbols have no address yet.
case ELF::SHN_UNDEF:
Result = UnknownAddressOrSize;
return object_error::success;
@@ -523,7 +535,7 @@ error_code ELFObjectFile<target_endianness, is64Bits>
case ELF::STT_FUNC:
case ELF::STT_OBJECT:
case ELF::STT_NOTYPE:
- Result = symb->st_value;
+ Result = symb->st_value + (Section ? Section->sh_addr : 0);
return object_error::success;
default:
Result = UnknownAddressOrSize;
@@ -1157,6 +1169,7 @@ template<support::endianness target_endianness, bool is64Bits>
ELFObjectFile<target_endianness, is64Bits>::ELFObjectFile(MemoryBuffer *Object
, error_code &ec)
: ObjectFile(Binary::isELF, Object, ec)
+ , isDyldELFObject(false)
, SectionHeaderTable(0)
, dot_shstrtab_sec(0)
, dot_strtab_sec(0) {
@@ -1168,10 +1181,12 @@ ELFObjectFile<target_endianness, is64Bits>::ELFObjectFile(MemoryBuffer *Object
SectionHeaderTable =
reinterpret_cast<const Elf_Shdr *>(base() + Header->e_shoff);
uint64_t SectionTableSize = getNumSections() * Header->e_shentsize;
- if (!( (const uint8_t *)SectionHeaderTable + SectionTableSize
- <= base() + Data->getBufferSize()))
+
+ if ((const uint8_t *)SectionHeaderTable + SectionTableSize
+ > base() + Data->getBufferSize()) {
// FIXME: Proper error handling.
report_fatal_error("Section table goes past end of file!");
+ }
// To find the symbol tables we walk the section table to find SHT_SYMTAB.
@@ -1466,21 +1481,226 @@ getElfArchType(MemoryBuffer *Object) {
, (uint8_t)Object->getBufferStart()[ELF::EI_DATA]);
}
+
+namespace {
+ template<support::endianness target_endianness, bool is64Bits>
+ class DyldELFObject : public ELFObjectFile<target_endianness, is64Bits> {
+ LLVM_ELF_IMPORT_TYPES(target_endianness, is64Bits)
+
+ typedef Elf_Shdr_Impl<target_endianness, is64Bits> Elf_Shdr;
+ typedef Elf_Sym_Impl<target_endianness, is64Bits> Elf_Sym;
+ typedef Elf_Rel_Impl<target_endianness, is64Bits, false> Elf_Rel;
+ typedef Elf_Rel_Impl<target_endianness, is64Bits, true> Elf_Rela;
+
+ typedef typename ELFObjectFile<target_endianness, is64Bits>::
+ Elf_Ehdr Elf_Ehdr;
+ Elf_Ehdr *Header;
+
+ // Update section headers according to the current location in memory
+ virtual void rebaseObject(std::vector<uint8_t*> *MemoryMap);
+ // Record memory addresses for cleanup
+ virtual void saveAddress(std::vector<uint8_t*> *MemoryMap, uint8_t *addr);
+
+ protected:
+ virtual error_code getSymbolAddress(DataRefImpl Symb, uint64_t &Res) const;
+
+ public:
+ DyldELFObject(MemoryBuffer *Object, std::vector<uint8_t*> *MemoryMap,
+ error_code &ec);
+
+ // Methods for type inquiry through isa, cast, and dyn_cast
+ static inline bool classof(const Binary *v) {
+ return (isa<ELFObjectFile<target_endianness, is64Bits> >(v)
+ && classof(cast<ELFObjectFile<target_endianness, is64Bits> >(v)));
+ }
+ static inline bool classof(
+ const ELFObjectFile<target_endianness, is64Bits> *v) {
+ return v->isDyldType();
+ }
+ static inline bool classof(const DyldELFObject *v) {
+ return true;
+ }
+ };
+} // end anonymous namespace
+
+template<support::endianness target_endianness, bool is64Bits>
+DyldELFObject<target_endianness, is64Bits>::DyldELFObject(MemoryBuffer *Object,
+ std::vector<uint8_t*> *MemoryMap, error_code &ec)
+ : ELFObjectFile<target_endianness, is64Bits>(Object, ec)
+ , Header(0) {
+ this->isDyldELFObject = true;
+ Header = const_cast<Elf_Ehdr *>(
+ reinterpret_cast<const Elf_Ehdr *>(this->base()));
+ if (Header->e_shoff == 0)
+ return;
+
+ // Mark the image as a dynamic shared library
+ Header->e_type = ELF::ET_DYN;
+
+ rebaseObject(MemoryMap);
+}
+
+// Walk through the ELF headers, updating virtual addresses to reflect where
+// the object is currently loaded in memory
+template<support::endianness target_endianness, bool is64Bits>
+void DyldELFObject<target_endianness, is64Bits>::rebaseObject(
+ std::vector<uint8_t*> *MemoryMap) {
+ typedef typename ELFDataTypeTypedefHelper<
+ target_endianness, is64Bits>::value_type addr_type;
+
+ uint8_t *base_p = const_cast<uint8_t *>(this->base());
+ Elf_Shdr *sectionTable =
+ reinterpret_cast<Elf_Shdr *>(base_p + Header->e_shoff);
+ uint64_t numSections = this->getNumSections();
+
+ // Allocate memory space for NOBITS sections (such as .bss), which only exist
+ // in memory, but don't occupy space in the object file.
+ // Update the address in the section headers to reflect this allocation.
+ for (uint64_t index = 0; index < numSections; index++) {
+ Elf_Shdr *sec = reinterpret_cast<Elf_Shdr *>(
+ reinterpret_cast<char *>(sectionTable) + index * Header->e_shentsize);
+
+ // Only update sections that are meant to be present in program memory
+ if (sec->sh_flags & ELF::SHF_ALLOC) {
+ uint8_t *addr = base_p + sec->sh_offset;
+ if (sec->sh_type == ELF::SHT_NOBITS) {
+ addr = static_cast<uint8_t *>(calloc(sec->sh_size, 1));
+ saveAddress(MemoryMap, addr);
+ }
+ else {
+ // FIXME: Currently memory with RWX permissions is allocated. In the
+ // future, make sure that permissions are as necessary
+ if (sec->sh_flags & ELF::SHF_WRITE) {
+ // see FIXME above
+ }
+ if (sec->sh_flags & ELF::SHF_EXECINSTR) {
+ // see FIXME above
+ }
+ }
+ assert(sizeof(addr_type) == sizeof(intptr_t) &&
+ "Cross-architecture ELF dy-load is not supported!");
+ sec->sh_addr = static_cast<addr_type>(intptr_t(addr));
+ }
+ }
+
+ // Now allocate actual space for COMMON symbols, which also don't occupy
+ // space in the object file.
+ // We want to allocate space for all COMMON symbols at once, so the flow is:
+ // 1. Go over all symbols, find those that are in COMMON. For each such
+ // symbol, record its size and the value field in its symbol header in a
+ // special vector.
+ // 2. Allocate memory for all COMMON symbols in one fell swoop.
+ // 3. Using the recorded information from (1), update the address fields in
+ // the symbol headers of the COMMON symbols to reflect their allocated
+ // address.
+ uint64_t TotalSize = 0;
+ std::vector<std::pair<Elf_Addr *, uint64_t> > SymbAddrInfo;
+ error_code ec = object_error::success;
+ for (symbol_iterator si = this->begin_symbols(),
+ se = this->end_symbols(); si != se; si.increment(ec)) {
+ uint64_t Size = 0;
+ ec = si->getSize(Size);
+ Elf_Sym* symb = const_cast<Elf_Sym*>(
+ this->getSymbol(si->getRawDataRefImpl()));
+ if (ec == object_error::success &&
+ this->getSymbolTableIndex(symb) == ELF::SHN_COMMON && Size > 0) {
+ SymbAddrInfo.push_back(std::make_pair(&(symb->st_value), Size));
+ TotalSize += Size;
+ }
+ }
+
+ uint8_t* SectionPtr = (uint8_t *)calloc(TotalSize, 1);
+ saveAddress(MemoryMap, SectionPtr);
+
+ typedef typename std::vector<std::pair<Elf_Addr *, uint64_t> >::iterator
+ AddrInfoIterator;
+ AddrInfoIterator EndIter = SymbAddrInfo.end();
+ for (AddrInfoIterator AddrIter = SymbAddrInfo.begin();
+ AddrIter != EndIter; ++AddrIter) {
+ assert(sizeof(addr_type) == sizeof(intptr_t) &&
+ "Cross-architecture ELF dy-load is not supported!");
+ *(AddrIter->first) = static_cast<addr_type>(intptr_t(SectionPtr));
+ SectionPtr += AddrIter->second;
+ }
+}
+
+// Record memory addresses for callers
+template<support::endianness target_endianness, bool is64Bits>
+void DyldELFObject<target_endianness, is64Bits>::saveAddress(
+ std::vector<uint8_t*> *MemoryMap, uint8_t* addr) {
+ if (MemoryMap)
+ MemoryMap->push_back(addr);
+ else
+ errs() << "WARNING: Memory leak - cannot record memory for ELF dyld.";
+}
+
+template<support::endianness target_endianness, bool is64Bits>
+error_code DyldELFObject<target_endianness, is64Bits>::getSymbolAddress(
+ DataRefImpl Symb, uint64_t &Result) const {
+ this->validateSymbol(Symb);
+ const Elf_Sym *symb = this->getSymbol(Symb);
+ if (this->getSymbolTableIndex(symb) == ELF::SHN_COMMON) {
+ Result = symb->st_value;
+ return object_error::success;
+ }
+ else {
+ return ELFObjectFile<target_endianness, is64Bits>::getSymbolAddress(
+ Symb, Result);
+ }
+}
+
namespace llvm {
- ObjectFile *ObjectFile::createELFObjectFile(MemoryBuffer *Object) {
+ // Creates an in-memory object-file by default: createELFObjectFile(Buffer)
+ // Set doDyld to true to create a live (executable/debug-worthy) image
+ // If doDyld is true, any memory allocated for non-resident sections and
+ // symbols is recorded in MemoryMap.
+ ObjectFile *ObjectFile::createELFObjectFile(MemoryBuffer *Object,
+ bool doDyld, std::vector<uint8_t *> *MemoryMap) {
std::pair<unsigned char, unsigned char> Ident = getElfArchType(Object);
error_code ec;
+
+ if (doDyld) {
+ if (Ident.first == ELF::ELFCLASS32 && Ident.second == ELF::ELFDATA2LSB)
+ return new DyldELFObject<support::little, false>(Object, MemoryMap, ec);
+ else if (Ident.first == ELF::ELFCLASS32 && Ident.second == ELF::ELFDATA2MSB)
+ return new DyldELFObject<support::big, false>(Object, MemoryMap, ec);
+ else if (Ident.first == ELF::ELFCLASS64 && Ident.second == ELF::ELFDATA2MSB)
+ return new DyldELFObject<support::big, true>(Object, MemoryMap, ec);
+ else if (Ident.first == ELF::ELFCLASS64 && Ident.second == ELF::ELFDATA2LSB) {
+ DyldELFObject<support::little, true> *result =
+ new DyldELFObject<support::little, true>(Object, MemoryMap, ec);
+
+ // Unit testing for type inquiry
+ bool isBinary = isa<Binary>(result);
+ bool isDyld = isa<DyldELFObject<support::little, true> >(result);
+ bool isFile = isa<ELFObjectFile<support::little, true> >(result);
+ assert(isBinary && isDyld && isFile &&
+ "Type inquiry failed for ELF object!");
+ return result;
+ }
+ }
+
if (Ident.first == ELF::ELFCLASS32 && Ident.second == ELF::ELFDATA2LSB)
return new ELFObjectFile<support::little, false>(Object, ec);
else if (Ident.first == ELF::ELFCLASS32 && Ident.second == ELF::ELFDATA2MSB)
return new ELFObjectFile<support::big, false>(Object, ec);
- else if (Ident.first == ELF::ELFCLASS64 && Ident.second == ELF::ELFDATA2LSB)
- return new ELFObjectFile<support::little, true>(Object, ec);
else if (Ident.first == ELF::ELFCLASS64 && Ident.second == ELF::ELFDATA2MSB)
return new ELFObjectFile<support::big, true>(Object, ec);
- // FIXME: Proper error handling.
- report_fatal_error("Not an ELF object file!");
+ else if (Ident.first == ELF::ELFCLASS64 && Ident.second == ELF::ELFDATA2LSB) {
+ ELFObjectFile<support::little, true> *result =
+ new ELFObjectFile<support::little, true>(Object, ec);
+
+ // Unit testing for type inquiry
+ bool isBinary = isa<Binary>(result);
+ bool isDyld = isa<DyldELFObject<support::little, true> >(result);
+ bool isFile = isa<ELFObjectFile<support::little, true> >(result);
+ assert(isBinary && isFile && !isDyld &&
+ "Type inquiry failed for ELF object!");
+ return result;
+ }
+
+ report_fatal_error("Buffer is not an ELF object file!");
}
} // end namespace llvm