aboutsummaryrefslogtreecommitdiff
path: root/fesvr/elfloader.cc
diff options
context:
space:
mode:
authorAndrew Waterman <andrew@sifive.com>2019-03-31 00:49:57 -0700
committerAndrew Waterman <andrew@sifive.com>2019-03-31 00:50:15 -0700
commitf49618ca9d674ec7e596118f85027c4b503862ac (patch)
tree3b6ad73cbe8760309f930b743950a2853ad17668 /fesvr/elfloader.cc
parent61cb96df00067ba61cf3816c74c18aef5677197a (diff)
downloadspike-f49618ca9d674ec7e596118f85027c4b503862ac.zip
spike-f49618ca9d674ec7e596118f85027c4b503862ac.tar.gz
spike-f49618ca9d674ec7e596118f85027c4b503862ac.tar.bz2
Add fesvr; only globally install fesvr headers/libsstatic-link
Diffstat (limited to 'fesvr/elfloader.cc')
-rw-r--r--fesvr/elfloader.cc89
1 files changed, 89 insertions, 0 deletions
diff --git a/fesvr/elfloader.cc b/fesvr/elfloader.cc
new file mode 100644
index 0000000..3042f54
--- /dev/null
+++ b/fesvr/elfloader.cc
@@ -0,0 +1,89 @@
+// See LICENSE for license details.
+
+#include "elf.h"
+#include "memif.h"
+#include <cstring>
+#include <string>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <assert.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <vector>
+#include <map>
+
+std::map<std::string, uint64_t> load_elf(const char* fn, memif_t* memif, reg_t* entry)
+{
+ int fd = open(fn, O_RDONLY);
+ struct stat s;
+ assert(fd != -1);
+ if (fstat(fd, &s) < 0)
+ abort();
+ size_t size = s.st_size;
+
+ char* buf = (char*)mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
+ assert(buf != MAP_FAILED);
+ close(fd);
+
+ assert(size >= sizeof(Elf64_Ehdr));
+ const Elf64_Ehdr* eh64 = (const Elf64_Ehdr*)buf;
+ assert(IS_ELF32(*eh64) || IS_ELF64(*eh64));
+
+ std::vector<uint8_t> zeros;
+ std::map<std::string, uint64_t> symbols;
+
+ #define LOAD_ELF(ehdr_t, phdr_t, shdr_t, sym_t) do { \
+ ehdr_t* eh = (ehdr_t*)buf; \
+ phdr_t* ph = (phdr_t*)(buf + eh->e_phoff); \
+ *entry = eh->e_entry; \
+ assert(size >= eh->e_phoff + eh->e_phnum*sizeof(*ph)); \
+ for (unsigned i = 0; i < eh->e_phnum; i++) { \
+ if(ph[i].p_type == PT_LOAD && ph[i].p_memsz) { \
+ if (ph[i].p_filesz) { \
+ assert(size >= ph[i].p_offset + ph[i].p_filesz); \
+ memif->write(ph[i].p_paddr, ph[i].p_filesz, (uint8_t*)buf + ph[i].p_offset); \
+ } \
+ zeros.resize(ph[i].p_memsz - ph[i].p_filesz); \
+ memif->write(ph[i].p_paddr + ph[i].p_filesz, ph[i].p_memsz - ph[i].p_filesz, &zeros[0]); \
+ } \
+ } \
+ shdr_t* sh = (shdr_t*)(buf + eh->e_shoff); \
+ assert(size >= eh->e_shoff + eh->e_shnum*sizeof(*sh)); \
+ assert(eh->e_shstrndx < eh->e_shnum); \
+ assert(size >= sh[eh->e_shstrndx].sh_offset + sh[eh->e_shstrndx].sh_size); \
+ char *shstrtab = buf + sh[eh->e_shstrndx].sh_offset; \
+ unsigned strtabidx = 0, symtabidx = 0; \
+ for (unsigned i = 0; i < eh->e_shnum; i++) { \
+ unsigned max_len = sh[eh->e_shstrndx].sh_size - sh[i].sh_name; \
+ assert(sh[i].sh_name < sh[eh->e_shstrndx].sh_size); \
+ assert(strnlen(shstrtab + sh[i].sh_name, max_len) < max_len); \
+ if (sh[i].sh_type & SHT_NOBITS) continue; \
+ assert(size >= sh[i].sh_offset + sh[i].sh_size); \
+ if (strcmp(shstrtab + sh[i].sh_name, ".strtab") == 0) \
+ strtabidx = i; \
+ if (strcmp(shstrtab + sh[i].sh_name, ".symtab") == 0) \
+ symtabidx = i; \
+ } \
+ if (strtabidx && symtabidx) { \
+ char* strtab = buf + sh[strtabidx].sh_offset; \
+ sym_t* sym = (sym_t*)(buf + sh[symtabidx].sh_offset); \
+ for (unsigned i = 0; i < sh[symtabidx].sh_size/sizeof(sym_t); i++) { \
+ unsigned max_len = sh[strtabidx].sh_size - sym[i].st_name; \
+ assert(sym[i].st_name < sh[strtabidx].sh_size); \
+ assert(strnlen(strtab + sym[i].st_name, max_len) < max_len); \
+ symbols[strtab + sym[i].st_name] = sym[i].st_value; \
+ } \
+ } \
+ } while(0)
+
+ if (IS_ELF32(*eh64))
+ LOAD_ELF(Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr, Elf32_Sym);
+ else
+ LOAD_ELF(Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr, Elf64_Sym);
+
+ munmap(buf, size);
+
+ return symbols;
+}