mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
native: implement a working hello world compilation for w64 (#12577)
This commit is contained in:
parent
04b030b7ab
commit
89bab98833
@ -71,6 +71,23 @@ fn get_all_commands() []Command {
|
||||
line: '$vexe run examples/v_script.vsh > /dev/null'
|
||||
okmsg: 'V can run the .VSH script file examples/v_script.vsh'
|
||||
}
|
||||
//
|
||||
res << Command{
|
||||
line: '$vexe -os linux -b native -o hw.linux examples/hello_world.v'
|
||||
okmsg: 'V compiles hello_world.v on the native backend for linux'
|
||||
rmfile: 'hw.linux'
|
||||
}
|
||||
res << Command{
|
||||
line: '$vexe -os macos -b native -o hw.macos examples/hello_world.v'
|
||||
okmsg: 'V compiles hello_world.v on the native backend for macos'
|
||||
rmfile: 'hw.macos'
|
||||
}
|
||||
res << Command{
|
||||
line: '$vexe -os windows -b native -o hw.exe examples/hello_world.v'
|
||||
okmsg: 'V compiles hello_world.v on the native backend for windows'
|
||||
rmfile: 'hw.exe'
|
||||
}
|
||||
//
|
||||
res << Command{
|
||||
line: '$vexe -b js -o hw.js examples/hello_world.v'
|
||||
okmsg: 'V compiles hello_world.v on the JS backend'
|
||||
|
@ -204,6 +204,14 @@ fn (mut g Gen) mov64(reg Register, val i64) {
|
||||
g.write8(0xc7)
|
||||
g.write8(0xc1)
|
||||
}
|
||||
.rdx {
|
||||
g.write8(0x48)
|
||||
g.write8(0xc7)
|
||||
g.write8(0xc2)
|
||||
g.write32(i32(int(val)))
|
||||
g.println('mov32 $reg, $val')
|
||||
return
|
||||
}
|
||||
.rbx {
|
||||
g.write8(0x48)
|
||||
g.write8(0xc7)
|
||||
@ -427,7 +435,8 @@ pub fn (mut g Gen) allocate_string(s string, opsize int) int {
|
||||
g.strings << s
|
||||
str_pos := g.buf.len + opsize
|
||||
g.str_pos << str_pos
|
||||
return 0
|
||||
g.strs << String{s, str_pos}
|
||||
return str_pos
|
||||
}
|
||||
|
||||
pub fn (mut g Gen) cld_repne_scasb() {
|
||||
@ -484,7 +493,45 @@ pub fn (mut g Gen) gen_print_reg(r Register, n int, fd int) {
|
||||
g.syscall()
|
||||
}
|
||||
|
||||
pub fn (mut g Gen) apicall(s string) {
|
||||
if g.pref.os != .windows {
|
||||
g.n_error('apicalls are only for windows')
|
||||
}
|
||||
g.write8(0xff)
|
||||
g.write8(0x15)
|
||||
delta := match s {
|
||||
'WriteFile' {
|
||||
-(0xbcc + g.buf.len)
|
||||
}
|
||||
'GetStdHandle' {
|
||||
-(0xbcc + g.buf.len + 8)
|
||||
}
|
||||
'ExitProcess' {
|
||||
-(0xbcc + g.buf.len + 16)
|
||||
}
|
||||
else {
|
||||
0
|
||||
}
|
||||
}
|
||||
g.write32(delta)
|
||||
}
|
||||
|
||||
pub fn (mut g Gen) gen_print(s string, fd int) {
|
||||
if g.pref.os == .windows {
|
||||
g.sub(.rsp, 0x38)
|
||||
g.mov(.rcx, -11)
|
||||
g.apicall('GetStdHandle')
|
||||
g.mov_reg(.rcx, .rax)
|
||||
// g.mov64(.rdx, g.allocate_string(s, 3))
|
||||
g.lea(.rdx, g.allocate_string(s, 3))
|
||||
g.mov(.r8, s.len) // string length
|
||||
g.write([byte(0x4c), 0x8d, 0x4c, 0x24, 0x20]) // lea r9, [rsp+0x20]
|
||||
g.write([byte(0x48), 0xc7, 0x44, 0x24, 0x20])
|
||||
g.write32(0) // mov qword[rsp+0x20], 0
|
||||
// g.mov(.r9, rsp+0x20)
|
||||
g.apicall('WriteFile')
|
||||
return
|
||||
}
|
||||
//
|
||||
// qq := s + '\n'
|
||||
//
|
||||
@ -554,8 +601,21 @@ pub fn (mut g Gen) gen_amd64_exit(expr ast.Expr) {
|
||||
g.n_error('native builtin exit expects a numeric argument')
|
||||
}
|
||||
}
|
||||
g.mov(.eax, g.nsyscall_exit())
|
||||
g.syscall()
|
||||
if g.pref.os == .windows {
|
||||
g.mov_reg(.rcx, .rdi)
|
||||
g.apicall('ExitProcess')
|
||||
} else {
|
||||
g.mov(.eax, g.nsyscall_exit())
|
||||
g.syscall()
|
||||
}
|
||||
g.trap() // should never be reached, just in case
|
||||
}
|
||||
|
||||
fn (mut g Gen) lea(reg Register, val int) {
|
||||
g.write8(0x48)
|
||||
g.write8(0x8d)
|
||||
g.write8(0x15)
|
||||
g.write32(val)
|
||||
}
|
||||
|
||||
fn (mut g Gen) mov(reg Register, val int) {
|
||||
@ -569,10 +629,15 @@ fn (mut g Gen) mov(reg Register, val int) {
|
||||
return
|
||||
}
|
||||
.rcx {
|
||||
g.write8(0x48)
|
||||
g.write8(0xc7)
|
||||
g.write8(0xc1)
|
||||
g.write32(-1)
|
||||
if val == -1 {
|
||||
g.write8(0x48)
|
||||
g.write8(0xc7)
|
||||
g.write8(0xc1)
|
||||
g.write32(-1)
|
||||
} else {
|
||||
g.write8(0xff)
|
||||
g.write8(0xff) // mov rcx 0xffff5
|
||||
}
|
||||
return
|
||||
}
|
||||
else {
|
||||
@ -629,7 +694,16 @@ fn (mut g Gen) mov(reg Register, val int) {
|
||||
g.write8(0xbf)
|
||||
}
|
||||
.rcx {
|
||||
g.write8(0x48)
|
||||
g.write8(0xc7)
|
||||
g.write8(0xc1)
|
||||
}
|
||||
.r8 {
|
||||
g.write8(0x41)
|
||||
g.write8(0xb8)
|
||||
}
|
||||
.r9 {
|
||||
g.write8(0xb9)
|
||||
}
|
||||
.rdx, .edx {
|
||||
g.write8(0xba)
|
||||
@ -738,6 +812,14 @@ fn (mut g Gen) mov_reg(a Register, b Register) {
|
||||
g.write8(0x48)
|
||||
g.write8(0x89)
|
||||
g.write8(0xf8)
|
||||
} else if a == .rcx && b == .rdi {
|
||||
g.write8(0x48)
|
||||
g.write8(0x89)
|
||||
g.write8(0xf9)
|
||||
} else if a == .rcx && b == .rax {
|
||||
g.write8(0x48)
|
||||
g.write8(0x89)
|
||||
g.write8(0xc1)
|
||||
} else if a == .rdi && b == .rsi {
|
||||
g.write8(0x48)
|
||||
g.write8(0x89)
|
||||
@ -1076,7 +1158,11 @@ fn (mut g Gen) infix_expr(node ast.InfixExpr) {
|
||||
|
||||
fn (mut g Gen) trap() {
|
||||
// funnily works on x86 and arm64
|
||||
g.write32(0xcccccccc)
|
||||
if g.pref.arch == .arm64 {
|
||||
g.write32(0xcccccccc)
|
||||
} else {
|
||||
g.write8(0xcc)
|
||||
}
|
||||
g.println('trap')
|
||||
}
|
||||
|
||||
|
@ -47,6 +47,12 @@ mut:
|
||||
size_pos []int
|
||||
nlines int
|
||||
callpatches []CallPatch
|
||||
strs []String
|
||||
}
|
||||
|
||||
struct String {
|
||||
str string
|
||||
pos int
|
||||
}
|
||||
|
||||
struct CallPatch {
|
||||
@ -176,6 +182,12 @@ pub fn (g &Gen) pos() i64 {
|
||||
return g.buf.len
|
||||
}
|
||||
|
||||
fn (mut g Gen) write(bytes []byte) {
|
||||
for _, b in bytes {
|
||||
g.buf << b
|
||||
}
|
||||
}
|
||||
|
||||
fn (mut g Gen) write8(n int) {
|
||||
// write 1 byte
|
||||
g.buf << byte(n)
|
||||
|
@ -395,9 +395,14 @@ fn (mut g Gen) write_symbol(s Symbol) {
|
||||
fn (mut g Gen) sym_string_table() int {
|
||||
begin := g.buf.len
|
||||
g.zeroes(1)
|
||||
at := i64(0x100000000)
|
||||
for i, s in g.strings {
|
||||
g.write64_at(at + g.buf.len, int(g.str_pos[i]))
|
||||
pos := g.buf.len - int(g.str_pos[i])
|
||||
if g.pref.os == .windows {
|
||||
g.write32_at(int(g.str_pos[i]), pos - 4) // 0x402028 + pos)
|
||||
} else {
|
||||
baddr := i64(0x100000000)
|
||||
g.write64_at(g.buf.len + baddr, int(g.str_pos[i]))
|
||||
}
|
||||
g.write_string(s)
|
||||
g.write8(0)
|
||||
}
|
||||
|
@ -1,9 +1,16 @@
|
||||
module native
|
||||
|
||||
enum PeCharacteristics {
|
||||
executable_image = 0x102
|
||||
// 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
|
||||
@ -18,14 +25,14 @@ enum PeHeader {
|
||||
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
|
||||
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
|
||||
0xb8, // initial SP
|
||||
0x140, // initial SP
|
||||
0, // checksum
|
||||
0, // IP
|
||||
0, // IP relative CS
|
||||
@ -47,14 +54,14 @@ pub fn (mut g Gen) write_dos_header() {
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0xf8,
|
||||
0x80,
|
||||
0, // address of PE header
|
||||
]
|
||||
for b in dos_header {
|
||||
g.write16(b)
|
||||
}
|
||||
if g.buf.len != 0x40 {
|
||||
// g.warning('Invalid dos header size')
|
||||
g.n_error('Invalid dos header size')
|
||||
}
|
||||
}
|
||||
|
||||
@ -65,9 +72,46 @@ pub fn (mut g Gen) write_dos_stub() {
|
||||
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
|
||||
}
|
||||
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([byte(0), 0, 96])
|
||||
g.zeroes(52)
|
||||
g.write([byte(72), 16, 0, 0])
|
||||
g.write([byte(40), 16, 0, 0])
|
||||
g.zeroes(20)
|
||||
g.write([byte(96), 16, 0, 0])
|
||||
g.write32(0)
|
||||
g.write([byte(110), 16, 0, 0])
|
||||
g.write32(0)
|
||||
g.write([byte(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() {
|
||||
@ -75,7 +119,7 @@ pub fn (mut g Gen) write_pe_header() {
|
||||
int(PeHeader.pe),
|
||||
0,
|
||||
int(PeMachine.amd64), // machine
|
||||
1, // number of sections
|
||||
2, // number of sections
|
||||
0,
|
||||
0, // timestamp
|
||||
0,
|
||||
@ -83,55 +127,59 @@ pub fn (mut g Gen) write_pe_header() {
|
||||
0, // number of symbols
|
||||
0,
|
||||
0xf0, // 40 // size of optional header
|
||||
int(PeCharacteristics.executable_image), // c
|
||||
// 0 // optional header magic
|
||||
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 != 0x110 {
|
||||
eprintln('Invalid optdr location')
|
||||
if p_opthdr != 0x98 {
|
||||
eprintln('Invalid optdr location $p_opthdr != 0x98')
|
||||
}
|
||||
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
|
||||
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
|
||||
|
||||
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(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(6) // Major OS Version
|
||||
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(6) // major subsystem version
|
||||
g.write16(5) // 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(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(0x100000) // SizeOfStackReserve
|
||||
*/
|
||||
g.write64(0x1000) // SizeOfStackReserve
|
||||
g.write64(0x1000) // SizeOfStackCommit
|
||||
g.write64(0x100000) // SizeOfHeapReserve
|
||||
g.write64(0x1000) // SizeOfHeapCommit
|
||||
g.write64(0x10000) // SizeOfHeapReserve
|
||||
g.write64(0) // SizeOfHeapCommit
|
||||
g.write32(0) // LoaderFlags
|
||||
g.write32(1) // NumberOfRvaAndSizes
|
||||
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() {
|
||||
@ -155,30 +203,35 @@ 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 []byte, len int) {
|
||||
for buf.len < len {
|
||||
buf << byte(0)
|
||||
}
|
||||
}
|
||||
|
||||
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.sym_string_table()
|
||||
pad_to(mut g.buf, 0x600)
|
||||
g.file_size_pos = g.buf.len
|
||||
g.main_fn_addr = g.buf.len
|
||||
g.code_start_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()
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user