diff options
author | Brandon Potter <brandon.potter@amd.com> | 2016-03-17 10:31:03 -0700 |
---|---|---|
committer | Brandon Potter <brandon.potter@amd.com> | 2016-03-17 10:31:03 -0700 |
commit | 9b4249410ec18cac9df2c7e9c0a4a6ce5459233d (patch) | |
tree | c3260ef4f23b9eca7d835ab1e0dfc8ce1173b17c /src/base | |
parent | 4fc69db8f89049a881a5f4aa68545840818b124c (diff) | |
download | gem5-9b4249410ec18cac9df2c7e9c0a4a6ce5459233d.tar.xz |
base: support dynamic loading of Linux ELF objects in SE mode
Diffstat (limited to 'src/base')
-rw-r--r-- | src/base/loader/elf_object.cc | 85 | ||||
-rw-r--r-- | src/base/loader/elf_object.hh | 39 | ||||
-rw-r--r-- | src/base/loader/object_file.hh | 10 |
3 files changed, 120 insertions, 14 deletions
diff --git a/src/base/loader/elf_object.cc b/src/base/loader/elf_object.cc index 6bfc0e460..e0b0439c8 100644 --- a/src/base/loader/elf_object.cc +++ b/src/base/loader/elf_object.cc @@ -41,22 +41,30 @@ * Ali Saidi */ +#include "base/loader/elf_object.hh" + +#include <fcntl.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + #include <cassert> #include <string> -#include "base/loader/elf_object.hh" -#include "base/loader/symtab.hh" #include "base/bitfield.hh" +#include "base/loader/symtab.hh" #include "base/misc.hh" #include "base/trace.hh" #include "debug/Loader.hh" -#include "sim/byteswap.hh" #include "gelf.h" +#include "sim/byteswap.hh" using namespace std; ObjectFile * -ElfObject::tryFile(const string &fname, size_t len, uint8_t *data) +ElfObject::tryFile(const string &fname, size_t len, uint8_t *data, + bool skip_interp_check) { Elf *elf; GElf_Ehdr ehdr; @@ -243,6 +251,41 @@ ElfObject::tryFile(const string &fname, size_t len, uint8_t *data) result->_programHeaderTable = 0; + if (!skip_interp_check) { + for (int i = 0; i < ehdr.e_phnum; i++) { + GElf_Phdr phdr; + M5_VAR_USED void *check_p = gelf_getphdr(elf, i, &phdr); + assert(check_p != nullptr); + + if (phdr.p_type != PT_INTERP) + continue; + + char *interp_path = (char*)data + phdr.p_offset; + int fd = open(interp_path, O_RDONLY); + if (fd == -1) { + fatal("Unable to open dynamic executable's " + "interpreter.\n"); + } + + struct stat sb; + M5_VAR_USED int check_i = fstat(fd, &sb); + assert(check_i == 0); + + void *mm = mmap(nullptr, sb.st_size, PROT_READ, + MAP_PRIVATE, fd, 0); + assert(mm != MAP_FAILED); + ::close(fd); + + uint8_t *interp_image = (uint8_t*)mm; + ObjectFile *obj = tryFile(interp_path, sb.st_size, + interp_image, true); + assert(obj != nullptr); + result->interpreter = dynamic_cast<ElfObject*>(obj); + assert(result->interpreter != nullptr); + break; + } + } + elf_end(elf); return result; } @@ -252,8 +295,10 @@ ElfObject::tryFile(const string &fname, size_t len, uint8_t *data) ElfObject::ElfObject(const string &_filename, size_t _len, uint8_t *_data, Arch _arch, OpSys _opSys) : ObjectFile(_filename, _len, _data, _arch, _opSys), - _programHeaderTable(0), _programHeaderSize(0), _programHeaderCount(0) - + _programHeaderTable(0), _programHeaderSize(0), _programHeaderCount(0), + interpreter(nullptr), ldBias(0), relocate(true), + ldMin(std::numeric_limits<Addr>::max()), + ldMax(std::numeric_limits<Addr>::min()) { Elf *elf; GElf_Ehdr ehdr; @@ -326,6 +371,9 @@ ElfObject::ElfObject(const string &_filename, size_t _len, uint8_t *_data, if (!(phdr.p_type & PT_LOAD)) continue; + ldMin = std::min(ldMin, phdr.p_vaddr); + ldMax = std::max(ldMax, phdr.p_vaddr + phdr.p_memsz); + // Check to see if this segment contains the bss section. if (phdr.p_paddr <= bssSecStart && phdr.p_paddr + phdr.p_memsz > bssSecStart && @@ -338,6 +386,11 @@ ElfObject::ElfObject(const string &_filename, size_t _len, uint8_t *_data, // Check to see if this is the text or data segment if (phdr.p_vaddr <= textSecStart && phdr.p_vaddr + phdr.p_filesz > textSecStart) { + + // If this value is nonzero, we need to flip the relocate flag. + if (phdr.p_vaddr != 0) + relocate = false; + text.baseAddr = phdr.p_paddr; text.size = phdr.p_filesz; text.fileImage = fileData + phdr.p_offset; @@ -462,6 +515,10 @@ ElfObject::loadSections(PortProxy& memProxy, Addr addrMask, Addr offset) return false; } } + + if (interpreter) + interpreter->loadSections(memProxy, addrMask, offset); + return true; } @@ -510,3 +567,19 @@ ElfObject::sectionExists(string sec) } +void +ElfObject::updateBias(Addr bias_addr) +{ + // Record the bias. + ldBias = bias_addr; + + // Patch the entry point with bias_addr. + entry += bias_addr; + + // Patch segments with the bias_addr. + text.baseAddr += bias_addr; + data.baseAddr += bias_addr; + bss.baseAddr += bias_addr; + for (auto &segment : extraSegments) + segment.baseAddr += bias_addr; +} diff --git a/src/base/loader/elf_object.hh b/src/base/loader/elf_object.hh index 969896290..cec20a47a 100644 --- a/src/base/loader/elf_object.hh +++ b/src/base/loader/elf_object.hh @@ -62,6 +62,24 @@ class ElfObject : public ObjectFile uint16_t _programHeaderCount; std::set<std::string> sectionNames; + ElfObject *interpreter; + + // An interpreter load bias is the location in the process address space + // where the interpreter is chosen to reside. Typically, this is carved + // out of the top of the mmap reserve section. + Addr ldBias; + + // The interpreter is typically a relocatable shared library and will + // have a default value of zero which means that it does not care where + // it is placed. However, the loader can be compiled and linked so that + // it does care and needs a specific entry point. + bool relocate; + + // The ldMin and ldMax fields are required to know how large of an + // area is required to map the interpreter. + Addr ldMin; + Addr ldMax; + /// Helper functions for loadGlobalSymbols() and loadLocalSymbols(). bool loadSomeSymbols(SymbolTable *symtab, int binding, Addr mask); @@ -78,19 +96,26 @@ class ElfObject : public ObjectFile bool loadSections(PortProxy& memProxy, Addr addrMask = std::numeric_limits<Addr>::max(), - Addr offset = 0); + Addr offset = 0) override; virtual bool loadGlobalSymbols(SymbolTable *symtab, Addr addrMask = - std::numeric_limits<Addr>::max()); + std::numeric_limits<Addr>::max()) override; virtual bool loadLocalSymbols(SymbolTable *symtab, Addr addrMask = - std::numeric_limits<Addr>::max()); + std::numeric_limits<Addr>::max()) override; virtual bool loadWeakSymbols(SymbolTable *symtab, Addr addrMask = - std::numeric_limits<Addr>::max()); + std::numeric_limits<Addr>::max()) override; + + virtual ObjectFile *getInterpreter() const override + { return interpreter; } + virtual Addr bias() const override { return ldBias; } + virtual bool relocatable() const override { return relocate; } + virtual Addr mapSize() const override { return ldMax - ldMin; } + virtual void updateBias(Addr bias_addr) override; - virtual bool isDynamic() { return sectionExists(".interp"); } - virtual bool hasTLS() { return sectionExists(".tbss"); } + virtual bool hasTLS() override { return sectionExists(".tbss"); } static ObjectFile *tryFile(const std::string &fname, - size_t len, uint8_t *data); + size_t len, uint8_t *data, + bool skip_interp_check = false); Addr programHeaderTable() {return _programHeaderTable;} uint16_t programHeaderSize() {return _programHeaderSize;} uint16_t programHeaderCount() {return _programHeaderCount;} diff --git a/src/base/loader/object_file.hh b/src/base/loader/object_file.hh index e5961add2..26750038f 100644 --- a/src/base/loader/object_file.hh +++ b/src/base/loader/object_file.hh @@ -35,6 +35,7 @@ #include <limits> #include <string> +#include "base/misc.hh" #include "base/types.hh" class PortProxy; @@ -94,7 +95,14 @@ class ObjectFile std::numeric_limits<Addr>::max()) { return false; } - virtual bool isDynamic() { return false; } + virtual ObjectFile *getInterpreter() const { return nullptr; } + virtual bool relocatable() const { return false; } + virtual Addr mapSize() const + { panic("mapSize() should only be called on relocatable objects\n"); } + virtual void updateBias(Addr bias_addr) + { panic("updateBias() should only be called on relocatable objects\n"); } + virtual Addr bias() const { return 0; } + virtual bool hasTLS() { return false; } Arch getArch() const { return arch; } |