# Mu - subx/012elf.cc

      1 //: Generating ELF binaries for SubX programs.
      2 //: This will allow us to run them natively on a Linux kernel.
      3 //: Based on https://github.com/kragen/stoneknifeforth/blob/702d2ebe1b/386.c
      4
      5 :(before "End Main")
      6 assert(argc > 1);
      7 if (is_equal(argv[1], "run")) {
      8   START_TRACING_UNTIL_END_OF_SCOPE;
      9   assert(argc > 2);
     10   reset();
     11   cerr << std::hex;
     12   load_elf(argv[2], argc, argv);
     13   while (EIP < End_of_program)  // weak final-gasp termination check
     14     run_one_instruction();
     15   raise << "executed past end of the world: " << EIP << " vs " << End_of_program << '\n' << end();
     16   return 1;
     17 }
     18
     19 :(code)
     20 void load_elf(const string& filename, int argc, char* argv[]) {
     21   int fd = open(filename.c_str(), O_RDONLY);
     22   if (fd < 0) raise << filename.c_str() << ": open" << perr() << '\n' << die();
     23   off_t size = lseek(fd, 0, SEEK_END);
     24   lseek(fd, 0, SEEK_SET);
     25   uint8_t* elf_contents = static_cast<uint8_t*>(malloc(size));
     26   if (elf_contents == NULL) raise << "malloc(" << size << ')' << perr() << '\n' << die();
     27   ssize_t read_size = read(fd, elf_contents, size);
     28   if (size != read_size) raise << "read → " << size << " (!= " << read_size << ')' << perr() << '\n' << die();
     29   load_elf_contents(elf_contents, size, argc, argv);
     30   free(elf_contents);
     31 }
     32
     33 void load_elf_contents(uint8_t* elf_contents, size_t size, int argc, char* argv[]) {
     34   uint8_t magic[5] = {0};
     35   memcpy(magic, elf_contents, 4);
     36   if (memcmp(magic, "\177ELF", 4) != 0)
     37     raise << "Invalid ELF file; starts with \"" << magic << '"' << die();
     38   if (elf_contents[4] != 1)
     39     raise << "Only 32-bit ELF files (4-byte words; virtual addresses up to 4GB) supported.\n" << die();
     40   if (elf_contents[5] != 1)
     41     raise << "Only little-endian ELF files supported.\n" << die();
     42   // unused: remaining 10 bytes of e_ident
     43   uint32_t e_machine_type = u32_in(&elf_contents[16]);
     44   if (e_machine_type != 0x00030002)
     45     raise << "ELF type/machine 0x" << HEXWORD << e_machine_type << " isn't i386 executable\n" << die();
     46   // unused: e_version. We only support version 1, and later versions will be backwards compatible.
     47   uint32_t e_entry = u32_in(&elf_contents[24]);
     48   uint32_t e_phoff = u32_in(&elf_contents[28]);
     49   // unused: e_shoff
     50   // unused: e_flags
     51   uint32_t e_ehsize = u16_in(&elf_contents[40]);
     52   if (e_ehsize < 52) raise << "Invalid binary; ELF header too small\n" << die();
     53   uint32_t e_phentsize = u16_in(&elf_contents[42]);
     54   uint32_t e_phnum = u16_in(&elf_contents[44]);
     55   trace(90, "load") << e_phnum << " entries in the program header, each " << e_phentsize << " bytes long" << end();
     56   // unused: e_shentsize
     57   // unused: e_shnum
     58   // unused: e_shstrndx
     59
     60   set<uint32_t> overlap;  // to detect overlapping segments
     61   for (size_t i = 0;  i < e_phnum;  ++i)
     62     load_segment_from_program_header(elf_contents, i, size, e_phoff + i*e_phentsize, e_ehsize, overlap);
     63
     64   // initialize code and stack
     65   assert(overlap.find(STACK_SEGMENT) == overlap.end());
     66   Mem.push_back(vma(STACK_SEGMENT));
     67   assert(overlap.find(AFTER_STACK) == overlap.end());
     68   Reg[ESP].u = AFTER_STACK;
     69   Reg[EBP].u = 0;
     70   EIP = e_entry;
     71
     72   // initialize args on stack
     73   // no envp for now
     74   // we wastefully use a separate page of memory for argv
     75   Mem.push_back(vma(ARGV_DATA_SEGMENT));
     76   uint32_t argv_data = ARGV_DATA_SEGMENT;
     77   for (int i = argc-1;  i >= /*skip 'subx_bin' and 'run'*/2;  --i) {
     78     push(argv_data);
     79     for (size_t j = 0;  j <= strlen(argv[i]);  ++j) {
     80       assert(overlap.find(argv_data) == overlap.end());  // don't bother comparing ARGV and STACK
     81       write_mem_u8(argv_data, argv[i][j]);
     82       argv_data += sizeof(char);
     83       assert(argv_data < ARGV_DATA_SEGMENT + INITIAL_SEGMENT_SIZE);
     84     }
     85   }
     86   push(argc-/*skip 'subx_bin' and 'run'*/2);
     87 }
     88
     89 void push(uint32_t val) {
     90   Reg[ESP].u -= 4;
     91   trace(90, "run") << "decrementing ESP to 0x" << HEXWORD << Reg[ESP].u << end();
     92   trace(90, "run") << "pushing value 0x" << HEXWORD << val << end();
     93   write_mem_u32(Reg[ESP].u, val);
     94 }
     95
     96 void load_segment_from_program_header(uint8_t* elf_contents, int segment_index, size_t size, uint32_t offset, uint32_t e_ehsize, set<uint32_t>& overlap) {
     97   uint32_t p_type = u32_in(&elf_contents[offset]);
     98   trace(90, "load") << "program header at offset " << offset << ": type " << p_type << end();
     99   if (p_type != 1) {
    100     trace(90, "load") << "ignoring segment at offset " << offset << " of non PT_LOAD type " << p_type << " (see http://refspecs.linuxbase.org/elf/elf.pdf)" << end();
    101     return;
    102   }
    103   uint32_t p_offset = u32_in(&elf_contents[offset + 4]);
    104   uint32_t p_vaddr = u32_in(&elf_contents[offset + 8]);
    105   if (e_ehsize > p_vaddr) raise << "Invalid binary; program header overlaps ELF header\n" << die();
    106   // unused: p_paddr
    107   uint32_t p_filesz = u32_in(&elf_contents[offset + 16]);
    108   uint32_t p_memsz = u32_in(&elf_contents[offset + 20]);
    109   if (p_filesz != p_memsz)
    110     raise << "Can't yet handle segments where p_filesz != p_memsz (see http://refspecs.linuxbase.org/elf/elf.pdf)\n" << die();
    111
    112   if (p_offset + p_filesz > size)
    113     raise << "Invalid binary; segment at offset " << offset << " is too large: wants to end at " << p_offset+p_filesz << " but the file ends at " << size << '\n' << die();
    114   if (p_memsz >= INITIAL_SEGMENT_SIZE) {
    115     raise << "Code segment too small for SubX; for now please manually increase INITIAL_SEGMENT_SIZE.\n" << end();
    116     return;
    117   }
    118   trace(90, "load") << "blitting file offsets (" << p_offset << ", " << (p_offset+p_filesz) << ") to addresses (" << p_vaddr << ", " << (p_vaddr+p_memsz) << ')' << end();
    119   if (size > p_memsz) size = p_memsz;
    120   Mem.push_back(vma(p_vaddr));
    121   for (size_t i = 0;  i < p_filesz;  ++i) {
    122     assert(overlap.find(p_vaddr+i) == overlap.end());
    123     write_mem_u8(p_vaddr+i, elf_contents[p_offset+i]);
    124     overlap.insert(p_vaddr+i);
    125   }
    126   if (segment_index == 0 && End_of_program < p_vaddr+p_memsz)
    127     End_of_program = p_vaddr+p_memsz;
    128 }
    129
    130 :(before "End Includes")
    131 // Very primitive/fixed/insecure ELF segments for now.
    132 //   code: 0x09000000 -> 0x09ffffff
    133 //   data/heap: 0x0a000000 -> 0x0affffff
    134 //   stack: 0x0b000ffc -> 0x0b000000 (downward)
    135 const int CODE_SEGMENT = 0x09000000;
    136 const int DATA_SEGMENT = 0x0a000000;
    137 const int STACK_SEGMENT = 0x0b000000;
    138 const int AFTER_STACK = 0x0b000ffc;  // forget final word because of the off-by-one with INITIAL_SEGMENT_SIZE;
    139 const int ARGV_DATA_SEGMENT = 0x0c000000;
    140 :(code)
    141 void dump_stack() {
    142   cerr << "stack:\n";
    143   for (uint32_t a = AFTER_STACK-4;  a > Reg[ESP].u;  a -= 4)
    144     cerr << "  0x" << HEXWORD << a << " => 0x" << HEXWORD << read_mem_u32(a) << '\n';
    145   cerr << "  0x" << HEXWORD << Reg[ESP].u << " => 0x" << HEXWORD << read_mem_u32(Reg[ESP].u) << "  <=== ESP\n";
    146   for (uint32_t a = Reg[ESP].u-4;  a > Reg[ESP].u-40;  a -= 4)
    147     cerr << "  0x" << HEXWORD << a << " => 0x" << HEXWORD << read_mem_u32(a) << '\n';
    148 }
    149
    150 inline uint32_t u32_in(uint8_t* p) {
    151   return p[0] | p[1] << 8 | p[2] << 16 | p[3] << 24;
    152 }
    153
    154 inline uint16_t u16_in(uint8_t* p) {
    155   return p[0] | p[1] << 8;
    156 }
    157
    158 :(before "End Types")
    159 struct perr {};
    160 :(code)
    161 ostream& operator<<(ostream& os, perr /*unused*/) {
    162   if (errno)
    163     os << ": " << strerror(errno);
    164   return os;
    165 }
    166
    167 :(before "End Types")
    168 struct die {};
    169 :(code)
    170 ostream& operator<<(ostream& /*unused*/, die /*unused*/) {
    171   if (Trace_stream) Trace_stream->newline();
    172   exit(1);
    173 }
    174
    175 :(before "End Includes")
    176 #include <sys/types.h>
    177 #include <sys/stat.h>
    178 #include <fcntl.h>
    179 #include <stdarg.h>
    180 #include <errno.h>
    181 #include <unistd.h>