native: implement a working hello world compilation for w64 (#12577)
							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