//: Loading SubX programs from ELF binaries. //: This will allow us to run them natively on a Linux kernel. //: Based on https://github.com/kragen/stoneknifeforth/blob/702d2ebe1b/386.c :(before "End Main") assert(argc > 1); if (is_equal(argv[1], "run")) { // Outside of tests, traces must be explicitly requested. if (Trace_file.is_open()) Trace_stream = new trace_stream; trace(2, "run") << "=== Starting to run" << end(); if (argc <= 2) { raise << "Not enough arguments provided.\n" << die(); } reset(); cerr << std::hex; load_elf(argv[2], argc, argv); while (EIP < End_of_program) // weak final-gasp termination check run_one_instruction(); raise << "executed past end of the world: " << EIP << " vs " << End_of_program << '\n' << end(); return 1; } :(code) void load_elf(const string& filename, int argc, char* argv[]) { int fd = open(filename.c_str(), O_RDONLY); if (fd < 0) raise << filename.c_str() << ": open" << perr() << '\n' << die(); off_t size = lseek(fd, 0, SEEK_END); lseek(fd, 0, SEEK_SET); uint8_t* elf_contents = static_cast(malloc(size)); if (elf_contents == NULL) raise << "malloc(" << size << ')' << perr() << '\n' << die(); ssize_t read_size = read(fd, elf_contents, size); if (size != read_size) raise << "read → " << size << " (!= " << read_size << ')' << perr() << '\n' << die(); load_elf_contents(elf_contents, size, argc, argv); free(elf_contents); } void load_elf_contents(uint8_t* elf_contents, size_t size, int argc, char* argv[]) { uint8_t magic[5] = {0}; memcpy(magic, elf_contents, 4); if (memcmp(magic, "\177ELF", 4) != 0) raise << "Invalid ELF file; starts with \"" << magic << '"' << die(); if (elf_contents[4] != 1) raise << "Only 32-bit ELF files (4-byte words; virtual addresses up to 4GB) supported.\n" << die(); if (elf_contents[5] != 1) raise << "Only little-endian ELF files supported.\n" << die(); // unused: remaining 10 bytes of e_ident uint32_t e_machine_type = u32_in(&elf_contents[16]); if (e_machine_type != 0x00030002) raise << "ELF type/machine 0x" << HEXWORD << e_machine_type << " isn't i386 executable\n" << die(); // unused: e_version. We only support version 1, and later versions will be backwards compatible. uint32_t e_entry = u32_in(&elf_contents[24]); uint32_t e_phoff = u32_in(&elf_contents[28]); // unused: e_shoff // unused: e_flags uint32_t e_ehsize = u16_in(&elf_contents[40]); if (e_ehsize < 52) raise << "Invalid binary; ELF header too small\n" << die(); uint32_t e_phentsize = u16_in(&elf_contents[42]); uint32_t e_phnum = u16_in(&elf_contents[44]); trace(90, "load") << e_phnum << " entries in the program header, each " << e_phentsize << " bytes long" << end(); // unused: e_shentsize // unused: e_shnum // unused: e_shstrndx set overlap; // to detect overlapping segments for (size_t i = 0; i < e_phnum; ++i) load_segment_from_program_header(elf_contents, i, size, e_phoff + i*e_phentsize, e_ehsize, overlap); // initialize code and stack assert(overlap.find(STACK_SEGMENT) == overlap.end()); Mem.push_back(vma(STACK_SEGMENT)); assert(overlap.find(AFTER_STACK) == overlap.end()); // The stack grows downward. Reg[ESP].u = AFTER_STACK; Reg[EBP].u = 0; EIP = e_entry; // initialize args on stack // no envp for now // we wastefully use a separate page of memory for argv Mem.push_back(vma(ARGV_DATA_SEGMENT)); uint32_t argv_data = ARGV_DATA_SEGMENT; for (int i = argc-1; i >= /*skip 'subx_bin' and 'run'*/2; --i) { push(argv_data); for (size_t j = 0; j <= strlen(argv[i]); ++j) { assert(overlap.find(argv_data) == overlap.end()); // don't bother comparing ARGV and STACK write_mem_u8(argv_data, argv[i][j]); argv_data += sizeof(char); assert(argv_data < ARGV_DATA_SEGMENT + SEGMENT_ALIGNMENT); } } push(argc-/*skip 'subx_bin' and 'run'*/2); } void push(uint32_t val) { Reg[ESP].u -= 4; if (Reg[ESP].u < STACK_SEGMENT) { raise << "The stack overflowed its segment. " << "Maybe SPACE_FOR_SEGMENT should be larger? " << "Or you need to carve out an exception for the stack segment " << "to be larger.\n" << die(); } trace(Callstack_depth+1, "run") << "decrementing ESP to 0x" << HEXWORD << Reg[ESP].u << end(); trace(Callstack_depth+1, "run") << "pushing value 0x" << HEXWORD << val << end(); write_mem_u