authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2015-11-05 00:05:25-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2015-11-05 00:05:25-07:00
log775e98be5a089e1749f14b10748ae8e180d2a724
treef53ee6053b9e0af2919c29f0fb4dea964ed2377e
parent174baa49bda0d4ce1dfc99555d7e41cd86a49513

experiment with being a linker


4 files changed, 431 insertions(+), 40 deletions(-)

CMakeLists.txt+4-3
......@@ -24,11 +24,12 @@ set(GRAMMAR_TXT "${CMAKE_BINARY_DIR}/simple.txt")
2424set(PARSER_GENERATED_CPP "${CMAKE_BINARY_DIR}/parser_generated.cpp")
2525
2626set(ZIG_SOURCES
27 "${CMAKE_SOURCE_DIR}/src/main.cpp"
28 "${CMAKE_SOURCE_DIR}/src/util.cpp"
2927 "${CMAKE_SOURCE_DIR}/src/buffer.cpp"
30 "${CMAKE_SOURCE_DIR}/src/tokenizer.cpp"
28 "${CMAKE_SOURCE_DIR}/src/error.cpp"
29 "${CMAKE_SOURCE_DIR}/src/main.cpp"
3130 "${CMAKE_SOURCE_DIR}/src/parser.cpp"
31 "${CMAKE_SOURCE_DIR}/src/tokenizer.cpp"
32 "${CMAKE_SOURCE_DIR}/src/util.cpp"
3233 ${PARSER_GENERATED_CPP}
3334)
3435
src/error.cpp created+9
......@@ -0,0 +1,9 @@
1#include "error.hpp"
2
3const char *err_str(int err) {
4 switch ((enum Error)err) {
5 case ErrorNoMem: return "out of memory";
6 case ErrorInvalidFormat: return "invalid format";
7 }
8 return "(invalid error)";
9}
src/error.hpp created+18
......@@ -0,0 +1,18 @@
1/*
2 * Copyright (c) 2015 Andrew Kelley
3 *
4 * This file is part of zig, which is MIT licensed.
5 * See http://opensource.org/licenses/MIT
6 */
7
8#ifndef ERROR_HPP
9#define ERROR_HPP
10
11enum Error {
12 ErrorNoMem,
13 ErrorInvalidFormat,
14};
15
16const char *err_str(int err);
17
18#endif
src/main.cpp+400-37
......@@ -11,6 +11,7 @@
1111#include "buffer.hpp"
1212#include "parser.hpp"
1313#include "tokenizer.hpp"
14#include "error.hpp"
1415
1516#include <stdio.h>
1617#include <string.h>
......@@ -21,12 +22,17 @@
2122#include <sys/types.h>
2223#include <sys/stat.h>
2324#include <unistd.h>
25#include <inttypes.h>
2426
25static int usage(char *arg0) {
26 fprintf(stderr, "Usage: %s --output outfile code.zig\n"
27 "Other options:\n"
28 "--version print version number and exit\n"
29 "-Ipath add path to header include path\n"
27static int usage(const char *arg0) {
28 fprintf(stderr, "Usage: %s [command] [options] target\n"
29 "Commands:\n"
30 " build create an executable from target\n"
31 " link turn a .o file into an executable\n"
32 "Options:\n"
33 " --output output file\n"
34 " --version print version number and exit\n"
35 " -Ipath add path to header include path\n"
3036 , arg0);
3137 return EXIT_FAILURE;
3238}
......@@ -49,37 +55,8 @@ static Buf *fetch_file(FILE *f) {
4955 return buf;
5056}
5157
52char cur_dir[1024];
53
54int main(int argc, char **argv) {
55 char *arg0 = argv[0];
56 char *in_file = NULL;
57 char *out_file = NULL;
58 ZigList<char *> include_paths = {0};
59 for (int i = 1; i < argc; i += 1) {
60 char *arg = argv[i];
61 if (arg[0] == '-' && arg[1] == '-') {
62 if (strcmp(arg, "--version") == 0) {
63 printf("%s\n", ZIG_VERSION_STRING);
64 return EXIT_SUCCESS;
65 } else if (i + 1 >= argc) {
66 return usage(arg0);
67 } else {
68 i += 1;
69 if (strcmp(arg, "--output") == 0) {
70 out_file = argv[i];
71 } else {
72 return usage(arg0);
73 }
74 }
75 } else if (arg[0] == '-' && arg[1] == 'I') {
76 include_paths.append(arg + 2);
77 } else if (!in_file) {
78 in_file = arg;
79 } else {
80 return usage(arg0);
81 }
82 }
58static int build(const char *arg0, const char *in_file, const char *out_file, ZigList<char *> *include_paths) {
59 static char cur_dir[1024];
8360
8461 if (!in_file || !out_file)
8562 return usage(arg0);
......@@ -116,5 +93,391 @@ int main(int argc, char **argv) {
11693 ast_print(root, 0);
11794
11895
119 return EXIT_SUCCESS;
96 return 0;
12097}
98
99enum ElfType {
100 ElfTypeRelocatable,
101 ElfTypeExecutable,
102 ElfTypeShared,
103 ElfTypeCore,
104};
105
106enum ElfArch {
107 ElfArchSparc,
108 ElfArchx86,
109 ElfArchMips,
110 ElfArchPowerPc,
111 ElfArchArm,
112 ElfArchSuperH,
113 ElfArchIA_64,
114 ElfArchx86_64,
115 ElfArchAArch64,
116};
117
118
119enum ElfSectionType {
120 SHT_NULL = 0,
121 SHT_PROGBITS = 1,
122 SHT_SYMTAB = 2,
123 SHT_STRTAB = 3,
124 SHT_RELA = 4,
125 SHT_HASH = 5,
126 SHT_DYNAMIC = 6,
127 SHT_NOTE = 7,
128 SHT_NOBITS = 8,
129 SHT_REL = 9,
130 SHT_SHLIB = 10,
131 SHT_DYNSYM = 11,
132 SHT_INIT_ARRAY = 14,
133 SHT_FINI_ARRAY = 15,
134 SHT_PREINIT_ARRAY = 16,
135 SHT_GROUP = 17,
136 SHT_SYMTAB_SHNDX = 18,
137 SHT_LOOS = 0x60000000,
138 SHT_HIOS = 0x6fffffff,
139 SHT_LOPROC = 0x70000000,
140 SHT_HIPROC = 0x7fffffff,
141 SHT_LOUSER = 0x80000000,
142 SHT_HIUSER = 0xffffffff,
143};
144
145struct Elf32SectionHeader {
146 uint32_t name;
147 uint32_t type;
148 uint32_t flags;
149 size_t addr;
150 off_t offset;
151 uint32_t size;
152 uint32_t link;
153 uint32_t info;
154 uint32_t addralign;
155 uint32_t entsize;
156};
157
158struct Elf64SectionHeader{
159 uint32_t name;
160 uint32_t type;
161 uint64_t flags;
162 size_t addr;
163 off_t offset;
164 uint64_t size;
165 uint32_t link;
166 uint32_t info;
167 uint64_t addralign;
168 uint64_t entsize;
169};
170
171struct Elf {
172 bool is_64;
173 bool is_little_endian;
174 ElfType type;
175 ElfArch arch;
176 uint64_t entry_addr;
177 uint64_t program_header_offset;
178 uint64_t section_header_offset;
179 int sh_entry_count;
180 int string_section_index;
181 union {
182 Elf32SectionHeader *elf32_section_headers;
183 Elf64SectionHeader *elf64_section_headers;
184 };
185};
186
187static int parse_elf(Elf *elf, Buf *buf) {
188 int len = buf_len(buf);
189 if (len < 52) {
190 return ErrorInvalidFormat;
191 }
192
193 char *ptr = buf_ptr(buf);
194 static const int magic_size = 4;
195 static const char magic[magic_size] = {0x7f, 'E', 'L', 'F'};
196 if (memcmp(ptr, magic, magic_size) != 0) {
197 return ErrorInvalidFormat;
198 }
199 ptr += magic_size;
200
201 if (*ptr == 1) {
202 elf->is_64 = false;
203 } else if (*ptr == 2) {
204 elf->is_64 = true;
205 if (len < 64)
206 return ErrorInvalidFormat;
207 } else {
208 return ErrorInvalidFormat;
209 }
210 ptr += 1;
211
212
213 if (*ptr == 1) {
214 elf->is_little_endian = true;
215 } else if (*ptr == 2) {
216 elf->is_little_endian = false;
217 zig_panic("can only handle little endian");
218 } else {
219 return ErrorInvalidFormat;
220 }
221 ptr += 1;
222
223
224 if (*ptr != 1) {
225 return ErrorInvalidFormat;
226 }
227
228 uint16_t *type_number = (uint16_t *)&buf_ptr(buf)[0x10];
229 if (*type_number == 1) {
230 elf->type = ElfTypeRelocatable;
231 } else if (*type_number == 2) {
232 elf->type = ElfTypeExecutable;
233 } else if (*type_number == 3) {
234 elf->type = ElfTypeShared;
235 } else if (*type_number == 4) {
236 elf->type = ElfTypeCore;
237 } else {
238 return ErrorInvalidFormat;
239 }
240
241 uint16_t *arch = (uint16_t *)&buf_ptr(buf)[0x12];
242 if (*arch == 0x02) {
243 elf->arch = ElfArchSparc;
244 } else if (*arch == 0x03) {
245 elf->arch = ElfArchx86;
246 } else if (*arch == 0x08) {
247 elf->arch = ElfArchMips;
248 } else if (*arch == 0x14) {
249 elf->arch = ElfArchPowerPc;
250 } else if (*arch == 0x28) {
251 elf->arch = ElfArchArm;
252 } else if (*arch == 0x2A) {
253 elf->arch = ElfArchSuperH;
254 } else if (*arch == 0x32) {
255 elf->arch = ElfArchIA_64;
256 } else if (*arch == 0x3E) {
257 elf->arch = ElfArchx86_64;
258 } else if (*arch == 0xb7) {
259 elf->arch = ElfArchAArch64;
260 } else {
261 return ErrorInvalidFormat;
262 }
263
264 uint32_t *elf_vers = (uint32_t *)&buf_ptr(buf)[0x14];
265 if (*elf_vers != 1) {
266 return ErrorInvalidFormat;
267 }
268
269 ptr = &buf_ptr(buf)[0x18];
270 if (elf->is_64) {
271 elf->entry_addr = *((uint64_t *)ptr);
272 ptr += 8;
273 elf->program_header_offset = *((uint64_t *)ptr);
274 ptr += 8;
275 elf->section_header_offset = *((uint64_t *)ptr);
276 ptr += 8;
277 } else {
278 elf->entry_addr = *((uint32_t *)ptr);
279 ptr += 4;
280 elf->program_header_offset = *((uint32_t *)ptr);
281 ptr += 4;
282 elf->section_header_offset = *((uint32_t *)ptr);
283 ptr += 4;
284 }
285
286 // skip over flags
287 ptr += 4;
288
289 uint16_t header_size = *((uint16_t*)ptr);
290 if ((elf->is_64 && header_size != 64) || (!elf->is_64 && header_size != 52)) {
291 return ErrorInvalidFormat;
292 }
293 ptr += 2;
294
295 uint16_t ph_entry_size = *((uint16_t*)ptr);
296 ptr += 2;
297 uint16_t ph_entry_count = *((uint16_t*)ptr);
298 ptr += 2;
299 uint16_t sh_entry_size = *((uint16_t*)ptr);
300 ptr += 2;
301 uint16_t sh_entry_count = *((uint16_t*)ptr);
302 ptr += 2;
303 uint16_t sh_name_index = *((uint16_t*)ptr);
304 ptr += 2;
305
306 elf->string_section_index = sh_name_index;
307 if (elf->string_section_index >= sh_entry_count) {
308 return ErrorInvalidFormat;
309 }
310
311 long sh_byte_count = ((long) sh_entry_size) * ((long) sh_entry_count);
312 long end_sh = ((long)elf->section_header_offset) + sh_byte_count;
313 long end_ph = ((long)elf->program_header_offset) + ((long) ph_entry_size) * ((long) ph_entry_count);
314
315 if (len < end_sh || len < end_ph) {
316 return ErrorInvalidFormat;
317 }
318
319 ptr = &buf_ptr(buf)[elf->section_header_offset];
320 if (elf->is_64) {
321 if (sh_entry_size != sizeof(Elf64SectionHeader)) {
322 return ErrorInvalidFormat;
323 }
324
325 elf->elf64_section_headers = allocate<Elf64SectionHeader>(sh_entry_count);
326 memcpy(elf->elf64_section_headers, ptr, sh_byte_count);
327 elf->sh_entry_count = sh_entry_count;
328
329 Elf64SectionHeader *string_section = &elf->elf64_section_headers[elf->string_section_index];
330 if (string_section->type != SHT_STRTAB) {
331 // not a string table
332 return ErrorInvalidFormat;
333 }
334
335 // validate section types and offsets
336 for (int i = 0; i < elf->sh_entry_count; i += 1) {
337 Elf64SectionHeader *section = &elf->elf64_section_headers[i];
338
339 if (section->type != SHT_NOBITS) {
340 long file_end_offset = ((long)section->offset) + ((long)section->size);
341 if (len < file_end_offset) {
342 return ErrorInvalidFormat;
343 }
344 }
345
346 }
347
348 for (int i = 0; i < elf->sh_entry_count; i += 1) {
349 Elf64SectionHeader *section = &elf->elf64_section_headers[i];
350 if (section->type == SHT_NULL)
351 continue;
352 long name_offset = section->name;
353 ptr = &buf_ptr(buf)[string_section->offset + name_offset];
354 fprintf(stderr, "section: %s\n", ptr);
355 fprintf(stderr, " start: 0x%" PRIx64 "\n", (uint64_t)section->offset);
356 fprintf(stderr, " end: 0x%" PRIx64 "\n", (uint64_t)section->offset + (uint64_t)section->size);
357
358 if (section->type == SHT_SYMTAB) {
359 fprintf(stderr, " symtab\n");
360 } else if (section->type == SHT_DYNSYM) {
361 fprintf(stderr, " dynsym\n");
362 zig_panic("TODO SHT_DYNSYM");
363 }
364 }
365
366
367 } else {
368 if (sh_entry_size != sizeof(Elf32SectionHeader))
369 return ErrorInvalidFormat;
370
371 zig_panic("TODO 32-bit ELF");
372 }
373
374
375
376 return 0;
377}
378
379static int link(const char *arg0, const char *in_file, const char *out_file) {
380 if (!in_file || !out_file)
381 return usage(arg0);
382
383 FILE *in_f;
384 if (strcmp(in_file, "-") == 0) {
385 in_f = stdin;
386 } else {
387 in_f = fopen(in_file, "rb");
388 if (!in_f)
389 zig_panic("unable to open %s for reading: %s\n", in_file, strerror(errno));
390 }
391 Buf *in_data = fetch_file(in_f);
392
393 Elf elf = {0};
394 int err;
395 if ((err = parse_elf(&elf, in_data))) {
396 fprintf(stderr, "unable to parse ELF: %s\n", err_str(err));
397 return 1;
398 }
399
400 fprintf(stderr, "ELF is 64? %d\n", (int)elf.is_64);
401 fprintf(stderr, "arch: %d\n", (int)elf.arch);
402 fprintf(stderr, "exe? %d\n", (int)(elf.type == ElfTypeExecutable));
403 fprintf(stderr, "entry: 0x%" PRIx64 "\n", elf.entry_addr);
404
405
406
407 return 0;
408}
409
410enum Cmd {
411 CmdNone,
412 CmdBuild,
413 CmdLink,
414};
415
416int main(int argc, char **argv) {
417 char *arg0 = argv[0];
418 char *in_file = NULL;
419 char *out_file = NULL;
420 ZigList<char *> include_paths = {0};
421
422 Cmd cmd = CmdNone;
423 for (int i = 1; i < argc; i += 1) {
424 char *arg = argv[i];
425 if (arg[0] == '-' && arg[1] == '-') {
426 if (strcmp(arg, "--version") == 0) {
427 printf("%s\n", ZIG_VERSION_STRING);
428 return EXIT_SUCCESS;
429 } else if (i + 1 >= argc) {
430 return usage(arg0);
431 } else {
432 i += 1;
433 if (strcmp(arg, "--output") == 0) {
434 out_file = argv[i];
435 } else {
436 return usage(arg0);
437 }
438 }
439 } else if (arg[0] == '-' && arg[1] == 'I') {
440 include_paths.append(arg + 2);
441 } else if (cmd == CmdNone) {
442 if (strcmp(arg, "build") == 0) {
443 cmd = CmdBuild;
444 } else if (strcmp(arg, "link") == 0) {
445 cmd = CmdLink;
446 } else {
447 fprintf(stderr, "Unrecognized command: %s\n", arg);
448 return usage(arg0);
449 }
450 } else {
451 switch (cmd) {
452 case CmdNone:
453 zig_panic("unreachable");
454 case CmdBuild:
455 if (!in_file) {
456 in_file = arg;
457 } else {
458 return usage(arg0);
459 }
460 break;
461 case CmdLink:
462 if (!in_file) {
463 in_file = arg;
464 } else {
465 return usage(arg0);
466 }
467 break;
468 }
469 }
470 }
471
472 switch (cmd) {
473 case CmdNone:
474 return usage(arg0);
475 case CmdBuild:
476 return build(arg0, in_file, out_file, &include_paths);
477 break;
478 case CmdLink:
479 return link(arg0, in_file, out_file);
480 break;
481 }
482}
483