about summary refs log tree commit diff stats
path: root/subx/020elf.cc
diff options
context:
space:
mode:
authorKartik Agaram <vc@akkartik.com>2018-06-28 15:22:13 -0700
committerKartik Agaram <vc@akkartik.com>2018-06-28 15:47:43 -0700
commit96b2216bf8982f9cc16a316d4c92cf90041a948d (patch)
tree3aaef1777e5d63a32ebce7174517e940de581a8a /subx/020elf.cc
parentbbe7cd53cb37e5d318c87626a0169987c3841e88 (diff)
downloadmu-96b2216bf8982f9cc16a316d4c92cf90041a948d.tar.gz
4282 - subx now loads teensy/test4
We're now parsing the ELF spec more closely and better handling multiple
program header table entries.
Diffstat (limited to 'subx/020elf.cc')
-rw-r--r--subx/020elf.cc66
1 files changed, 53 insertions, 13 deletions
diff --git a/subx/020elf.cc b/subx/020elf.cc
index bde5cca3..a065e9fb 100644
--- a/subx/020elf.cc
+++ b/subx/020elf.cc
@@ -27,23 +27,32 @@ void load_elf(const string& filename) {
 void load_elf_contents(uint8_t* elf_contents, size_t size) {
   uint8_t magic[5] = {0};
   memcpy(magic, elf_contents, 4);
-  if (0 != memcmp(magic, "\177ELF", 4))
+  if (memcmp(magic, "\177ELF", 4) != 0)
     die("Invalid ELF file starting with \"%s\"", magic);
-
-  uint32_t e_type = u32_in(&elf_contents[16]);
-  if (0x00030002 != e_type)
-    die("ELF type/machine 0x%x isn't i386 executable", e_type);
-
+  if (elf_contents[4] != 1)
+    die("Only 32-bit ELF files (4-byte words; virtual addresses up to 4GB) supported.\n");
+  if (elf_contents[5] != 1)
+    die("Only little-endian ELF files supported.\n");
+  // unused: remaining 10 bytes of e_ident
+  uint32_t e_machine_type = u32_in(&elf_contents[16]);
+  if (e_machine_type != 0x00030002)
+    die("ELF type/machine 0x%x isn't i386 executable", e_machine_type);
+  // unused: e_version. We only support version 1, and later versions will be backwards compatible.
   uint32_t e_entry = u32_in(&elf_contents[24]);
   uint32_t e_phoff = u32_in(&elf_contents[28]);
-  uint32_t p_vaddr = u32_in(&elf_contents[e_phoff + 8]);
-  uint32_t p_memsz = u32_in(&elf_contents[e_phoff + 20]);
+  // unused: e_shoff
+  // unused: e_flags
+  uint32_t e_ehsize = u16_in(&elf_contents[40]);
+  if (e_ehsize < 52) die("Invalid binary; ELF header too small\n");
+  uint32_t e_phentsize = u16_in(&elf_contents[42]);
+  uint32_t e_phnum = u16_in(&elf_contents[44]);
+  cerr << e_phnum << " entries in the program header, each " << e_phentsize << " bytes long\n";
+  // unused: e_shentsize
+  // unused: e_shnum
+  // unused: e_shstrndx
 
-  Mem.resize(p_vaddr + p_memsz);
-  if (size > p_memsz - p_vaddr) size = p_memsz - p_vaddr;
-  for (size_t i = 0;  i < size;  ++i)
-    Mem.at(p_vaddr + i) = elf_contents[i];
-  End_of_program = p_vaddr + p_memsz;
+  for (size_t i = 0;  i < e_phnum;  ++i)
+    load_program_header(elf_contents, size, e_phoff + i*e_phentsize, e_ehsize);
 
   // TODO: need to set up real stack somewhere
 
@@ -51,10 +60,41 @@ void load_elf_contents(uint8_t* elf_contents, size_t size) {
   EIP = e_entry;
 }
 
+void load_program_header(uint8_t* elf_contents, size_t size, uint32_t offset, uint32_t e_ehsize) {
+  uint32_t p_type = u32_in(&elf_contents[offset]);
+  cerr << "program header at offset " << offset << ": type " << p_type << '\n';
+  if (p_type != 1) {
+    cerr << "ignoring segment at offset " << offset << " of non PT_LOAD type " << p_type << " (see http://refspecs.linuxbase.org/elf/elf.pdf)\n";
+    return;
+  }
+  uint32_t p_offset = u32_in(&elf_contents[offset + 4]);
+  uint32_t p_vaddr = u32_in(&elf_contents[offset + 8]);
+  if (e_ehsize > p_vaddr) die("Invalid binary; program header overlaps ELF header\n");
+  // unused: p_paddr
+  uint32_t p_filesz = u32_in(&elf_contents[offset + 16]);
+  uint32_t p_memsz = u32_in(&elf_contents[offset + 20]);
+  if (p_filesz != p_memsz)
+    die("Can't handle segments where p_filesz != p_memsz (see http://refspecs.linuxbase.org/elf/elf.pdf)\n");
+
+  if (p_offset + p_filesz > size)
+    die("Invalid binary; segment at offset %d is too large: wants to end at %d but the file ends at %d\n", offset, p_offset+p_filesz, size);
+  Mem.resize(p_vaddr + p_memsz);
+  if (size > p_memsz) size = p_memsz;
+  cerr << "blitting file offsets (" << p_offset << ", " << (p_offset+p_filesz) << ") to addresses (" << p_vaddr << ", " << (p_vaddr+p_memsz) << ")\n";
+  for (size_t i = 0;  i < p_filesz;  ++i)
+    Mem.at(p_vaddr + i) = elf_contents[p_offset + i];
+  if (End_of_program < p_vaddr+p_memsz)
+    End_of_program = p_vaddr+p_memsz;
+}
+
 inline uint32_t u32_in(uint8_t* p) {
   return p[0] | p[1] << 8 | p[2] << 16 | p[3] << 24;
 }
 
+inline uint16_t u16_in(uint8_t* p) {
+  return p[0] | p[1] << 8;
+}
+
 void die(const char* format, ...) {
   va_list args;
   va_start(args, format);