arch,base: Separate the idea of a memory image and object file.

A memory image can be described by an object file, but an object file
is more than a memory image. Also, it makes sense to manipulate a
memory image to, for instance, change how it's loaded into memory. That
takes on larger implications (relocations, the entry point, symbols,
etc.) when talking about the whole object file, and also modifies
aspects which may not need to change. For instance if an image needs
to be loaded into memory at addresses different from what's in the
object file, but other things like symbols need to stay unmodified.

Change-Id: Ia360405ffb2c1c48e0cc201ac0a0764357996a54
Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/21466
Tested-by: kokoro <noreply+kokoro@google.com>
Reviewed-by: Brandon Potter <Brandon.Potter@amd.com>
Maintainer: Gabe Black <gabeblack@google.com>
diff --git a/src/arch/alpha/process.cc b/src/arch/alpha/process.cc
index e266b92..9311a64 100644
--- a/src/arch/alpha/process.cc
+++ b/src/arch/alpha/process.cc
@@ -54,10 +54,10 @@
       objFile)
 {
     fatal_if(params->useArchPT, "Arch page tables not implemented.");
-    Addr brk_point = roundUp(objFile->maxSegmentAddr(), PageBytes);
+    Addr brk_point = roundUp(image.maxAddr(), PageBytes);
 
     // Set up stack.  On Alpha, stack goes below the image.
-    Addr stack_base = objFile->minSegmentAddr() - (409600 + 4096);
+    Addr stack_base = image.minAddr() - (409600 + 4096);
 
     // Set up region for mmaps.
     Addr mmap_end = 0x10000;
@@ -74,13 +74,6 @@
 void
 AlphaProcess::argsInit(int intSize, int pageSize)
 {
-    // Patch the ld_bias for dynamic executables.
-    updateBias();
-
-    objFile->loadSegments(initVirtMem);
-    if (objFile->getInterpreter())
-        objFile->getInterpreter()->loadSegments(initVirtMem);
-
     std::vector<AuxVector<uint64_t>>  auxv;
 
     ElfObject * elfObject = dynamic_cast<ElfObject *>(objFile);
diff --git a/src/arch/alpha/system.cc b/src/arch/alpha/system.cc
index 31e7aa0..a7a4703 100644
--- a/src/arch/alpha/system.cc
+++ b/src/arch/alpha/system.cc
@@ -57,13 +57,11 @@
      */
     // Load Console Code
     console = createObjectFile(params()->console);
-    console->setLoadMask(loadAddrMask);
     if (console == NULL)
         fatal("Could not load console file %s", params()->console);
 
     // Load pal file
     pal = createObjectFile(params()->pal);
-    pal->setLoadMask(loadAddrMask);
     if (pal == NULL)
         fatal("Could not load PALcode file %s", params()->pal);
 
@@ -111,8 +109,8 @@
     System::initState();
 
     // Load program sections into memory
-    pal->loadSegments(physProxy);
-    console->loadSegments(physProxy);
+    pal->buildImage().mask(loadAddrMask).write(physProxy);
+    console->buildImage().mask(loadAddrMask).write(physProxy);
 
     /**
      * Copy the osflags (kernel arguments) into the consoles
diff --git a/src/arch/arm/freebsd/system.cc b/src/arch/arm/freebsd/system.cc
index 764f036..106c8e4 100644
--- a/src/arch/arm/freebsd/system.cc
+++ b/src/arch/arm/freebsd/system.cc
@@ -132,8 +132,8 @@
     if (ra)
         bootReleaseAddr = ra & ~ULL(0x7F);
 
-    dtb_file->setLoadOffset(params()->atags_addr + loadAddrOffset);
-    dtb_file->loadSegments(physProxy);
+    dtb_file->buildImage().
+        offset(params()->atags_addr + loadAddrOffset).write(physProxy);
     delete dtb_file;
 
     // Kernel boot requirements to set up r0, r1 and r2 in ARMv7
diff --git a/src/arch/arm/linux/system.cc b/src/arch/arm/linux/system.cc
index a0869b4..740d2e0 100644
--- a/src/arch/arm/linux/system.cc
+++ b/src/arch/arm/linux/system.cc
@@ -151,8 +151,8 @@
                  "to DTB file: %s\n", params()->dtb_filename);
         }
 
-        dtb_file->setLoadOffset(params()->atags_addr + loadAddrOffset);
-        dtb_file->loadSegments(physProxy);
+        dtb_file->buildImage().
+            offset(params()->atags_addr + loadAddrOffset).write(physProxy);
         delete dtb_file;
     } else {
         // Using ATAGS
diff --git a/src/arch/arm/process.cc b/src/arch/arm/process.cc
index ff3b92f..d9b714c 100644
--- a/src/arch/arm/process.cc
+++ b/src/arch/arm/process.cc
@@ -75,7 +75,7 @@
                            ObjectFile::Arch _arch)
     : ArmProcess(params, objFile, _arch)
 {
-    Addr brk_point = roundUp(objFile->maxSegmentAddr(), PageBytes);
+    Addr brk_point = roundUp(image.maxAddr(), PageBytes);
     Addr stack_base = 0xbf000000L;
     Addr max_stack_size = 8 * 1024 * 1024;
     Addr next_thread_stack_base = stack_base - max_stack_size;
@@ -89,7 +89,7 @@
                            ObjectFile::Arch _arch)
     : ArmProcess(params, objFile, _arch)
 {
-    Addr brk_point = roundUp(objFile->maxSegmentAddr(), PageBytes);
+    Addr brk_point = roundUp(image.maxAddr(), PageBytes);
     Addr stack_base = 0x7fffff0000L;
     Addr max_stack_size = 8 * 1024 * 1024;
     Addr next_thread_stack_base = stack_base - max_stack_size;
@@ -266,14 +266,6 @@
     //We want 16 byte alignment
     uint64_t align = 16;
 
-    // Patch the ld_bias for dynamic executables.
-    updateBias();
-
-    // load object file into target memory
-    objFile->loadSegments(initVirtMem);
-    if (objFile->getInterpreter())
-        objFile->getInterpreter()->loadSegments(initVirtMem);
-
     //Setup the auxilliary vectors. These will already have endian conversion.
     //Auxilliary vectors are loaded only for elf formatted executables.
     ElfObject * elfObject = dynamic_cast<ElfObject *>(objFile);
diff --git a/src/arch/arm/system.cc b/src/arch/arm/system.cc
index efc347d..bf28810 100644
--- a/src/arch/arm/system.cc
+++ b/src/arch/arm/system.cc
@@ -143,7 +143,7 @@
 
     if (bootldr) {
         bool isGICv3System = dynamic_cast<Gicv3 *>(getGIC()) != nullptr;
-        bootldr->loadSegments(physProxy);
+        bootldr->buildImage().write(physProxy);
 
         inform("Using bootloader at address %#x\n", bootldr->entryPoint());
 
diff --git a/src/arch/mips/process.cc b/src/arch/mips/process.cc
index 4c4b0e4..8e1e22b 100644
--- a/src/arch/mips/process.cc
+++ b/src/arch/mips/process.cc
@@ -65,7 +65,7 @@
     Addr next_thread_stack_base = stack_base - max_stack_size;
 
     // Set up break point (Top of Heap)
-    Addr brk_point = objFile->maxSegmentAddr();
+    Addr brk_point = image.maxAddr();
     brk_point = roundUp(brk_point, PageBytes);
 
     // Set up region for mmaps.  Start it 1GB above the top of the heap.
@@ -89,14 +89,6 @@
 {
     int intSize = sizeof(IntType);
 
-    // Patch the ld_bias for dynamic executables.
-    updateBias();
-
-    // load object file into target memory
-    objFile->loadSegments(initVirtMem);
-    if (objFile->getInterpreter())
-        objFile->getInterpreter()->loadSegments(initVirtMem);
-
     std::vector<AuxVector<IntType>> auxv;
 
     ElfObject * elfObject = dynamic_cast<ElfObject *>(objFile);
diff --git a/src/arch/power/process.cc b/src/arch/power/process.cc
index 89b94b2..7b53d70 100644
--- a/src/arch/power/process.cc
+++ b/src/arch/power/process.cc
@@ -56,7 +56,7 @@
 {
     fatal_if(params->useArchPT, "Arch page tables not implemented.");
     // Set up break point (Top of Heap)
-    Addr brk_point = objFile->maxSegmentAddr();
+    Addr brk_point = image.maxAddr();
     brk_point = roundUp(brk_point, PageBytes);
 
     Addr stack_base = 0xbf000000L;
@@ -95,13 +95,9 @@
     //We want 16 byte alignment
     uint64_t align = 16;
 
-    // Patch the ld_bias for dynamic executables.
-    updateBias();
-
     // load object file into target memory
-    objFile->loadSegments(initVirtMem);
-    if (objFile->getInterpreter())
-        objFile->getInterpreter()->loadSegments(initVirtMem);
+    image.write(initVirtMem);
+    interpImage.write(initVirtMem);
 
     //Setup the auxilliary vectors. These will already have endian conversion.
     //Auxilliary vectors are loaded only for elf formatted executables.
diff --git a/src/arch/riscv/bare_metal/system.cc b/src/arch/riscv/bare_metal/system.cc
index 3fb07a4..44e14e5 100644
--- a/src/arch/riscv/bare_metal/system.cc
+++ b/src/arch/riscv/bare_metal/system.cc
@@ -55,7 +55,7 @@
     RiscvSystem::initState();
 
     // load program sections into memory
-    if (!bootloader->loadSegments(physProxy)) {
+    if (!bootloader->buildImage().write(physProxy)) {
         warn("could not load sections to memory");
     }
 }
diff --git a/src/arch/riscv/process.cc b/src/arch/riscv/process.cc
index 35dde76..617efa0 100644
--- a/src/arch/riscv/process.cc
+++ b/src/arch/riscv/process.cc
@@ -75,7 +75,7 @@
     const Addr stack_base = 0x7FFFFFFFFFFFFFFFL;
     const Addr max_stack_size = 8 * 1024 * 1024;
     const Addr next_thread_stack_base = stack_base - max_stack_size;
-    const Addr brk_point = roundUp(objFile->maxSegmentAddr(), PageBytes);
+    const Addr brk_point = roundUp(image.maxAddr(), PageBytes);
     const Addr mmap_end = 0x4000000000000000L;
     memState = make_shared<MemState>(brk_point, stack_base, max_stack_size,
             next_thread_stack_base, mmap_end);
@@ -87,7 +87,7 @@
     const Addr stack_base = 0x7FFFFFFF;
     const Addr max_stack_size = 8 * 1024 * 1024;
     const Addr next_thread_stack_base = stack_base - max_stack_size;
-    const Addr brk_point = roundUp(objFile->maxSegmentAddr(), PageBytes);
+    const Addr brk_point = roundUp(image.maxAddr(), PageBytes);
     const Addr mmap_end = 0x40000000L;
     memState = make_shared<MemState>(brk_point, stack_base, max_stack_size,
                                      next_thread_stack_base, mmap_end);
@@ -123,10 +123,6 @@
     const int RandomBytes = 16;
     const int addrSize = sizeof(IntType);
 
-    updateBias();
-    objFile->loadSegments(initVirtMem);
-    if (objFile->getInterpreter())
-        objFile->getInterpreter()->loadSegments(initVirtMem);
     ElfObject* elfObject = dynamic_cast<ElfObject*>(objFile);
     memState->setStackMin(memState->getStackBase());
 
diff --git a/src/arch/sparc/process.cc b/src/arch/sparc/process.cc
index 1c020c6..048c7e9 100644
--- a/src/arch/sparc/process.cc
+++ b/src/arch/sparc/process.cc
@@ -204,14 +204,6 @@
     // 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->loadSegments(initVirtMem);
-    if (objFile->getInterpreter())
-        objFile->getInterpreter()->loadSegments(initVirtMem);
-
     enum hardwareCaps
     {
         M5_HWCAP_SPARC_FLUSH = 1,
diff --git a/src/arch/sparc/process.hh b/src/arch/sparc/process.hh
index f8f19f2..0ef3435 100644
--- a/src/arch/sparc/process.hh
+++ b/src/arch/sparc/process.hh
@@ -78,7 +78,7 @@
     Sparc32Process(ProcessParams * params, ObjectFile *objFile)
         : SparcProcess(params, objFile, 0)
     {
-        Addr brk_point = objFile->maxSegmentAddr();
+        Addr brk_point = image.maxAddr();
         brk_point = roundUp(brk_point, SparcISA::PageBytes);
 
         // Reserve 8M for main stack.
@@ -122,7 +122,7 @@
     Sparc64Process(ProcessParams * params, ObjectFile *objFile)
         : SparcProcess(params, objFile, 2047)
     {
-        Addr brk_point = objFile->maxSegmentAddr();
+        Addr brk_point = image.maxAddr();
         brk_point = roundUp(brk_point, SparcISA::PageBytes);
 
         Addr max_stack_size = 8 * 1024 * 1024;
diff --git a/src/arch/sparc/system.cc b/src/arch/sparc/system.cc
index e9615b0..255cc14 100644
--- a/src/arch/sparc/system.cc
+++ b/src/arch/sparc/system.cc
@@ -136,25 +136,15 @@
     // Call the initialisation of the super class
     System::initState();
 
-    // Load reset binary into memory
-    reset->setLoadOffset(params()->reset_addr);
-    reset->loadSegments(physProxy);
-    // Load the openboot binary
-    openboot->setLoadOffset(params()->openboot_addr);
-    openboot->loadSegments(physProxy);
-    // Load the hypervisor binary
-    hypervisor->setLoadOffset(params()->hypervisor_addr);
-    hypervisor->loadSegments(physProxy);
-    // Load the nvram image
-    nvram->setLoadOffset(params()->nvram_addr);
-    nvram->loadSegments(physProxy);
-    // Load the hypervisor description image
-    hypervisor_desc->setLoadOffset(params()->hypervisor_desc_addr);
-    hypervisor_desc->loadSegments(physProxy);
-    // Load the partition description image
-    partition_desc->setLoadOffset(params()->partition_desc_addr);
-    partition_desc->loadSegments(physProxy);
-
+    reset->buildImage().offset(params()->reset_addr).write(physProxy);
+    openboot->buildImage().offset(params()->openboot_addr).write(physProxy);
+    hypervisor->buildImage().
+        offset(params()->hypervisor_addr).write(physProxy);
+    nvram->buildImage().offset(params()->nvram_addr).write(physProxy);
+    hypervisor_desc->buildImage().
+        offset(params()->hypervisor_desc_addr).write(physProxy);
+    partition_desc->buildImage().
+        offset(params()->partition_desc_addr).write(physProxy);
 
     // @todo any fixup code over writing data in binaries on setting break
     // events on functions should happen here.
diff --git a/src/arch/x86/process.cc b/src/arch/x86/process.cc
index 5464741..11b46c8 100644
--- a/src/arch/x86/process.cc
+++ b/src/arch/x86/process.cc
@@ -136,7 +136,7 @@
     vsyscallPage.vtimeOffset = 0x400;
     vsyscallPage.vgettimeofdayOffset = 0x0;
 
-    Addr brk_point = roundUp(objFile->maxSegmentAddr(), PageBytes);
+    Addr brk_point = roundUp(image.maxAddr(), PageBytes);
     Addr stack_base = 0x7FFFFFFFF000ULL;
     Addr max_stack_size = 8 * 1024 * 1024;
     Addr next_thread_stack_base = stack_base - max_stack_size;
@@ -175,7 +175,7 @@
     vsyscallPage.vsyscallOffset = 0x400;
     vsyscallPage.vsysexitOffset = 0x410;
 
-    Addr brk_point = roundUp(objFile->maxSegmentAddr(), PageBytes);
+    Addr brk_point = roundUp(image.maxAddr(), PageBytes);
     Addr stack_base = _gdtStart;
     Addr max_stack_size = 8 * 1024 * 1024;
     Addr next_thread_stack_base = stack_base - max_stack_size;
@@ -770,14 +770,6 @@
     // We want 16 byte alignment
     uint64_t align = 16;
 
-    // Patch the ld_bias for dynamic executables.
-    updateBias();
-
-    // load object file into target memory
-    objFile->loadSegments(initVirtMem);
-    if (objFile->getInterpreter())
-        objFile->getInterpreter()->loadSegments(initVirtMem);
-
     enum X86CpuFeature {
         X86_OnboardFPU = 1 << 0,
         X86_VirtualModeExtensions = 1 << 1,
diff --git a/src/base/SConscript b/src/base/SConscript
index 0c7558d..efa7df7 100644
--- a/src/base/SConscript
+++ b/src/base/SConscript
@@ -76,6 +76,7 @@
 Source('loader/dtb_object.cc')
 Source('loader/ecoff_object.cc')
 Source('loader/elf_object.cc')
+Source('loader/memory_image.cc')
 Source('loader/object_file.cc')
 Source('loader/raw_object.cc')
 Source('loader/symtab.cc')
diff --git a/src/base/loader/aout_object.cc b/src/base/loader/aout_object.cc
index 9765158..eb633c1 100644
--- a/src/base/loader/aout_object.cc
+++ b/src/base/loader/aout_object.cc
@@ -58,17 +58,23 @@
     : ObjectFile(_filename, _len, _data, _arch, _opSys)
 {
     execHdr = (aout_exechdr *)fileData;
-
     entry = execHdr->entry;
+}
 
-    addSegment("text", N_TXTADDR(*execHdr), fileData + N_TXTOFF(*execHdr),
-            execHdr->tsize);
-    addSegment("data", N_DATADDR(*execHdr), fileData + N_DATOFF(*execHdr),
-            execHdr->dsize);
-    addSegment("bss", N_BSSADDR(*execHdr), nullptr, execHdr->bsize);
+MemoryImage
+AoutObject::buildImage() const
+{
+    MemoryImage image({
+            { "text", N_TXTADDR(*execHdr),
+              fileData + N_TXTOFF(*execHdr), execHdr->tsize },
+            { "data", N_DATADDR(*execHdr),
+              fileData + N_DATOFF(*execHdr), execHdr->dsize },
+            { "bss", N_BSSADDR(*execHdr), nullptr, execHdr->bsize}
+    });
 
-    for (auto &seg: segments)
-        DPRINTFR(Loader, "%s\n", *seg);
+    for (auto &seg: image.segments())
+        DPRINTFR(Loader, "%s\n", seg);
+    return image;
 }
 
 
diff --git a/src/base/loader/aout_object.hh b/src/base/loader/aout_object.hh
index 65ed7ca..4f8c86f 100644
--- a/src/base/loader/aout_object.hh
+++ b/src/base/loader/aout_object.hh
@@ -48,13 +48,14 @@
   public:
     virtual ~AoutObject() {}
 
-    virtual bool loadAllSymbols(SymbolTable *symtab, Addr base = 0,
-                                Addr offset = 0, Addr addr_mask = maxAddr);
-    virtual bool loadGlobalSymbols(SymbolTable *symtab, Addr base = 0,
-                                   Addr offset = 0,
-                                   Addr addr_mask = maxAddr);
-    virtual bool loadLocalSymbols(SymbolTable *symtab, Addr base = 0,
-                                  Addr offset = 0, Addr addr_mask = maxAddr);
+    MemoryImage buildImage() const override;
+
+    bool loadAllSymbols(SymbolTable *symtab, Addr base=0,
+                        Addr offset=0, Addr addr_mask = MaxAddr) override;
+    bool loadGlobalSymbols(SymbolTable *symtab, Addr base=0,
+                           Addr offset=0, Addr addr_mask=MaxAddr) override;
+    bool loadLocalSymbols(SymbolTable *symtab, Addr base=0,
+                          Addr offset=0, Addr addr_mask=MaxAddr) override;
 
     static ObjectFile *tryFile(const std::string &fname,
                                size_t len, uint8_t *data);
diff --git a/src/base/loader/dtb_object.cc b/src/base/loader/dtb_object.cc
index 275139a..f5b206a 100644
--- a/src/base/loader/dtb_object.cc
+++ b/src/base/loader/dtb_object.cc
@@ -53,10 +53,8 @@
 
 DtbObject::DtbObject(const std::string &_filename, size_t _len, uint8_t *_data,
                      Arch _arch, OpSys _opSys)
-    : ObjectFile(_filename, _len, _data, _arch, _opSys),
-    data(new Segment{ "data", 0, fileData, len })
+    : ObjectFile(_filename, _len, _data, _arch, _opSys)
 {
-    segments.emplace_back(data);
     fileDataMmapped = true;
 }
 
@@ -131,9 +129,6 @@
         return false;
     }
 
-    data->size = newLen;
-    data->data = fdt_buf_w_space;
-
     // clean up old buffer and set to new fdt blob
     munmap(fileData, this->len);
     fileData = fdt_buf_w_space;
@@ -164,6 +159,12 @@
     return rel_addr;
 }
 
+MemoryImage
+DtbObject::buildImage() const
+{
+    return {{"data", 0, fileData, len}};
+}
+
 bool
 DtbObject::loadAllSymbols(SymbolTable *symtab, Addr base, Addr offset,
                           Addr addr_mask)
diff --git a/src/base/loader/dtb_object.hh b/src/base/loader/dtb_object.hh
index 7cb842e..1284025 100644
--- a/src/base/loader/dtb_object.hh
+++ b/src/base/loader/dtb_object.hh
@@ -40,50 +40,50 @@
  */
 class DtbObject : public ObjectFile
 {
-    protected:
-        DtbObject(const std::string &_filename, size_t _len, uint8_t *_data,
-                  Arch _arch, OpSys _opSys);
+  protected:
+    DtbObject(const std::string &_filename, size_t _len, uint8_t *_data,
+              Arch _arch, OpSys _opSys);
 
-        /** Bool marking if this dtb file has replaced the original
-         *  read in DTB file with a new modified buffer
-         */
-        bool fileDataMmapped;
+    /** Bool marking if this dtb file has replaced the original
+     *  read in DTB file with a new modified buffer
+     */
+    bool fileDataMmapped;
 
-        Segment *data;
+  public:
+    virtual ~DtbObject();
 
-    public:
-        virtual ~DtbObject();
+    /** Adds the passed in Command Line options for the kernel
+      * to the proper location in the device tree.
+      * @param _args command line to append
+      * @param len length of the command line string
+      * @return returns true on success, false otherwise
+      */
+    bool addBootCmdLine(const char* _args, size_t len);
 
-        /** Adds the passed in Command Line options for the kernel
-          * to the proper location in the device tree.
-          * @param _args command line to append
-          * @param len length of the command line string
-          * @return returns true on success, false otherwise
-          */
-        bool addBootCmdLine(const char* _args, size_t len);
+    /** Parse the DTB file enough to find the provided release
+     * address and return it.
+     * @return release address for SMP boot
+     */
+    Addr findReleaseAddr();
 
-        /** Parse the DTB file enough to find the provided release
-         * address and return it.
-         * @return release address for SMP boot
-         */
-        Addr findReleaseAddr();
+    MemoryImage buildImage() const override;
 
-        bool loadAllSymbols(SymbolTable *symtab, Addr base = 0,
-                            Addr offset = 0, Addr addrMask = maxAddr);
-        bool loadGlobalSymbols(SymbolTable *symtab, Addr base = 0,
-                               Addr offset = 0, Addr addrMask = maxAddr);
-        bool loadLocalSymbols(SymbolTable *symtab, Addr base = 0,
-                              Addr offset = 0, Addr addrMask = maxAddr);
+    bool loadAllSymbols(SymbolTable *symtab, Addr base=0,
+                        Addr offset=0, Addr addrMask=MaxAddr) override;
+    bool loadGlobalSymbols(SymbolTable *symtab, Addr base=0,
+                           Addr offset=0, Addr addrMask=MaxAddr) override;
+    bool loadLocalSymbols(SymbolTable *symtab, Addr base=0,
+                          Addr offset=0, Addr addrMask=MaxAddr) override;
 
-        /** Static function that tries to load file as a
-          * flattened device tree blob.
-          * @param fname path to file
-          * @param len length of file
-          * @param data mmap'ed data buffer containing file contents
-          * @return ObjectFile representing closest match of file type
-          */
-        static ObjectFile *tryFile(const std::string &fname,
-                                   size_t len, uint8_t *data);
+    /** Static function that tries to load file as a
+      * flattened device tree blob.
+      * @param fname path to file
+      * @param len length of file
+      * @param data mmap'ed data buffer containing file contents
+      * @return ObjectFile representing closest match of file type
+      */
+    static ObjectFile *tryFile(const std::string &fname,
+                               size_t len, uint8_t *data);
 };
 
 #endif //__DTB_OBJECT_HH__
diff --git a/src/base/loader/ecoff_object.cc b/src/base/loader/ecoff_object.cc
index 56f9b35..cecc68d 100644
--- a/src/base/loader/ecoff_object.cc
+++ b/src/base/loader/ecoff_object.cc
@@ -72,15 +72,23 @@
     aoutHdr = &(execHdr->a);
 
     entry = aoutHdr->entry;
+}
 
-    addSegment("text", aoutHdr->text_start, fileData + ECOFF_TXTOFF(execHdr),
-            aoutHdr->tsize);
-    addSegment("data", aoutHdr->data_start, fileData + ECOFF_DATOFF(execHdr),
-            aoutHdr->dsize);
-    addSegment("bss", aoutHdr->bss_start, nullptr, aoutHdr->bsize);
+MemoryImage
+EcoffObject::buildImage() const
+{
+    MemoryImage image({
+            { "text", aoutHdr->text_start,
+              fileData + ECOFF_TXTOFF(execHdr), aoutHdr->tsize },
+            { "data", aoutHdr->data_start,
+              fileData + ECOFF_DATOFF(execHdr), aoutHdr->dsize },
+            { "bss", aoutHdr->bss_start, nullptr, aoutHdr->bsize }
+    });
 
-    for (auto &seg: segments)
-        DPRINTFR(Loader, "%s\n", *seg);
+    for (auto &seg: image.segments())
+        DPRINTFR(Loader, "%s\n", seg);
+
+    return image;
 }
 
 bool
diff --git a/src/base/loader/ecoff_object.hh b/src/base/loader/ecoff_object.hh
index b1ae911..9481115 100644
--- a/src/base/loader/ecoff_object.hh
+++ b/src/base/loader/ecoff_object.hh
@@ -51,12 +51,14 @@
   public:
     virtual ~EcoffObject() {}
 
-    virtual bool loadAllSymbols(SymbolTable *symtab, Addr base = 0,
-                                Addr offset = 0, Addr addr_mask = maxAddr);
-    virtual bool loadGlobalSymbols(SymbolTable *symtab, Addr base = 0,
-                                  Addr offset = 0, Addr addr_mask = maxAddr);
-    virtual bool loadLocalSymbols(SymbolTable *symtab, Addr base = 0,
-                                  Addr offset = 0, Addr addr_mask = maxAddr);
+    MemoryImage buildImage() const override;
+
+    bool loadAllSymbols(SymbolTable *symtab, Addr base=0,
+                        Addr offset=0, Addr addr_mask=MaxAddr) override;
+    bool loadGlobalSymbols(SymbolTable *symtab, Addr base=0,
+                           Addr offset=0, Addr addr_mask=MaxAddr) override;
+    bool loadLocalSymbols(SymbolTable *symtab, Addr base=0,
+                          Addr offset=0, Addr addr_mask=MaxAddr) override;
 
     static ObjectFile *tryFile(const std::string &fname,
                                size_t len, uint8_t *data);
diff --git a/src/base/loader/elf_object.cc b/src/base/loader/elf_object.cc
index 4cf2097..7a83c0b 100644
--- a/src/base/loader/elf_object.cc
+++ b/src/base/loader/elf_object.cc
@@ -339,26 +339,25 @@
             section = elf_getscn(elf, ++sec_idx);
         }
 
-        addSegment(name, phdr.p_paddr, fileData + phdr.p_offset,
-                phdr.p_filesz);
+        image.addSegment(name, phdr.p_paddr, fileData + phdr.p_offset,
+                         phdr.p_filesz);
         Addr uninitialized = phdr.p_memsz - phdr.p_filesz;
         if (uninitialized) {
             // There may be parts of a segment which aren't included in the
             // file. In those cases, we need to create a new segment with no
             // data to take up the extra space. This should be zeroed when
             // loaded into memory.
-            addSegment(name + "(uninitialized)", phdr.p_paddr + phdr.p_filesz,
-                    nullptr, uninitialized);
+            image.addSegment(name + "(uninitialized)",
+                    phdr.p_paddr + phdr.p_filesz, nullptr, uninitialized);
         }
     }
 
     // should have found at least one loadable segment
-    warn_if(segments.empty(),
-            "No loadable segments in '%s'. ELF file corrupted?\n",
-            filename);
+    warn_if(image.segments().empty(),
+            "No loadable segments in '%s'. ELF file corrupted?\n", filename);
 
-    for (auto &seg: segments)
-        DPRINTFR(Loader, "%s\n", *seg);
+    for (auto &seg: image.segments())
+        DPRINTFR(Loader, "%s\n", seg);
 
     elf_end(elf);
 
@@ -518,6 +517,5 @@
     entry += bias_addr;
 
     // Patch segments with the bias_addr.
-    for (auto &segment : segments)
-        segment->base += bias_addr;
+    image.offset(bias_addr);
 }
diff --git a/src/base/loader/elf_object.hh b/src/base/loader/elf_object.hh
index dbefadd..0b8c79b 100644
--- a/src/base/loader/elf_object.hh
+++ b/src/base/loader/elf_object.hh
@@ -86,34 +86,30 @@
     void getSections();
     bool sectionExists(std::string sec);
 
+    MemoryImage image;
+
   public:
     virtual ~ElfObject() {}
 
-    virtual bool loadAllSymbols(SymbolTable *symtab, Addr base = 0,
-                                Addr offset = 0, Addr addr_mask = maxAddr)
-                                override;
+    MemoryImage buildImage() const override { return image; }
 
-    virtual bool loadGlobalSymbols(SymbolTable *symtab, Addr base = 0,
-                                   Addr offset = 0, Addr addr_mask = maxAddr)
-                                   override;
-
-    virtual bool loadLocalSymbols(SymbolTable *symtab, Addr base = 0,
-                                  Addr offset = 0, Addr addr_mask = maxAddr)
-                                  override;
-
-    virtual bool loadWeakSymbols(SymbolTable *symtab, Addr base = 0,
-                                 Addr offset = 0, Addr addr_mask = maxAddr)
-                                 override;
+    bool loadAllSymbols(SymbolTable *symtab, Addr base=0,
+                        Addr offset=0, Addr addr_mask=MaxAddr) override;
+    bool loadGlobalSymbols(SymbolTable *symtab, Addr base=0,
+                           Addr offset=0, Addr addr_mask=MaxAddr) override;
+    bool loadLocalSymbols(SymbolTable *symtab, Addr base=0,
+                          Addr offset=0, Addr addr_mask=MaxAddr) override;
+    bool loadWeakSymbols(SymbolTable *symtab, Addr base=0,
+                         Addr offset=0, Addr addr_mask=MaxAddr) 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;
+    ObjectFile *getInterpreter() const override { return interpreter; }
+    Addr bias() const override { return ldBias; }
+    bool relocatable() const override { return relocate; }
+    Addr mapSize() const override { return ldMax - ldMin; }
+    void updateBias(Addr bias_addr) override;
 
-    virtual bool hasTLS() override { return sectionExists(".tbss"); }
+    bool hasTLS() override { return sectionExists(".tbss"); }
 
     static ObjectFile *tryFile(const std::string &fname,
                                size_t len, uint8_t *data,
diff --git a/src/base/loader/memory_image.cc b/src/base/loader/memory_image.cc
new file mode 100644
index 0000000..6fa70c9
--- /dev/null
+++ b/src/base/loader/memory_image.cc
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2002-2004 The Regents of The University of Michigan
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Nathan Binkert
+ *          Steve Reinhardt
+ */
+
+#include "base/loader/memory_image.hh"
+#include "mem/port_proxy.hh"
+
+bool
+MemoryImage::writeSegment(const Segment &seg, const PortProxy &proxy) const
+{
+    if (seg.size != 0) {
+        if (seg.data) {
+            proxy.writeBlob(seg.base, seg.data, seg.size);
+        } else {
+            // no image: must be bss
+            proxy.memsetBlob(seg.base, 0, seg.size);
+        }
+    }
+    return true;
+}
+
+bool
+MemoryImage::write(const PortProxy &proxy) const
+{
+    for (auto &seg: _segments)
+        if (!writeSegment(seg, proxy))
+            return false;
+    return true;
+}
+
+MemoryImage &
+MemoryImage::move(std::function<Addr(Addr)> mapper)
+{
+    for (auto &seg: _segments)
+        seg.base = mapper(seg.base);
+    return *this;
+}
diff --git a/src/base/loader/memory_image.hh b/src/base/loader/memory_image.hh
new file mode 100644
index 0000000..866e956
--- /dev/null
+++ b/src/base/loader/memory_image.hh
@@ -0,0 +1,146 @@
+/*
+ * Copyright (c) 2002-2004 The Regents of The University of Michigan
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Nathan Binkert
+ *          Steve Reinhardt
+ */
+
+#ifndef __BASE_LOADER_MEMORY_IMAGE_HH__
+#define __BASE_LOADER_MEMORY_IMAGE_HH__
+
+#include <functional>
+#include <initializer_list>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/logging.hh"
+#include "base/types.hh"
+
+class PortProxy;
+class Process;
+class ProcessParams;
+class SymbolTable;
+
+class MemoryImage
+{
+  public:
+    struct Segment
+    {
+        std::string name;
+        Addr base;
+        uint8_t *data;
+        size_t size;
+    };
+
+    MemoryImage() {}
+
+    MemoryImage(std::initializer_list<Segment> new_segs)
+    {
+        for (auto &seg: new_segs)
+            addSegment(seg);
+    }
+
+  private:
+    std::vector<Segment> _segments;
+    bool writeSegment(const Segment &seg, const PortProxy &proxy) const;
+
+  public:
+    const std::vector<Segment> &
+    segments() const
+    {
+        return _segments;
+    }
+
+    void
+    addSegment(const Segment &seg)
+    {
+        _segments.emplace_back(seg);
+    }
+
+    void
+    addSegment(std::string name, Addr base, uint8_t *data, size_t size)
+    {
+        _segments.push_back(Segment({name, base, data, size}));
+    }
+
+    bool write(const PortProxy &proxy) const;
+    MemoryImage &move(std::function<Addr(Addr)> mapper);
+    MemoryImage &
+    offset(Addr by)
+    {
+        return move([by](Addr a){ return by + a; });
+    }
+    MemoryImage &
+    mask(Addr m) {
+        return move([m](Addr a) { return a & m; });
+    }
+
+    Addr
+    maxAddr() const
+    {
+        Addr max = 0;
+        for (auto &seg: _segments) {
+            Addr end = seg.base + seg.size;
+            if (end > max)
+                max = end;
+        }
+        return max;
+    }
+
+    Addr
+    minAddr() const
+    {
+        Addr min = MaxAddr;
+        for (auto &seg: _segments)
+            if (seg.base < min)
+                min = seg.base;
+        return min;
+    }
+
+    bool
+    contains(Addr addr) const
+    {
+        for (auto &seg: _segments) {
+            Addr start = seg.base;
+            Addr end = seg.base + seg.size;
+            if (addr >= start && addr < end)
+                return true;
+        }
+        return false;
+    }
+};
+
+static inline std::ostream &
+operator << (std::ostream &os, const MemoryImage::Segment &seg)
+{
+    ccprintf(os, "%s: %#x %d", seg.name, seg.base, seg.size);
+    return os;
+}
+
+
+#endif // __BASE_LOADER_MEMORY_IMAGE_HH__
diff --git a/src/base/loader/object_file.cc b/src/base/loader/object_file.cc
index d63b222..afecd21 100644
--- a/src/base/loader/object_file.cc
+++ b/src/base/loader/object_file.cc
@@ -70,31 +70,6 @@
 }
 
 
-bool
-ObjectFile::loadSegment(Segment *seg, const PortProxy &mem_proxy)
-{
-    if (seg->size != 0) {
-        Addr addr = (seg->base & loadMask) + loadOffset;
-        if (seg->data) {
-            mem_proxy.writeBlob(addr, seg->data, seg->size);
-        } else {
-            // no image: must be bss
-            mem_proxy.memsetBlob(addr, 0, seg->size);
-        }
-    }
-    return true;
-}
-
-
-bool
-ObjectFile::loadSegments(const PortProxy &proxy)
-{
-    for (auto &seg: segments)
-        if (!loadSegment(seg.get(), proxy))
-            return false;
-    return true;
-}
-
 namespace
 {
 
diff --git a/src/base/loader/object_file.hh b/src/base/loader/object_file.hh
index e5053ad..669afeb 100644
--- a/src/base/loader/object_file.hh
+++ b/src/base/loader/object_file.hh
@@ -37,6 +37,7 @@
 #include <string>
 #include <vector>
 
+#include "base/loader/memory_image.hh"
 #include "base/logging.hh"
 #include "base/types.hh"
 
@@ -74,14 +75,10 @@
         FreeBSD
     };
 
-    static const Addr maxAddr = std::numeric_limits<Addr>::max();
-
   protected:
     const std::string filename;
     uint8_t *fileData;
     size_t len;
-    Addr loadOffset=0;
-    Addr loadMask=maxAddr;
 
     Arch arch;
     OpSys opSys;
@@ -92,16 +89,16 @@
   public:
     virtual ~ObjectFile();
 
-    virtual bool loadSegments(const PortProxy &mem_proxy);
+    virtual MemoryImage buildImage() const = 0;
 
     virtual bool loadAllSymbols(SymbolTable *symtab, Addr base = 0,
-                                Addr offset=0, Addr mask=maxAddr) = 0;
+                                Addr offset=0, Addr mask=MaxAddr) = 0;
     virtual bool loadGlobalSymbols(SymbolTable *symtab, Addr base = 0,
-                                   Addr offset=0, Addr mask=maxAddr) = 0;
+                                   Addr offset=0, Addr mask=MaxAddr) = 0;
     virtual bool loadLocalSymbols(SymbolTable *symtab, Addr base=0,
-                                  Addr offset=0, Addr mask=maxAddr) = 0;
+                                  Addr offset=0, Addr mask=MaxAddr) = 0;
     virtual bool loadWeakSymbols(SymbolTable *symtab, Addr base=0,
-                                 Addr offset=0, Addr mask=maxAddr)
+                                 Addr offset=0, Addr mask=MaxAddr)
     { return false; }
 
     virtual ObjectFile *getInterpreter() const { return nullptr; }
@@ -117,77 +114,12 @@
     Arch  getArch()  const { return arch; }
     OpSys getOpSys() const { return opSys; }
 
-    struct Segment
-    {
-        std::string name;
-        Addr base;
-        uint8_t *data;
-        size_t size;
-    };
-
   protected:
     Addr entry;
 
-    std::vector<std::unique_ptr<Segment>> segments;
-
-    void
-    addSegment(std::string name, Addr base, uint8_t *data, size_t size)
-    {
-        Segment *seg = new Segment;
-        seg->name = name;
-        seg->base = base;
-        seg->data = data;
-        seg->size = size;
-        segments.emplace_back(seg);
-    }
-
-    bool loadSegment(Segment *seg, const PortProxy &mem_proxy);
-
   public:
     Addr entryPoint() const { return entry; }
 
-    Addr
-    maxSegmentAddr() const
-    {
-        Addr max = 0;
-        for (auto &seg: segments) {
-            Addr end = seg->base + seg->size;
-            if (end > max)
-                max = end;
-        }
-        return max;
-    }
-
-    Addr
-    minSegmentAddr() const
-    {
-        Addr min = maxAddr;
-        for (auto &seg: segments)
-            if (seg->base < min)
-                min = seg->base;
-        return min;
-    }
-
-    bool
-    contains(Addr addr) const
-    {
-        for (auto &seg: segments) {
-            Addr start = seg->base;
-            Addr end = seg->base + seg->size;
-            if (addr >= start && addr < end)
-                return true;
-        }
-        return false;
-    }
-
-    /* This function allows you to override the base address where
-     * a binary is going to be loaded or set it if the binary is just a
-     * blob that doesn't include an object header.
-     * @param a address to load the binary/text section at
-     */
-    void setLoadOffset(Addr val) { loadOffset = val; }
-    void setLoadMask(Addr val) { loadMask = val; }
-
     /**
      * Each instance of a Loader subclass will have a chance to try to load
      * an object file when tryLoaders is called. If they can't because they
@@ -221,13 +153,6 @@
     static Process *tryLoaders(ProcessParams *params, ObjectFile *obj_file);
 };
 
-static inline std::ostream &
-operator << (std::ostream &os, const ObjectFile::Segment &seg)
-{
-    ccprintf(os, "%s: %#x %d", seg.name, seg.base, seg.size);
-    return os;
-}
-
 ObjectFile *createObjectFile(const std::string &fname, bool raw = false);
 
 
diff --git a/src/base/loader/raw_object.cc b/src/base/loader/raw_object.cc
index b0ece3b..35ed485 100644
--- a/src/base/loader/raw_object.cc
+++ b/src/base/loader/raw_object.cc
@@ -43,10 +43,14 @@
 
 RawObject::RawObject(const std::string &_filename, size_t _len,
         uint8_t *_data, Arch _arch, OpSys _opSys)
-    : ObjectFile(_filename, _len, _data, _arch, _opSys),
-    data(new Segment{ "data", 0, fileData, len })
+    : ObjectFile(_filename, _len, _data, _arch, _opSys)
 {
-    segments.emplace_back(data);
+}
+
+MemoryImage
+RawObject::buildImage() const
+{
+    return {{ "data", 0, fileData, len }};
 }
 
 bool
diff --git a/src/base/loader/raw_object.hh b/src/base/loader/raw_object.hh
index 9eb9291..6dc54c7 100644
--- a/src/base/loader/raw_object.hh
+++ b/src/base/loader/raw_object.hh
@@ -39,18 +39,17 @@
     RawObject(const std::string &_filename, size_t _len,
               uint8_t *_data, Arch _arch, OpSys _opSys);
 
-    Segment *data;
-
   public:
     virtual ~RawObject() {}
 
-    virtual bool loadAllSymbols(SymbolTable *symtab, Addr base = 0,
-                                Addr offset = 0, Addr addr_mask = maxAddr);
-    virtual bool loadGlobalSymbols(SymbolTable *symtab, Addr base = 0,
-                                   Addr offset = 0,
-                                   Addr addr_mask = maxAddr);
-    virtual bool loadLocalSymbols(SymbolTable *symtab, Addr base = 0,
-                                  Addr offset = 0, Addr addr_mask = maxAddr);
+    MemoryImage buildImage() const override;
+
+    bool loadAllSymbols(SymbolTable *symtab, Addr base=0,
+                        Addr offset=0, Addr addr_mask=MaxAddr) override;
+    bool loadGlobalSymbols(SymbolTable *symtab, Addr base=0,
+                           Addr offset=0, Addr addr_mask=MaxAddr) override;
+    bool loadLocalSymbols(SymbolTable *symtab, Addr base=0,
+                          Addr offset=0, Addr addr_mask=MaxAddr) override;
 
     static ObjectFile *tryFile(const std::string &fname, size_t len,
             uint8_t *data);
diff --git a/src/sim/process.cc b/src/sim/process.cc
index 224152a..a01cfea 100644
--- a/src/sim/process.cc
+++ b/src/sim/process.cc
@@ -129,6 +129,8 @@
     exitGroup = new bool();
     sigchld = new bool();
 
+    image = objFile->buildImage();
+
     if (!debugSymbolTable) {
         debugSymbolTable = new SymbolTable();
         if (!objFile->loadGlobalSymbols(debugSymbolTable) ||
@@ -271,6 +273,16 @@
 }
 
 void
+Process::init()
+{
+    // Patch the ld_bias for dynamic executables.
+    updateBias();
+
+    if (objFile->getInterpreter())
+        interpImage = objFile->getInterpreter()->buildImage();
+}
+
+void
 Process::initState()
 {
     if (contextIds.empty())
@@ -283,6 +295,10 @@
     tc->activate();
 
     pTable->initState();
+
+    // load object file into target memory
+    image.write(initVirtMem);
+    interpImage.write(initVirtMem);
 }
 
 DrainState
diff --git a/src/sim/process.hh b/src/sim/process.hh
index ae23de4..8e35307 100644
--- a/src/sim/process.hh
+++ b/src/sim/process.hh
@@ -41,6 +41,7 @@
 #include <vector>
 
 #include "arch/registers.hh"
+#include "base/loader/memory_image.hh"
 #include "base/statistics.hh"
 #include "base/types.hh"
 #include "config/the_isa.hh"
@@ -69,6 +70,7 @@
     void serialize(CheckpointOut &cp) const override;
     void unserialize(CheckpointIn &cp) override;
 
+    void init() override;
     void initState() override;
     DrainState drain() override;
 
@@ -182,6 +184,8 @@
     SETranslatingPortProxy initVirtMem; // memory proxy for initial image load
 
     ObjectFile *objFile;
+    MemoryImage image;
+    MemoryImage interpImage;
     std::vector<std::string> argv;
     std::vector<std::string> envp;
     std::string executable;
diff --git a/src/sim/syscall_emul.hh b/src/sim/syscall_emul.hh
index 6c3b172..91ddb25 100644
--- a/src/sim/syscall_emul.hh
+++ b/src/sim/syscall_emul.hh
@@ -1886,17 +1886,14 @@
         // executing inside the loader by checking the program counter value.
         // XXX: with multiprogrammed workloads or multi-node configurations,
         // this will not work since there is a single global symbol table.
-        ObjectFile *interpreter = p->getInterpreter();
-        if (interpreter) {
-            if (interpreter->contains(tc->pcState().instAddr())) {
-                std::shared_ptr<FDEntry> fdep = (*p->fds)[tgt_fd];
-                auto ffdp = std::dynamic_pointer_cast<FileFDEntry>(fdep);
-                ObjectFile *lib = createObjectFile(ffdp->getFileName());
+        if (p->interpImage.contains(tc->pcState().instAddr())) {
+            std::shared_ptr<FDEntry> fdep = (*p->fds)[tgt_fd];
+            auto ffdp = std::dynamic_pointer_cast<FileFDEntry>(fdep);
+            ObjectFile *lib = createObjectFile(ffdp->getFileName());
 
-                if (lib) {
-                    lib->loadAllSymbols(debugSymbolTable,
-                                        lib->minSegmentAddr(), start);
-                }
+            if (lib) {
+                lib->loadAllSymbols(debugSymbolTable,
+                                    lib->buildImage().minAddr(), start);
             }
         }
 
diff --git a/src/sim/system.cc b/src/sim/system.cc
index bf8b452..87a2203 100644
--- a/src/sim/system.cc
+++ b/src/sim/system.cc
@@ -152,16 +152,16 @@
         } else {
             // Get the kernel code
             kernel = createObjectFile(params()->kernel);
-            kernel->setLoadOffset(loadAddrOffset);
-            kernel->setLoadMask(loadAddrMask);
             inform("kernel located at: %s", params()->kernel);
 
             if (kernel == NULL)
                 fatal("Could not load kernel file %s", params()->kernel);
 
+            kernelImage = kernel->buildImage();
+
             // setup entry points
-            kernelStart = kernel->minSegmentAddr();
-            kernelEnd = kernel->maxSegmentAddr();
+            kernelStart = kernelImage.minAddr();
+            kernelEnd = kernelImage.maxAddr();
             kernelEntry = kernel->entryPoint();
 
             // If load_addr_mask is set to 0x0, then auto-calculate
@@ -170,9 +170,12 @@
             if (loadAddrMask == 0) {
                 Addr shift_amt = findMsbSet(kernelEnd - kernelStart) + 1;
                 loadAddrMask = ((Addr)1 << shift_amt) - 1;
-                kernel->setLoadMask(loadAddrMask);
             }
 
+            kernelImage.move([this](Addr a) {
+                return (a & loadAddrMask) + loadAddrOffset;
+            });
+
             // load symbols
             if (!kernel->loadGlobalSymbols(kernelSymtab))
                 fatal("could not load kernel symbols\n");
@@ -195,8 +198,6 @@
             ObjectFile *obj = createObjectFile(obj_name);
             fatal_if(!obj, "Failed to additional kernel object '%s'.\n",
                      obj_name);
-            obj->setLoadOffset(loadAddrOffset);
-            obj->setLoadMask(loadAddrMask);
             kernelExtras.push_back(obj);
         }
     }
@@ -316,25 +317,24 @@
         /**
          * Load the kernel code into memory
          */
+        auto mapper = [this](Addr a) {
+            return (a & loadAddrMask) + loadAddrOffset;
+        };
         if (params()->kernel != "")  {
             if (params()->kernel_addr_check) {
                 // Validate kernel mapping before loading binary
-                if (!(isMemAddr((kernelStart & loadAddrMask) +
-                                loadAddrOffset) &&
-                      isMemAddr((kernelEnd & loadAddrMask) +
-                                loadAddrOffset))) {
+                if (!isMemAddr(mapper(kernelStart)) ||
+                        !isMemAddr(mapper(kernelEnd))) {
                     fatal("Kernel is mapped to invalid location (not memory). "
                           "kernelStart 0x(%x) - kernelEnd 0x(%x) %#x:%#x\n",
-                          kernelStart,
-                          kernelEnd, (kernelStart & loadAddrMask) +
-                          loadAddrOffset,
-                          (kernelEnd & loadAddrMask) + loadAddrOffset);
+                          kernelStart, kernelEnd,
+                          mapper(kernelStart), mapper(kernelEnd));
                 }
             }
             // Load program sections into memory
-            kernel->loadSegments(physProxy);
+            kernelImage.write(physProxy);
             for (const auto &extra_kernel : kernelExtras)
-                extra_kernel->loadSegments(physProxy);
+                extra_kernel->buildImage().move(mapper).write(physProxy);
 
             DPRINTF(Loader, "Kernel start = %#x\n", kernelStart);
             DPRINTF(Loader, "Kernel end   = %#x\n", kernelEnd);
diff --git a/src/sim/system.hh b/src/sim/system.hh
index c618e39..345d83c 100644
--- a/src/sim/system.hh
+++ b/src/sim/system.hh
@@ -53,6 +53,7 @@
 #include <vector>
 
 #include "arch/isa_traits.hh"
+#include "base/loader/memory_image.hh"
 #include "base/loader/symtab.hh"
 #include "base/statistics.hh"
 #include "config/the_isa.hh"
@@ -224,6 +225,7 @@
 
     /** Object pointer for the kernel code */
     ObjectFile *kernel;
+    MemoryImage kernelImage;
 
     /** Additional object files */
     std::vector<ObjectFile *> kernelExtras;