https://github.com/akkartik/mu/blob/master/subx/012elf.cc
1
2
3
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)
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
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
47 uint32_t e_entry = u32_in(&elf_contents[24]);
48 uint32_t e_phoff = u32_in(&elf_contents[28]);
49
50
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
57
58
59
60 set<uint32_t> overlap;
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
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
73
74
75 Mem.push_back(vma(ARGV_DATA_SEGMENT));
76 uint32_t argv_data = ARGV_DATA_SEGMENT;
77 for (int i = argc-1; i >= 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());
81 write_mem_u8(argv_data, argv[i][j]);
82 argv_data += sizeof(char);
83 assert(argv_data < ARGV_DATA_SEGMENT + SEGMENT_ALIGNMENT);
84 }
85 }
86 push(argc-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
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 >= SEGMENT_ALIGNMENT) {
115 raise << "Code segment too small for SubX; for now please manually increase SEGMENT_ALIGNMENT.\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
132
133
134
135 const int CODE_SEGMENT = 0x09000000;
136 const int DATA_SEGMENT = 0x0a000000;
137 const int STACK_SEGMENT = 0x0b000000;
138 const int AFTER_STACK = 0x0b000ffc;
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 ) {
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& , die ) {
171 if (Trace_stream) Trace_stream->newline();
172 exit(1);
173 }
174
175 :(before "End Includes")
176
177
178
179
180
181