From 96b2216bf8982f9cc16a316d4c92cf90041a948d Mon Sep 17 00:00:00 2001 From: Kartik Agaram Date: Thu, 28 Jun 2018 15:22:13 -0700 Subject: [PATCH] 4282 - subx now loads teensy/test4 We're now parsing the ELF spec more closely and better handling multiple program header table entries. --- subx/020elf.cc | 66 ++++++++++++++++++++++++++++++++++++++++---------- 1 file 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);