diff --git a/vlib/v/builder/native.v b/vlib/v/builder/native.v index c2176bee3c..8029b2a3de 100644 --- a/vlib/v/builder/native.v +++ b/vlib/v/builder/native.v @@ -5,7 +5,9 @@ import v.util import v.gen.native pub fn (mut b Builder) build_native(v_files []string, out_file string) { - if b.pref.os !in [.linux, .macos] { + if b.pref.os == .windows { + eprintln('Warning: v -native is experimental for Windows') + } else if b.pref.os !in [.linux, .macos] { eprintln('Warning: v -native can only generate macOS and Linux binaries for now') } b.front_and_middle_stages(v_files) or { return } diff --git a/vlib/v/gen/native/amd64.v b/vlib/v/gen/native/amd64.v index 732c2b7d54..cb3896e6cb 100644 --- a/vlib/v/gen/native/amd64.v +++ b/vlib/v/gen/native/amd64.v @@ -508,11 +508,14 @@ fn (mut g Gen) nsyscall_write() int { .linux { return 1 } + .windows { + return 0 + } .macos { return 0x2000004 } else { - g.n_error('unsupported exit syscall for this platform') + g.n_error('unsupported write syscall for this platform') } } return 0 @@ -526,6 +529,9 @@ fn (mut g Gen) nsyscall_exit() int { .macos { return 0x2000001 } + .windows { + return 0 + } else { g.n_error('unsupported exit syscall for this platform') } diff --git a/vlib/v/gen/native/gen.v b/vlib/v/gen/native/gen.v index 4f243e2ade..0ca3768532 100644 --- a/vlib/v/gen/native/gen.v +++ b/vlib/v/gen/native/gen.v @@ -109,6 +109,9 @@ pub fn (mut g Gen) generate_header() { .macos { g.generate_macho_header() } + .windows { + g.generate_pe_header() + } .linux { g.generate_elf_header() } @@ -137,6 +140,9 @@ pub fn (mut g Gen) generate_footer() { .macos { g.generate_macho_footer() } + .windows { + g.generate_pe_footer() + } .linux { g.generate_elf_footer() } diff --git a/vlib/v/gen/native/pe.v b/vlib/v/gen/native/pe.v new file mode 100644 index 0000000000..d23794d1f6 --- /dev/null +++ b/vlib/v/gen/native/pe.v @@ -0,0 +1,184 @@ +module native + +enum PeCharacteristics { + executable_image = 0x102 +} + +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), + 0x90, // usedbytesinthelastpage + 3, // filesizeinpages + 0, // numofrelocs + 2, // header size in paragraph + 0, // minimum extra paragraphs + -1, // maximum extra paragraphs + 0, // initial relative ss + 0xb8, // 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, + 0xf8, + 0, // address of PE header + ] + for b in dos_header { + g.write16(b) + } + if g.buf.len != 0x40 { + // g.warning('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 + for g.buf.len < 0xf8 { + g.write8(0) // entries + } +} + +pub fn (mut g Gen) write_pe_header() { + pe_header := [ + int(PeHeader.pe), + 0, + int(PeMachine.amd64), // machine + 1, // 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), // c + // 0 // optional header magic + ] + for b in pe_header { + g.write16(b) + } + // optional here comes here + p_opthdr := g.buf.len // should be 0x110 + if p_opthdr != 0x110 { + eprintln('Invalid optdr location') + } + g.write16(0x20b) // magic (0x10b=pe32, 0x20b=pe64) + g.write8(0xe) // major linker version + g.write8(0x1d) // minor linker version + g.write32(0x10) // sizeofcode + g.write32(0x10) // initial data size + g.write32(0) // sizeof sizeof uninit data + + image_base := i64(0x140000000) + g.write32(0x1188) // paddr of map // aligned to 4 bytes // entrypoint + g.write32(0x1000) // base of code // aligned to 4 bytes + g.write64(image_base) // image base vaddr // va // aligned to 4 bytes + g.write32(0x1000) // SectionAlignment + g.write32(0x200) // FileAlignment + g.write16(6) // Major OS Version + g.write16(0) // Minor OS Version + g.write16(0) // major image version + g.write16(0) // minor image version + g.write16(6) // major subsystem version + g.write16(0) // minor subsystem version + + g.write32(0) // win32versionvalue + g.write32(0x1000) // hdrsize + codelen) // sizeofimage + g.write32(0x180) // hdrsize) // sizeofheaders + + g.write32(0) // checksum + g.write16(3) // subsystem // subsystem + // g.write16(0x400) // dll characteristics + g.write8(0x60) // dll characteristics + g.write8(0x81) // dll characteristics + g.write64(0x100000) // SizeOfStackReserve + g.write64(0x1000) // SizeOfStackCommit + g.write64(0x100000) // SizeOfHeapReserve + g.write64(0x1000) // SizeOfHeapCommit + g.write32(0) // LoaderFlags + g.write32(1) // NumberOfRvaAndSizes + + g.write32(0) + g.write32(0) +} + +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.code_start_pos = g.buf.len + + g.call(0x18e) + g.ret() +} + +pub fn (mut g Gen) generate_pe_footer() { + /* + // TODO: when proper code is generated, uncomment this + codesize := g.buf.len - g.code_start_pos + delta := int(g.code_start_pos) // header_size + // patch the size depending on the codesize + for o in g.size_pos { + n := g.read32_at(o) + g.write32_at(o, n + delta) + } + */ + for g.buf.len < 0x200 { + g.write8(0) // entries + } + + g.write_pe_section() + g.file_size_pos = g.buf.len + g.main_fn_addr = g.buf.len + g.code_start_pos = g.buf.len + g.create_executable() +}