module native enum PeCharacteristics { // 1: relocation info stripped // 2: file is executable // 4: line numbers stripped // 8: local symbols stripped // 20: app can handle > 2GB executable_image = 0x2f } const image_base = i64(0x400000) enum PeMachine { i386 = 0x14c amd64 = 0x8664 arm64 = 0xaa64 } enum PeHeader { mz = 0x5a4d pe = 0x4550 } pub fn (mut g Gen) write_dos_header() { dos_header := [ int(PeHeader.mz), 0x80, // bytes on last page of file 1, // pages in file 0, // relocations 4, // header size in paragraph 0x10, // minimum extra paragraphs 0xffff, // maximum extra paragraphs 0, // initial relative ss 0x140, // initial SP 0, // checksum 0, // IP 0, // IP relative CS 0x40, // address of relocation table 0, // overlay number 0, 0, 0, 0, // reserved 0, // oemid 0, // oeminfo 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x80, 0, // address of PE header ] for b in dos_header { g.write16(b) } if g.buf.len != 0x40 { g.n_error('Invalid dos header size') } } // size is 0xb8 pub fn (mut g Gen) write_dos_stub() { g.write8(0x0e) // richend g.write8(0x1f) // richend g.write8(0xba) // richend g.write8(0x0e) // richend // TODO: add a stub for DOS/16, we only run on amd64 pad_to(mut g.buf, 0x80) } fn (mut g Gen) write_pe_sections() { pad_to(mut g.buf, 0x180) g.write64(0) g.write_string_with_padding('.idata', 8) g.write32(0x89) // 137 g.write16(0x1000) g.write32(0x02000000) g.write32(0x02000000) g.zeroes(14) g.write16(64) g.write8(0) g.write8(192) g.write_string_with_padding('.text', 8) g.write32(75) g.write8(0) g.write32(0x20) g.write32(0x00002) g.write32(4) g.write32(0) g.write32(0) g.write32(0x20000000) // 0, 0, 0, 32, g.write([u8(0), 0, 96]) g.zeroes(52) g.write([u8(72), 16, 0, 0]) g.write([u8(40), 16, 0, 0]) g.zeroes(20) g.write([u8(96), 16, 0, 0]) g.write32(0) g.write([u8(110), 16, 0, 0]) g.write32(0) g.write([u8(125), 16, 0, 0]) g.zeroes(12) g.write_string_with_padding('KERNEL32.DLL', 13) g.write_string_with_padding('USER32.DLL', 13) g.write_string_with_padding('ExitProcess', 14) g.write_string_with_padding('GetStdHandle', 15) g.write_string_with_padding('WriteFile', 13) } pub fn (mut g Gen) write_pe_header() { pe_header := [ int(PeHeader.pe), 0, int(PeMachine.amd64), // machine 2, // number of sections 0, 0, // timestamp 0, 0, // symbol table 0, // number of symbols 0, 0xf0, // 40 // size of optional header int(PeCharacteristics.executable_image), ] for b in pe_header { g.write16(b) } // optional here comes here p_opthdr := g.buf.len // should be 0x110 if p_opthdr != 0x98 { eprintln('Invalid optdr location $p_opthdr != 0x98') } g.write16(0x20b) // magic (0x10b=pe32, 0x20b=pe32+) g.write8(0x1) // major linker version g.write8(0x49) // minor linker version g.write32(0x200) // sizeofcode g.write32(0x200) // initial data size g.write32(0) // sizeof uninit data g.write32(0x2000) // paddr of map // aligned to 4 bytes // entrypoint g.write32(0x2000) // base of code // aligned to 4 bytes g.write64(native.image_base) // image base vaddr // va // aligned to 4 bytes g.write32(0x1000) // SectionAlignment g.write32(0x200) // FileAlignment g.write16(1) // Major OS Version g.write16(0) // Minor OS Version g.write16(0) // major image version g.write16(0) // minor image version g.write16(5) // major subsystem version g.write16(0) // minor subsystem version g.write32(0) // win32versionvalue g.write32(0x3000) // hdrsize + codelen) // sizeofimage g.write32(0x200) // hdrsize) // sizeofheaders g.write32(0) // checksum g.write16(3) // subsystem // subsystem // g.write16(0x400) // dll characteristics g.write16(0) /* g.write8(0x60) // dll characteristics g.write8(0x81) // dll characteristics */ g.write64(0x1000) // SizeOfStackReserve g.write64(0x1000) // SizeOfStackCommit g.write64(0x10000) // SizeOfHeapReserve g.write64(0) // SizeOfHeapCommit g.write32(0) // LoaderFlags g.write32(0x10) // NumberOfRvaAndSizes g.write32(0) g.write32(0) g.write32(0x1000) g.write32(0x100) // size of code } fn (mut g Gen) write_pe_section() { // directory entries.. aka sections // section header g.write_string_with_padding('.text', 8) g.write32(0x0) // pa g.size_pos << g.buf.len g.write32(0x1000) // va g.write32(0x400) // sizeofrawdata // g.patch_code << g.buf.len g.write32(0) // ptr-torawdata g.write32(0) // relocs g.write32(0) // linenums g.write16(0) // nrelocs g.write16(0) // nlinenums g.write32(0) // characteristics } pub fn (mut g Gen) generate_pe_header() { g.write_dos_header() g.write_dos_stub() g.write_pe_header() g.write_pe_sections() pad_to(mut g.buf, 0x400) g.code_start_pos = g.buf.len g.call(0x18e) g.ret() g.main_fn_addr = g.buf.len } fn pad_to(mut buf []u8, len int) { for buf.len < len { buf << u8(0) } } pub fn (mut g Gen) generate_pe_footer() { g.sym_string_table() pad_to(mut g.buf, 0x600) g.file_size_pos = g.buf.len // patch call main if g.pref.arch == .arm64 { bl_next := u32(0x94000001) g.write32_at(g.code_start_pos, int(bl_next)) } else { // +1 is for "e8" // -5 is for "e8 00 00 00 00" g.write32_at(g.code_start_pos + 1, int(g.main_fn_addr - g.code_start_pos) - 5) } g.create_executable() }