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