void
AlphaLiveProcess::argsInit(int intSize, int pageSize)
{
+ // Patch the ld_bias for dynamic executables.
+ updateBias();
+
objFile->loadSections(initVirtMem);
typedef AuxVector<uint64_t> auxv_t;
auxv.push_back(auxv_t(M5_AT_PHDR, elfObject->programHeaderTable()));
DPRINTF(Loader, "auxv at PHDR %08p\n", elfObject->programHeaderTable());
auxv.push_back(auxv_t(M5_AT_PHNUM, elfObject->programHeaderCount()));
+ // This is the base address of the ELF interpreter; it should be
+ // zero for static executables or contain the base address for
+ // dynamic executables.
+ auxv.push_back(auxv_t(M5_AT_BASE, getBias()));
auxv.push_back(auxv_t(M5_AT_ENTRY, objFile->entryPoint()));
auxv.push_back(auxv_t(M5_AT_UID, uid()));
auxv.push_back(auxv_t(M5_AT_EUID, euid()));
setSyscallArg(tc, 1, argv_array_base);
tc->setIntReg(StackPointerReg, stack_min);
- tc->pcState(objFile->entryPoint());
+ tc->pcState(getStartPC());
}
void
//We want 16 byte alignment
uint64_t align = 16;
+ // Patch the ld_bias for dynamic executables.
+ updateBias();
+
// load object file into target memory
objFile->loadSections(initVirtMem);
auxv.push_back(auxv_t(M5_AT_PHENT, elfObject->programHeaderSize()));
// This is the number of program headers from the original elf file.
auxv.push_back(auxv_t(M5_AT_PHNUM, elfObject->programHeaderCount()));
- //This is the address of the elf "interpreter", It should be set
- //to 0 for regular executables. It should be something else
- //(not sure what) for dynamic libraries.
- auxv.push_back(auxv_t(M5_AT_BASE, 0));
+ // This is the base address of the ELF interpreter; it should be
+ // zero for static executables or contain the base address for
+ // dynamic executables.
+ auxv.push_back(auxv_t(M5_AT_BASE, getBias()));
//XXX Figure out what this should be.
auxv.push_back(auxv_t(M5_AT_FLAGS, 0));
//The entry point to the program
pc.nextThumb(pc.thumb());
pc.aarch64(arch == ObjectFile::Arm64);
pc.nextAArch64(pc.aarch64());
- pc.set(objFile->entryPoint() & ~mask(1));
+ pc.set(getStartPC() & ~mask(1));
tc->pcState(pc);
//Align the "stack_min" to a page boundary.
{
int intSize = sizeof(IntType);
+ // Patch the ld_bias for dynamic executables.
+ updateBias();
+
// load object file into target memory
objFile->loadSections(initVirtMem);
auxv.push_back(auxv_t(M5_AT_PHENT, elfObject->programHeaderSize()));
// This is the number of program headers from the original elf file.
auxv.push_back(auxv_t(M5_AT_PHNUM, elfObject->programHeaderCount()));
+ // This is the base address of the ELF interpreter; it should be
+ // zero for static executables or contain the base address for
+ // dynamic executables.
+ auxv.push_back(auxv_t(M5_AT_BASE, getBias()));
//The entry point to the program
auxv.push_back(auxv_t(M5_AT_ENTRY, objFile->entryPoint()));
//Different user and group IDs
setSyscallArg(tc, 1, argv_array_base);
tc->setIntReg(StackPointerReg, stack_min);
- tc->pcState(objFile->entryPoint());
+ tc->pcState(getStartPC());
}
//We want 16 byte alignment
uint64_t align = 16;
+ // Patch the ld_bias for dynamic executables.
+ updateBias();
+
// load object file into target memory
objFile->loadSections(initVirtMem);
auxv.push_back(auxv_t(M5_AT_PHENT, elfObject->programHeaderSize()));
// This is the number of program headers from the original elf file.
auxv.push_back(auxv_t(M5_AT_PHNUM, elfObject->programHeaderCount()));
- //This is the address of the elf "interpreter", It should be set
- //to 0 for regular executables. It should be something else
- //(not sure what) for dynamic libraries.
- auxv.push_back(auxv_t(M5_AT_BASE, 0));
-
+ // This is the base address of the ELF interpreter; it should be
+ // zero for static executables or contain the base address for
+ // dynamic executables.
+ auxv.push_back(auxv_t(M5_AT_BASE, getBias()));
//XXX Figure out what this should be.
auxv.push_back(auxv_t(M5_AT_FLAGS, 0));
//The entry point to the program
//Set the stack pointer register
tc->setIntReg(StackPointerReg, stack_min);
- tc->pcState(objFile->entryPoint());
+ tc->pcState(getStartPC());
//Align the "stack_min" to a page boundary.
stack_min = roundDown(stack_min, pageSize);
// maintain double word alignment of the stack pointer.
uint64_t align = 16;
+ // Patch the ld_bias for dynamic executables.
+ updateBias();
+
// load object file into target memory
objFile->loadSections(initVirtMem);
auxv.push_back(auxv_t(M5_AT_PHENT, elfObject->programHeaderSize()));
// This is the number of program headers from the original elf file.
auxv.push_back(auxv_t(M5_AT_PHNUM, elfObject->programHeaderCount()));
- // This is the address of the elf "interpreter", It should be set
- // to 0 for regular executables. It should be something else
- // (not sure what) for dynamic libraries.
- auxv.push_back(auxv_t(M5_AT_BASE, 0));
+ // This is the base address of the ELF interpreter; it should be
+ // zero for static executables or contain the base address for
+ // dynamic executables.
+ auxv.push_back(auxv_t(M5_AT_BASE, getBias()));
// This is hardwired to 0 in the elf loading code in the kernel
auxv.push_back(auxv_t(M5_AT_FLAGS, 0));
// The entry point to the program
// don't have anything like that, it should be set to 0.
tc->setIntReg(1, 0);
- tc->pcState(objFile->entryPoint());
+ tc->pcState(getStartPC());
// Align the "stack_min" to a page boundary.
stack_min = roundDown(stack_min, pageSize);
//We want 16 byte alignment
uint64_t align = 16;
+ // Patch the ld_bias for dynamic executables.
+ updateBias();
+
// load object file into target memory
objFile->loadSections(initVirtMem);
X86_IA64Processor = 1 << 30
};
- // Setup the auxilliary vectors. These will already have endian conversion.
- // Auxilliary vectors are loaded only for elf formatted executables.
+ // Setup the auxiliary vectors. These will already have endian
+ // conversion. Auxiliary vectors are loaded only for elf formatted
+ // executables; the auxv is responsible for passing information from
+ // the OS to the interpreter.
ElfObject * elfObject = dynamic_cast<ElfObject *>(objFile);
if (elfObject) {
uint64_t features =
//Frequency at which times() increments
//Defined to be 100 in the kernel source.
auxv.push_back(auxv_t(M5_AT_CLKTCK, 100));
- // For statically linked executables, this is the virtual address of the
- // program header tables if they appear in the executable image
+ // This is the virtual address of the program header tables if they
+ // appear in the executable image.
auxv.push_back(auxv_t(M5_AT_PHDR, elfObject->programHeaderTable()));
// This is the size of a program header entry from the elf file.
auxv.push_back(auxv_t(M5_AT_PHENT, elfObject->programHeaderSize()));
// This is the number of program headers from the original elf file.
auxv.push_back(auxv_t(M5_AT_PHNUM, elfObject->programHeaderCount()));
- //This is the address of the elf "interpreter", It should be set
- //to 0 for regular executables. It should be something else
- //(not sure what) for dynamic libraries.
- auxv.push_back(auxv_t(M5_AT_BASE, 0));
-
+ // This is the base address of the ELF interpreter; it should be
+ // zero for static executables or contain the base address for
+ // dynamic executables.
+ auxv.push_back(auxv_t(M5_AT_BASE, getBias()));
//XXX Figure out what this should be.
auxv.push_back(auxv_t(M5_AT_FLAGS, 0));
//The entry point to the program
// There doesn't need to be any segment base added in since we're dealing
// with the flat segmentation model.
- tc->pcState(objFile->entryPoint());
+ tc->pcState(getStartPC());
//Align the "stack_min" to a page boundary.
stack_min = roundDown(stack_min, pageSize);
* 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;
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;
}
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;
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 &&
// 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;
return false;
}
}
+
+ if (interpreter)
+ interpreter->loadSections(memProxy, addrMask, offset);
+
return true;
}
}
+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;
+}
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);
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;}
#include <limits>
#include <string>
+#include "base/misc.hh"
#include "base/types.hh"
class PortProxy;
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; }
return NULL;
}
+void
+LiveProcess::updateBias()
+{
+ ObjectFile *interp = objFile->getInterpreter();
+
+ if (!interp || !interp->relocatable())
+ return;
+
+ // Determine how large the interpreters footprint will be in the process
+ // address space.
+ Addr interp_mapsize = roundUp(interp->mapSize(), TheISA::PageBytes);
+
+ // We are allocating the memory area; set the bias to the lowest address
+ // in the allocated memory region.
+ Addr ld_bias = mmapGrowsDown() ? mmap_end - interp_mapsize : mmap_end;
+
+ // Adjust the process mmap area to give the interpreter room; the real
+ // execve system call would just invoke the kernel's internal mmap
+ // functions to make these adjustments.
+ mmap_end = mmapGrowsDown() ? ld_bias : mmap_end + interp_mapsize;
+
+ interp->updateBias(ld_bias);
+}
+
+
+Addr
+LiveProcess::getBias()
+{
+ ObjectFile *interp = objFile->getInterpreter();
+
+ return interp ? interp->bias() : objFile->bias();
+}
+
+
+Addr
+LiveProcess::getStartPC()
+{
+ ObjectFile *interp = objFile->getInterpreter();
+
+ return interp ? interp->entryPoint() : objFile->entryPoint();
+}
+
LiveProcess *
LiveProcess::create(LiveProcessParams * params)
fatal("Can't load object file %s", params->executable);
}
- if (objFile->isDynamic())
- fatal("Object file is a dynamic executable however only static "
- "executables are supported!\n Please recompile your "
- "executable as a static binary and try again.\n");
-
#if THE_ISA == ALPHA_ISA
if (objFile->getArch() != ObjectFile::Alpha)
fatal("Object file architecture does not match compiled ISA (Alpha).");
*/
EmulatedDriver *findDriver(std::string filename);
+ // This function acts as a callback to update the bias value in
+ // the object file because the parameters needed to calculate the
+ // bias are not available when the object file is created.
+ void updateBias();
+
+ Addr getBias();
+ Addr getStartPC();
+
// this function is used to create the LiveProcess object, since
// we can't tell which subclass of LiveProcess to use until we
// open and look at the object file.