summaryrefslogtreecommitdiff
path: root/src/base/loader
diff options
context:
space:
mode:
authorBrandon Potter <brandon.potter@amd.com>2016-03-17 10:31:03 -0700
committerBrandon Potter <brandon.potter@amd.com>2016-03-17 10:31:03 -0700
commit9b4249410ec18cac9df2c7e9c0a4a6ce5459233d (patch)
treec3260ef4f23b9eca7d835ab1e0dfc8ce1173b17c /src/base/loader
parent4fc69db8f89049a881a5f4aa68545840818b124c (diff)
downloadgem5-9b4249410ec18cac9df2c7e9c0a4a6ce5459233d.tar.xz
base: support dynamic loading of Linux ELF objects in SE mode
Diffstat (limited to 'src/base/loader')
-rw-r--r--src/base/loader/elf_object.cc85
-rw-r--r--src/base/loader/elf_object.hh39
-rw-r--r--src/base/loader/object_file.hh10
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; }