aboutsummaryrefslogtreecommitdiff
path: root/fesvr/elfloader.cc
blob: 6e764ef73aaf7f6baea631e6189c774428171b49 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
// See LICENSE for license details.

#include "elf.h"
#include "memif.h"
#include "byteorder.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));
  assert(IS_ELFLE(*eh64) || IS_ELFBE(*eh64));

  std::vector<uint8_t> zeros;
  std::map<std::string, uint64_t> symbols;

  #define LOAD_ELF(ehdr_t, phdr_t, shdr_t, sym_t, bswap) do { \
    ehdr_t* eh = (ehdr_t*)buf; \
    phdr_t* ph = (phdr_t*)(buf + bswap(eh->e_phoff)); \
    *entry = bswap(eh->e_entry); \
    assert(size >= bswap(eh->e_phoff) + bswap(eh->e_phnum)*sizeof(*ph)); \
    for (unsigned i = 0; i < bswap(eh->e_phnum); i++) {			\
      if(bswap(ph[i].p_type) == PT_LOAD && bswap(ph[i].p_memsz)) {	\
        if (bswap(ph[i].p_filesz)) {					\
          assert(size >= bswap(ph[i].p_offset) + bswap(ph[i].p_filesz)); \
          memif->write(bswap(ph[i].p_paddr), bswap(ph[i].p_filesz), (uint8_t*)buf + bswap(ph[i].p_offset)); \
        } \
        zeros.resize(bswap(ph[i].p_memsz) - bswap(ph[i].p_filesz)); \
        memif->write(bswap(ph[i].p_paddr) + bswap(ph[i].p_filesz), bswap(ph[i].p_memsz) - bswap(ph[i].p_filesz), &zeros[0]); \
      } \
    } \
    shdr_t* sh = (shdr_t*)(buf + bswap(eh->e_shoff)); \
    assert(size >= bswap(eh->e_shoff) + bswap(eh->e_shnum)*sizeof(*sh)); \
    assert(bswap(eh->e_shstrndx) < bswap(eh->e_shnum)); \
    assert(size >= bswap(sh[bswap(eh->e_shstrndx)].sh_offset) + bswap(sh[bswap(eh->e_shstrndx)].sh_size)); \
    char *shstrtab = buf + bswap(sh[bswap(eh->e_shstrndx)].sh_offset);	\
    unsigned strtabidx = 0, symtabidx = 0; \
    for (unsigned i = 0; i < bswap(eh->e_shnum); i++) {		     \
      unsigned max_len = bswap(sh[bswap(eh->e_shstrndx)].sh_size) - bswap(sh[i].sh_name); \
      assert(bswap(sh[i].sh_name) < bswap(sh[bswap(eh->e_shstrndx)].sh_size));	\
      assert(strnlen(shstrtab + bswap(sh[i].sh_name), max_len) < max_len); \
      if (bswap(sh[i].sh_type) & SHT_NOBITS) continue; \
      assert(size >= bswap(sh[i].sh_offset) + bswap(sh[i].sh_size)); \
      if (strcmp(shstrtab + bswap(sh[i].sh_name), ".strtab") == 0) \
        strtabidx = i; \
      if (strcmp(shstrtab + bswap(sh[i].sh_name), ".symtab") == 0) \
        symtabidx = i; \
    } \
    if (strtabidx && symtabidx) { \
      char* strtab = buf + bswap(sh[strtabidx].sh_offset); \
      sym_t* sym = (sym_t*)(buf + bswap(sh[symtabidx].sh_offset)); \
      for (unsigned i = 0; i < bswap(sh[symtabidx].sh_size)/sizeof(sym_t); i++) { \
        unsigned max_len = bswap(sh[strtabidx].sh_size) - bswap(sym[i].st_name); \
        assert(bswap(sym[i].st_name) < bswap(sh[strtabidx].sh_size));	\
        assert(strnlen(strtab + bswap(sym[i].st_name), max_len) < max_len); \
        symbols[strtab + bswap(sym[i].st_name)] = bswap(sym[i].st_value); \
      } \
    } \
  } while(0)

  if (IS_ELF32(*eh64))
    if (IS_ELFLE(*eh64))
      LOAD_ELF(Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr, Elf32_Sym, from_le);
    else
      LOAD_ELF(Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr, Elf32_Sym, from_be);
  else
    if (IS_ELFLE(*eh64))
      LOAD_ELF(Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr, Elf64_Sym, from_le);
    else
      LOAD_ELF(Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr, Elf64_Sym, from_be);

  munmap(buf, size);

  return symbols;
}