// 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 //str string file_size_pos 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 } } 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) { offset := 0xff - int(abs(addr - g.buf.len))-1 g.write8(0x7e) 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(val int) { g.write8(0xe8) } fn (g mut Gen) syscall() { // g.write(0x050f) g.write8(0x0f) g.write8(0x05) } 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.jle(label) } 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() } 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) }