269 lines
		
	
	
		
			4.6 KiB
		
	
	
	
		
			V
		
	
	
			
		
		
	
	
			269 lines
		
	
	
		
			4.6 KiB
		
	
	
	
		
			V
		
	
	
| // Copyright (c) 2019 Alexander Medvednikov. All rights reserved.
 | |
| // Use of this source code is governed by an MIT license
 | |
| // that can be found in the LICENSE file.
 | |
| module x64
 | |
| 
 | |
| pub struct Gen {
 | |
| 	out_name             string
 | |
| mut:
 | |
| 	buf                  []byte
 | |
| 	sect_header_name_pos int
 | |
| 	offset               i64
 | |
| 	str_pos              []i64
 | |
| 	strings              []string // TODO use a map and don't duplicate strings
 | |
| 	file_size_pos        i64
 | |
| 	main_fn_addr         i64
 | |
| 	code_start_pos       i64 // location of the start of the assembly instructions
 | |
| 	fn_addr              map[string]i64
 | |
| 	// string_addr map[string]i64
 | |
| }
 | |
| 
 | |
| enum Register {
 | |
| 	eax
 | |
| 	edi
 | |
| 	rax
 | |
| 	rdi
 | |
| 	rsi
 | |
| 	edx
 | |
| 	rdx
 | |
| 	r12
 | |
| }
 | |
| 
 | |
| enum Size {
 | |
| 	_8
 | |
| 	_16
 | |
| 	_32
 | |
| 	_64
 | |
| }
 | |
| 
 | |
| pub fn new_gen(out_name string) &Gen {
 | |
| 	return &Gen{
 | |
| 		sect_header_name_pos: 0
 | |
| 		buf: []
 | |
| 		out_name: out_name
 | |
| 	}
 | |
| }
 | |
| 
 | |
| pub fn (g &Gen) pos() i64 {
 | |
| 	return g.buf.len
 | |
| }
 | |
| 
 | |
| fn (g mut Gen) write8(n int) {
 | |
| 	// write 1 byte
 | |
| 	g.buf << byte(n)
 | |
| }
 | |
| 
 | |
| fn (g mut Gen) write16(n int) {
 | |
| 	// write 2 bytes
 | |
| 	g.buf << byte(n)
 | |
| 	g.buf << byte(n>>8)
 | |
| }
 | |
| 
 | |
| fn (g mut Gen) write32(n int) {
 | |
| 	// write 4 bytes
 | |
| 	g.buf << byte(n)
 | |
| 	g.buf << byte(n>>8)
 | |
| 	g.buf << byte(n>>16)
 | |
| 	g.buf << byte(n>>24)
 | |
| }
 | |
| 
 | |
| fn (g mut Gen) write64(n i64) {
 | |
| 	// write 8 bytes
 | |
| 	g.buf << byte(n)
 | |
| 	g.buf << byte(n>>8)
 | |
| 	g.buf << byte(n>>16)
 | |
| 	g.buf << byte(n>>24)
 | |
| 	g.buf << byte(n>>32)
 | |
| 	g.buf << byte(n>>40)
 | |
| 	g.buf << byte(n>>48)
 | |
| 	g.buf << byte(n>>56)
 | |
| }
 | |
| 
 | |
| fn (g mut Gen) write64_at(n i64, at i64) {
 | |
| 	// write 8 bytes
 | |
| 	g.buf[at] = byte(n)
 | |
| 	g.buf[at + 1] = byte(n>>8)
 | |
| 	g.buf[at + 2] = byte(n>>16)
 | |
| 	g.buf[at + 3] = byte(n>>24)
 | |
| 	g.buf[at + 4] = byte(n>>32)
 | |
| 	g.buf[at + 5] = byte(n>>40)
 | |
| 	g.buf[at + 6] = byte(n>>48)
 | |
| 	g.buf[at + 7] = byte(n>>56)
 | |
| }
 | |
| 
 | |
| fn (g mut Gen) write_string(s string) {
 | |
| 	for c in s {
 | |
| 		g.write8(int(c))
 | |
| 	}
 | |
| }
 | |
| 
 | |
| fn (g mut Gen) inc(reg Register) {
 | |
| 	g.write16(0xff49)
 | |
| 	match reg {
 | |
| 		.r12 {
 | |
| 			g.write8(0xc4)
 | |
| 		}
 | |
| 		else {
 | |
| 			panic('unhandled inc $reg')
 | |
| 		}}
 | |
| }
 | |
| 
 | |
| fn (g mut Gen) cmp(reg Register, size Size, val i64) {
 | |
| 	g.write8(0x49)
 | |
| 	// Second byte depends on the size of the value
 | |
| 	match size {
 | |
| 		._8 {
 | |
| 			g.write8(0x83)
 | |
| 		}
 | |
| 		._32 {
 | |
| 			g.write8(0x81)
 | |
| 		}
 | |
| 		else {
 | |
| 			panic('unhandled cmp')
 | |
| 		}}
 | |
| 	// Third byte depends on the register being compared to
 | |
| 	match reg {
 | |
| 		.r12 {
 | |
| 			g.write8(0xfc)
 | |
| 		}
 | |
| 		else {
 | |
| 			panic('unhandled cmp')
 | |
| 		}}
 | |
| 	g.write8(int(val))
 | |
| }
 | |
| 
 | |
| fn abs(a i64) i64 {
 | |
| 	return if a < 0 { -a } else { a }
 | |
| }
 | |
| 
 | |
| fn (g mut Gen) jle(addr i64) {
 | |
| 	// Calculate the relative offset to jump to
 | |
| 	// (`addr` is absolute address)
 | |
| 	offset := 0xff - int(abs(addr - g.buf.len)) - 1
 | |
| 	g.write8(0x7e)
 | |
| 	g.write8(offset)
 | |
| }
 | |
| 
 | |
| fn (g mut Gen) jl(addr i64) {
 | |
| 	offset := 0xff - int(abs(addr - g.buf.len)) - 1
 | |
| 	g.write8(0x7c)
 | |
| 	g.write8(offset)
 | |
| }
 | |
| 
 | |
| fn (g &Gen) abs_to_rel_addr(addr i64) int {
 | |
| 	return int(abs(addr - g.buf.len)) - 1
 | |
| }
 | |
| 
 | |
| fn (g mut Gen) jmp(addr i64) {
 | |
| 	offset := 0xff - g.abs_to_rel_addr(addr)
 | |
| 	g.write8(0xe9)
 | |
| 	g.write8(offset)
 | |
| }
 | |
| 
 | |
| fn (g mut Gen) mov64(reg Register, val i64) {
 | |
| 	match reg {
 | |
| 		.rsi {
 | |
| 			g.write8(0x48)
 | |
| 			g.write8(0xbe)
 | |
| 		}
 | |
| 		else {
 | |
| 			println('unhandled mov $reg')
 | |
| 		}}
 | |
| 	g.write64(val)
 | |
| }
 | |
| 
 | |
| fn (g mut Gen) call(addr int) {
 | |
| 	// rel := g.abs_to_rel_addr(addr)
 | |
| 	// rel := 0xffffffff - int(abs(addr - g.buf.len))-1
 | |
| 	println('call addr=$addr rel_addr=$addr pos=$g.buf.len')
 | |
| 	g.write8(0xe8)
 | |
| 	g.write32(addr)
 | |
| }
 | |
| 
 | |
| fn (g mut Gen) syscall() {
 | |
| 	// g.write(0x050f)
 | |
| 	g.write8(0x0f)
 | |
| 	g.write8(0x05)
 | |
| }
 | |
| 
 | |
| pub fn (g mut Gen) ret() {
 | |
| 	g.write8(0xc3)
 | |
| }
 | |
| 
 | |
| // returns label's relative address
 | |
| pub fn (g mut Gen) gen_loop_start(from int) int {
 | |
| 	g.mov(.r12, from)
 | |
| 	label := g.buf.len
 | |
| 	g.inc(.r12)
 | |
| 	return label
 | |
| }
 | |
| 
 | |
| pub fn (g mut Gen) gen_loop_end(to int, label int) {
 | |
| 	g.cmp(.r12, ._8, to)
 | |
| 	g.jl(label)
 | |
| }
 | |
| 
 | |
| pub fn (g mut Gen) save_main_fn_addr() {
 | |
| 	g.main_fn_addr = g.buf.len
 | |
| }
 | |
| 
 | |
| pub fn (g mut Gen) gen_print(s string) {
 | |
| 	g.strings << s + '\n'
 | |
| 	// g.string_addr[s] = str_pos
 | |
| 	g.mov(.eax, 1)
 | |
| 	g.mov(.edi, 1)
 | |
| 	str_pos := g.buf.len + 2
 | |
| 	g.str_pos << str_pos
 | |
| 	g.mov64(.rsi, 0) // segment_start +  0x9f) // str pos // PLACEHOLDER
 | |
| 	g.mov(.edx, s.len + 1) // len
 | |
| 	g.syscall()
 | |
| }
 | |
| 
 | |
| pub fn (g mut Gen) gen_exit() {
 | |
| 	// Return 0
 | |
| 	g.mov(.edi, 0) // ret value
 | |
| 	g.mov(.eax, 60)
 | |
| 	g.syscall()
 | |
| }
 | |
| 
 | |
| fn (g mut Gen) mov(reg Register, val int) {
 | |
| 	match reg {
 | |
| 		.eax {
 | |
| 			g.write8(0xb8)
 | |
| 		}
 | |
| 		.edi {
 | |
| 			g.write8(0xbf)
 | |
| 		}
 | |
| 		.edx {
 | |
| 			g.write8(0xba)
 | |
| 		}
 | |
| 		.rsi {
 | |
| 			g.write8(0x48)
 | |
| 			g.write8(0xbe)
 | |
| 		}
 | |
| 		.r12 {
 | |
| 			g.write8(0x41)
 | |
| 			g.write8(0xbc) // r11 is 0xbb etc
 | |
| 		}
 | |
| 		else {
 | |
| 			panic('unhandled mov $reg')
 | |
| 		}}
 | |
| 	g.write32(val)
 | |
| }
 | |
| 
 | |
| pub fn (g mut Gen) register_function_address(name string) {
 | |
| 	addr := g.pos()
 | |
| 	println('reg fn addr $name $addr')
 | |
| 	g.fn_addr[name] = addr
 | |
| }
 | |
| 
 | |
| pub fn (g mut Gen) call_fn(name string) {
 | |
| 	if !name.contains('__') {
 | |
| 		return
 | |
| 	}
 | |
| 	addr := g.fn_addr[name]
 | |
| 	g.call(int(addr))
 | |
| 	println('call $name $addr')
 | |
| }
 | |
| 
 |