v/vlib/v/gen/native/pe.v

238 lines
5.2 KiB
V

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()
}