x64: make hello world run

pull/4518/head
Alexander Medvednikov 2020-04-20 07:02:09 +02:00
parent 19a5436118
commit efff96d622
2 changed files with 110 additions and 114 deletions

View File

@ -6,29 +6,33 @@ module x64
import os import os
const ( const (
mag0 = 0x7f mag0 = byte(0x7f)
mag1 = `E` mag1 = `E`
mag2 = `L` mag2 = `L`
mag3 = `F` mag3 = `F`
ei_class = 4 ei_class = 4
elfclass64 = 2 elfclass64 = 2
elfdata2lsb = 1 elfdata2lsb = 1
ev_current = 1 ev_current = 1
elf_osabi = 0 )
// ELF file types
et_rel = 1 // ELF file types
et_exec = 2 const (
et_dyn = 3 elf_osabi = 0
e_machine = 0x3e et_rel = 1
et_exec = 2
et_dyn = 3
e_machine = 0x3e
shn_xindex = 0xffff shn_xindex = 0xffff
sht_null = 0 sht_null = 0
) )
const ( const (
segment_start = 0x400000 segment_start = 0x400000
PLACEHOLDER = 0
) )
pub fn (g mut Gen) generate_elf_header() { pub fn (var g Gen) generate_elf_header() {
g.buf << [byte(mag0), mag1, mag2, mag3] g.buf << [byte(mag0), mag1, mag2, mag3]
g.buf << elfclass64 // file class g.buf << elfclass64 // file class
g.buf << elfdata2lsb // data encoding g.buf << elfdata2lsb // data encoding
@ -63,14 +67,16 @@ pub fn (g mut Gen) generate_elf_header() {
// user code starts here at // user code starts here at
// address: 00070 and a half // address: 00070 and a half
g.code_start_pos = g.buf.len g.code_start_pos = g.buf.len
g.call(0) // call main function, it's not guaranteed to be the first g.call(PLACEHOLDER) // call main function, it's not guaranteed to be the first, we don't know its address yet
} }
pub fn (g mut Gen) generate_elf_footer() { pub fn (var g Gen) generate_elf_footer() {
// Return 0 // Return 0
/*
g.mov(.edi, 0) // ret value g.mov(.edi, 0) // ret value
g.mov(.eax, 60) g.mov(.eax, 60)
g.syscall() g.syscall()
*/
// Strings table // Strings table
// Loop thru all strings and set the right addresses // Loop thru all strings and set the right addresses
for i, s in g.strings { for i, s in g.strings {
@ -84,12 +90,12 @@ pub fn (g mut Gen) generate_elf_footer() {
g.write64_at(file_size, g.file_size_pos + 8) g.write64_at(file_size, g.file_size_pos + 8)
// call main function, it's not guaranteed to be the first // call main function, it's not guaranteed to be the first
// we generated call(0) ("e8 0") // we generated call(0) ("e8 0")
// no need to replace "0" with a relative address of the main function // now need to replace "0" with a relative address of the main function
// +1 is for "e8" // +1 is for "e8"
// -5 is for "e8 00 00 00 00" // -5 is for "e8 00 00 00 00"
g.write64_at(int(g.main_fn_addr - g.code_start_pos) - 5, g.code_start_pos + 1) g.write32_at(g.code_start_pos + 1, int(g.main_fn_addr - g.code_start_pos) - 5)
// Create the binary // Create the binary
mut f := os.create(g.out_name) or { var f := os.create(g.out_name) or {
panic(err) panic(err)
} }
os.chmod(g.out_name, 0o775) // make it an executable os.chmod(g.out_name, 0o775) // make it an executable

View File

@ -3,11 +3,8 @@
// that can be found in the LICENSE file. // that can be found in the LICENSE file.
module x64 module x64
import ( import v.ast
v.ast import v.util
v.util
// term
)
pub struct Gen { pub struct Gen {
out_name string out_name string
@ -21,9 +18,9 @@ mut:
main_fn_addr i64 main_fn_addr i64
code_start_pos i64 // location of the start of the assembly instructions code_start_pos i64 // location of the start of the assembly instructions
fn_addr map[string]i64 fn_addr map[string]i64
// string_addr map[string]i64
} }
// string_addr map[string]i64
enum Register { enum Register {
eax eax
edi edi
@ -43,10 +40,8 @@ enum Size {
} }
pub fn gen(files []ast.File, out_name string) { pub fn gen(files []ast.File, out_name string) {
mut g := Gen{ var g := Gen{
sect_header_name_pos: 0 sect_header_name_pos: 0
// buf: []
out_name: out_name out_name: out_name
} }
g.generate_elf_header() g.generate_elf_header()
@ -68,104 +63,100 @@ pub fn new_gen(out_name string) &Gen {
} }
} }
*/ */
pub fn (g &Gen) pos() i64 { pub fn (g &Gen) pos() i64 {
return g.buf.len return g.buf.len
} }
fn (g mut Gen) write8(n int) { fn (var g Gen) write8(n int) {
// write 1 byte // write 1 byte
g.buf << byte(n) g.buf << byte(n)
} }
fn (g mut Gen) write16(n int) { fn (var g Gen) write16(n int) {
// write 2 bytes // write 2 bytes
g.buf << byte(n) g.buf << byte(n)
g.buf << byte(n>>8) g.buf << byte(n >> 8)
} }
fn (g mut Gen) write32(n int) { fn (var g Gen) write32(n int) {
// write 4 bytes // write 4 bytes
g.buf << byte(n) g.buf << byte(n)
g.buf << byte(n>>8) g.buf << byte(n >> 8)
g.buf << byte(n>>16) g.buf << byte(n >> 16)
g.buf << byte(n>>24) g.buf << byte(n >> 24)
} }
fn (g mut Gen) write64(n i64) { fn (var g Gen) write64(n i64) {
// write 8 bytes // write 8 bytes
g.buf << byte(n) g.buf << byte(n)
g.buf << byte(n>>8) g.buf << byte(n >> 8)
g.buf << byte(n>>16) g.buf << byte(n >> 16)
g.buf << byte(n>>24) g.buf << byte(n >> 24)
g.buf << byte(n>>32) g.buf << byte(n >> 32)
g.buf << byte(n>>40) g.buf << byte(n >> 40)
g.buf << byte(n>>48) g.buf << byte(n >> 48)
g.buf << byte(n>>56) g.buf << byte(n >> 56)
} }
fn (g mut Gen) write64_at(n i64, at i64) { fn (var g Gen) write64_at(n, at i64) {
// write 8 bytes // write 8 bytes
g.buf[at] = byte(n) g.buf[at] = byte(n)
g.buf[at + 1] = byte(n>>8) g.buf[at + 1] = byte(n >> 8)
g.buf[at + 2] = byte(n>>16) g.buf[at + 2] = byte(n >> 16)
g.buf[at + 3] = byte(n>>24) g.buf[at + 3] = byte(n >> 24)
g.buf[at + 4] = byte(n>>32) g.buf[at + 4] = byte(n >> 32)
g.buf[at + 5] = byte(n>>40) g.buf[at + 5] = byte(n >> 40)
g.buf[at + 6] = byte(n>>48) g.buf[at + 6] = byte(n >> 48)
g.buf[at + 7] = byte(n>>56) g.buf[at + 7] = byte(n >> 56)
} }
fn (g mut Gen) write_string(s string) { fn (var g Gen) write32_at(at i64, n int) {
// write 4 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)
}
fn (var g Gen) write_string(s string) {
for c in s { for c in s {
g.write8(int(c)) g.write8(int(c))
} }
} }
fn (g mut Gen) inc(reg Register) { fn (var g Gen) inc(reg Register) {
g.write16(0xff49) g.write16(0xff49)
match reg { match reg {
.r12 { .r12 { g.write8(0xc4) }
g.write8(0xc4) else { panic('unhandled inc $reg') }
}
else {
panic('unhandled inc $reg')
}
} }
} }
fn (g mut Gen) cmp(reg Register, size Size, val i64) { fn (var g Gen) cmp(reg Register, size Size, val i64) {
g.write8(0x49) g.write8(0x49)
// Second byte depends on the size of the value // Second byte depends on the size of the value
match size { match size {
._8 { ._8 { g.write8(0x83) }
g.write8(0x83) ._32 { g.write8(0x81) }
} else { panic('unhandled cmp') }
._32 {
g.write8(0x81)
}
else {
panic('unhandled cmp')
}
} }
// Third byte depends on the register being compared to // Third byte depends on the register being compared to
match reg { match reg {
.r12 { .r12 { g.write8(0xfc) }
g.write8(0xfc) else { panic('unhandled cmp') }
}
else {
panic('unhandled cmp')
}
} }
g.write8(int(val)) g.write8(int(val))
} }
fn abs(a i64) i64 { fn abs(a i64) i64 {
return if a < 0 { -a } else { a } return if a < 0 {
-a
} else {
a
}
} }
fn (g mut Gen) jle(addr i64) { fn (var g Gen) jle(addr i64) {
// Calculate the relative offset to jump to // Calculate the relative offset to jump to
// (`addr` is absolute address) // (`addr` is absolute address)
offset := 0xff - int(abs(addr - g.buf.len)) - 1 offset := 0xff - int(abs(addr - g.buf.len)) - 1
@ -173,7 +164,7 @@ fn (g mut Gen) jle(addr i64) {
g.write8(offset) g.write8(offset)
} }
fn (g mut Gen) jl(addr i64) { fn (var g Gen) jl(addr i64) {
offset := 0xff - int(abs(addr - g.buf.len)) - 1 offset := 0xff - int(abs(addr - g.buf.len)) - 1
g.write8(0x7c) g.write8(0x7c)
g.write8(offset) g.write8(offset)
@ -183,13 +174,13 @@ fn (g &Gen) abs_to_rel_addr(addr i64) int {
return int(abs(addr - g.buf.len)) - 1 return int(abs(addr - g.buf.len)) - 1
} }
fn (g mut Gen) jmp(addr i64) { fn (var g Gen) jmp(addr i64) {
offset := 0xff - g.abs_to_rel_addr(addr) offset := 0xff - g.abs_to_rel_addr(addr)
g.write8(0xe9) g.write8(0xe9)
g.write8(offset) g.write8(offset)
} }
fn (g mut Gen) mov64(reg Register, val i64) { fn (var g Gen) mov64(reg Register, val i64) {
match reg { match reg {
.rsi { .rsi {
g.write8(0x48) g.write8(0x48)
@ -202,7 +193,7 @@ fn (g mut Gen) mov64(reg Register, val i64) {
g.write64(val) g.write64(val)
} }
fn (g mut Gen) call(addr int) { fn (var g Gen) call(addr int) {
// rel := g.abs_to_rel_addr(addr) // rel := g.abs_to_rel_addr(addr)
// rel := 0xffffffff - int(abs(addr - g.buf.len))-1 // rel := 0xffffffff - int(abs(addr - g.buf.len))-1
println('call addr=$addr rel_addr=$addr pos=$g.buf.len') println('call addr=$addr rel_addr=$addr pos=$g.buf.len')
@ -210,52 +201,49 @@ fn (g mut Gen) call(addr int) {
g.write32(addr) g.write32(addr)
} }
fn (g mut Gen) syscall() { fn (var g Gen) syscall() {
// g.write(0x050f) // g.write(0x050f)
g.write8(0x0f) g.write8(0x0f)
g.write8(0x05) g.write8(0x05)
} }
pub fn (g mut Gen) ret() { pub fn (var g Gen) ret() {
g.write8(0xc3) g.write8(0xc3)
} }
// returns label's relative address // returns label's relative address
pub fn (g mut Gen) gen_loop_start(from int) int { pub fn (var g Gen) gen_loop_start(from int) int {
g.mov(.r12, from) g.mov(.r12, from)
label := g.buf.len label := g.buf.len
g.inc(.r12) g.inc(.r12)
return label return label
} }
pub fn (g mut Gen) gen_loop_end(to int, label int) { pub fn (var g Gen) gen_loop_end(to, label int) {
g.cmp(.r12, ._8, to) g.cmp(.r12, ._8, to)
g.jl(label) g.jl(label)
} }
pub fn (g mut Gen) save_main_fn_addr() { pub fn (var g Gen) save_main_fn_addr() {
g.main_fn_addr = g.buf.len g.main_fn_addr = g.buf.len
} }
pub fn (g mut Gen) gen_print_from_expr(expr ast.Expr, newline bool) { pub fn (var g Gen) gen_print_from_expr(expr ast.Expr, newline bool) {
match expr { match expr {
ast.StringLiteral { ast.StringLiteral { if newline {
if newline { g.gen_print(it.val + '\n')
g.gen_print(it.val+'\n') } else {
}
else {
g.gen_print(it.val) g.gen_print(it.val)
} } }
}
else {} else {}
} }
} }
pub fn (g mut Gen) gen_print(s string) { pub fn (var g Gen) gen_print(s string) {
// //
// qq := s + '\n' // qq := s + '\n'
// //
g.strings << s + '\n' g.strings << s // + '\n'
// g.string_addr[s] = str_pos // g.string_addr[s] = str_pos
g.mov(.eax, 1) g.mov(.eax, 1)
g.mov(.edi, 1) g.mov(.edi, 1)
@ -266,14 +254,14 @@ pub fn (g mut Gen) gen_print(s string) {
g.syscall() g.syscall()
} }
pub fn (g mut Gen) gen_exit() { pub fn (var g Gen) gen_exit() {
// Return 0 // Return 0
g.mov(.edi, 0) // ret value g.mov(.edi, 0) // ret value
g.mov(.eax, 60) g.mov(.eax, 60)
g.syscall() g.syscall()
} }
fn (g mut Gen) mov(reg Register, val int) { fn (var g Gen) mov(reg Register, val int) {
match reg { match reg {
.eax { .eax {
g.write8(0xb8) g.write8(0xb8)
@ -299,17 +287,19 @@ fn (g mut Gen) mov(reg Register, val int) {
g.write32(val) g.write32(val)
} }
pub fn (g mut Gen) register_function_address(name string) { pub fn (var g Gen) register_function_address(name string) {
addr := g.pos() addr := g.pos()
// println('reg fn addr $name $addr') // println('reg fn addr $name $addr')
g.fn_addr[name] = addr g.fn_addr[name] = addr
} }
pub fn (g &Gen) write(s string) {} pub fn (g &Gen) write(s string) {
}
pub fn (g &Gen) writeln(s string) {} pub fn (g &Gen) writeln(s string) {
}
pub fn (g mut Gen) call_fn(name string) { pub fn (var g Gen) call_fn(name string) {
if !name.contains('__') { if !name.contains('__') {
return return
} }
@ -318,7 +308,7 @@ pub fn (g mut Gen) call_fn(name string) {
println('call $name $addr') println('call $name $addr')
} }
fn (g mut Gen) stmt(node ast.Stmt) { fn (var g Gen) stmt(node ast.Stmt) {
match node { match node {
ast.ConstDecl {} ast.ConstDecl {}
ast.FnDecl { ast.FnDecl {
@ -326,15 +316,17 @@ fn (g mut Gen) stmt(node ast.Stmt) {
if is_main { if is_main {
g.save_main_fn_addr() g.save_main_fn_addr()
} }
for arg in it.args {} for arg in it.args {
}
for stmt in it.stmts { for stmt in it.stmts {
g.stmt(stmt) g.stmt(stmt)
} }
if is_main { if is_main {
println('end of main: gen exit') println('end of main: gen exit')
g.gen_exit() g.gen_exit()
// g.write32(0x88888888)
} }
g.ret() // g.ret()
} }
ast.Return {} ast.Return {}
ast.AssignStmt {} ast.AssignStmt {}
@ -344,12 +336,12 @@ fn (g mut Gen) stmt(node ast.Stmt) {
g.expr(it.expr) g.expr(it.expr)
} }
else { else {
verror('x64.stmt(): bad node') println('x64.stmt(): bad node')
} }
} }
} }
fn (g mut Gen) expr(node ast.Expr) { fn (var g Gen) expr(node ast.Expr) {
// println('cgen expr()') // println('cgen expr()')
match node { match node {
ast.AssignExpr {} ast.AssignExpr {}
@ -359,8 +351,7 @@ fn (g mut Gen) expr(node ast.Expr) {
ast.UnaryExpr { ast.UnaryExpr {
g.expr(it.left) g.expr(it.left)
} }
*/ */
ast.StringLiteral {} ast.StringLiteral {}
ast.InfixExpr {} ast.InfixExpr {}
// `user := User{name: 'Bob'}` // `user := User{name: 'Bob'}`
@ -379,8 +370,7 @@ fn (g mut Gen) expr(node ast.Expr) {
} }
} }
g.write(')') g.write(')')
*/ */
} }
ast.ArrayInit {} ast.ArrayInit {}
ast.Ident {} ast.Ident {}