| ... | ... | @@ -11,6 +11,7 @@ |
| 11 | 11 | #include "buffer.hpp" |
| 12 | 12 | #include "parser.hpp" |
| 13 | 13 | #include "tokenizer.hpp" |
| 14 | #include "error.hpp" |
| 14 | 15 | |
| 15 | 16 | #include <stdio.h> |
| 16 | 17 | #include <string.h> |
| ... | ... | @@ -21,12 +22,17 @@ |
| 21 | 22 | #include <sys/types.h> |
| 22 | 23 | #include <sys/stat.h> |
| 23 | 24 | #include <unistd.h> |
| 25 | #include <inttypes.h> |
| 24 | 26 | |
| 25 | | static 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" |
| 27 | static 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" |
| 30 | 36 | , arg0); |
| 31 | 37 | return EXIT_FAILURE; |
| 32 | 38 | } |
| ... | ... | @@ -49,37 +55,8 @@ static Buf *fetch_file(FILE *f) { |
| 49 | 55 | return buf; |
| 50 | 56 | } |
| 51 | 57 | |
| 52 | | char cur_dir[1024]; |
| 53 | | |
| 54 | | int 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 | | } |
| 58 | static int build(const char *arg0, const char *in_file, const char *out_file, ZigList<char *> *include_paths) { |
| 59 | static char cur_dir[1024]; |
| 83 | 60 | |
| 84 | 61 | if (!in_file || !out_file) |
| 85 | 62 | return usage(arg0); |
| ... | ... | @@ -116,5 +93,391 @@ int main(int argc, char **argv) { |
| 116 | 93 | ast_print(root, 0); |
| 117 | 94 | |
| 118 | 95 | |
| 119 | | return EXIT_SUCCESS; |
| 96 | return 0; |
| 120 | 97 | } |
| 98 | |
| 99 | enum ElfType { |
| 100 | ElfTypeRelocatable, |
| 101 | ElfTypeExecutable, |
| 102 | ElfTypeShared, |
| 103 | ElfTypeCore, |
| 104 | }; |
| 105 | |
| 106 | enum ElfArch { |
| 107 | ElfArchSparc, |
| 108 | ElfArchx86, |
| 109 | ElfArchMips, |
| 110 | ElfArchPowerPc, |
| 111 | ElfArchArm, |
| 112 | ElfArchSuperH, |
| 113 | ElfArchIA_64, |
| 114 | ElfArchx86_64, |
| 115 | ElfArchAArch64, |
| 116 | }; |
| 117 | |
| 118 | |
| 119 | enum 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 | |
| 145 | struct 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 | |
| 158 | struct 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 | |
| 171 | struct 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 | |
| 187 | static 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 | |
| 379 | static 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 | |
| 410 | enum Cmd { |
| 411 | CmdNone, |
| 412 | CmdBuild, |
| 413 | CmdLink, |
| 414 | }; |
| 415 | |
| 416 | int 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 | |