native: generate relocatable ELFs and support relative strings (#13671)
parent
0fd4c699dd
commit
b20c911d3e
|
@ -458,11 +458,9 @@ pub fn (mut g Gen) gen_loop_end(to int, label int) {
|
|||
g.jl(label)
|
||||
}
|
||||
|
||||
pub fn (mut g Gen) allocate_string(s string, opsize int) int {
|
||||
g.strings << s
|
||||
pub fn (mut g Gen) allocate_string(s string, opsize int, typ RelocType) int {
|
||||
str_pos := g.buf.len + opsize
|
||||
g.str_pos << str_pos
|
||||
g.strs << String{s, str_pos}
|
||||
g.strs << String{s, str_pos, typ}
|
||||
return str_pos
|
||||
}
|
||||
|
||||
|
@ -507,7 +505,7 @@ pub fn (mut g Gen) inline_strlen(r Register) {
|
|||
|
||||
// TODO: strlen of string at runtime
|
||||
pub fn (mut g Gen) gen_print_reg(r Register, n int, fd int) {
|
||||
mystrlen := true
|
||||
mystrlen := true // if n < 0 maybe?
|
||||
g.mov_reg(.rsi, r)
|
||||
if mystrlen {
|
||||
g.inline_strlen(.rsi)
|
||||
|
@ -550,24 +548,20 @@ pub fn (mut g Gen) gen_print(s string, fd int) {
|
|||
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.lea(.rdx, g.allocate_string(s, 3, .abs64))
|
||||
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'
|
||||
//
|
||||
} else {
|
||||
g.mov(.eax, g.nsyscall_write())
|
||||
g.mov(.edi, fd)
|
||||
// segment_start + 0x9f) // str pos // placeholder
|
||||
g.mov64(.rsi, g.allocate_string(s, 2)) // for rsi its 2
|
||||
g.learel(.rsi, g.allocate_string(s, 3, .rel32)) // for rsi its 2
|
||||
g.mov(.edx, s.len) // len
|
||||
g.syscall()
|
||||
}
|
||||
}
|
||||
|
||||
fn (mut g Gen) nsyscall_write() int {
|
||||
|
@ -638,6 +632,24 @@ pub fn (mut g Gen) gen_amd64_exit(expr ast.Expr) {
|
|||
g.trap() // should never be reached, just in case
|
||||
}
|
||||
|
||||
fn (mut g Gen) learel(reg Register, val int) {
|
||||
g.write8(0x48)
|
||||
g.write8(0x8d)
|
||||
match reg {
|
||||
.rax {
|
||||
g.write8(0x05)
|
||||
}
|
||||
.rsi {
|
||||
g.write8(0x35)
|
||||
}
|
||||
else {
|
||||
g.n_error('learel must use rsi or rax')
|
||||
}
|
||||
}
|
||||
g.write32(val)
|
||||
g.println('lea $reg, rip + $val')
|
||||
}
|
||||
|
||||
fn (mut g Gen) lea(reg Register, val int) {
|
||||
g.write8(0x48)
|
||||
g.write8(0x8d)
|
||||
|
@ -1109,7 +1121,7 @@ g.v_error('oops', node.pos)
|
|||
pos += 8
|
||||
}
|
||||
ast.StringLiteral {
|
||||
g.mov64(.rsi, g.allocate_string('$e.val', 2)) // for rsi its 2
|
||||
g.mov64(.rsi, g.allocate_string('$e.val', 2, .abs64)) // for rsi its 2
|
||||
g.mov_reg_to_var(pos, .rsi)
|
||||
pos += 8
|
||||
}
|
||||
|
@ -1148,7 +1160,7 @@ g.v_error('oops', node.pos)
|
|||
ast.StringLiteral {
|
||||
dest := g.allocate_var(name, 4, 0)
|
||||
ie := node.right[i] as ast.StringLiteral
|
||||
g.mov64(.rsi, g.allocate_string(ie.str(), 2)) // for rsi its 2
|
||||
g.mov64(.rsi, g.allocate_string(ie.str(), 2, .abs64)) // for rsi its 2
|
||||
g.mov_reg_to_var(dest, .rsi)
|
||||
}
|
||||
ast.CallExpr {
|
||||
|
@ -1254,6 +1266,10 @@ fn (mut g Gen) gen_asm_stmt_amd64(asm_node ast.AsmStmt) {
|
|||
line += a.val.str()
|
||||
imm = if a.val { 1 } else { 0 }
|
||||
}
|
||||
ast.CharLiteral {
|
||||
line += a.val.str()
|
||||
imm = a.val.int()
|
||||
}
|
||||
/*
|
||||
ast.AsmAddressing {
|
||||
}
|
||||
|
@ -1261,8 +1277,6 @@ fn (mut g Gen) gen_asm_stmt_amd64(asm_node ast.AsmStmt) {
|
|||
}
|
||||
ast.AsmDisp {
|
||||
}
|
||||
ast.CharLiteral {
|
||||
}
|
||||
ast.FloatLiteral {
|
||||
}
|
||||
*/
|
||||
|
@ -1489,7 +1503,6 @@ fn (mut g Gen) for_stmt(node ast.ForStmt) {
|
|||
return
|
||||
}
|
||||
infix_expr := node.cond as ast.InfixExpr
|
||||
// g.mov(.eax, 0x77777777)
|
||||
mut jump_addr := 0 // location of `jne *00 00 00 00*`
|
||||
start := g.pos()
|
||||
match mut infix_expr.left {
|
||||
|
|
|
@ -4,58 +4,82 @@
|
|||
module native
|
||||
|
||||
const (
|
||||
mag0 = byte(0x7f)
|
||||
mag1 = `E`
|
||||
mag2 = `L`
|
||||
mag3 = `F`
|
||||
ei_class = 4
|
||||
elfclass64 = 2
|
||||
elfdata2lsb = 1
|
||||
ev_current = 1
|
||||
)
|
||||
elf_class32 = 1
|
||||
elf_class64 = 2
|
||||
|
||||
// ELF file types
|
||||
const (
|
||||
elf_osabi = 0
|
||||
et_rel = 1
|
||||
et_exec = 2
|
||||
et_dyn = 3
|
||||
e_machine_amd64 = 0x3e
|
||||
e_machine_arm64 = 0xb7
|
||||
shn_xindex = 0xffff
|
||||
sht_null = 0
|
||||
elf_data_le = 1
|
||||
elf_data_be = 2
|
||||
|
||||
elf_version = 1
|
||||
elf_abiversion = 0
|
||||
|
||||
elf_type_rel = 1
|
||||
elf_type_exec = 2
|
||||
elf_type_dyn = 3
|
||||
|
||||
elf_amd64 = 0x3e
|
||||
elf_arm64 = 0xb7
|
||||
|
||||
elf_osabi_none = 0
|
||||
elf_osabi_hpux = 1
|
||||
elf_osabi_netbsd = 2
|
||||
elf_osabi_linux = 3
|
||||
elf_osabi_freebsd = 9
|
||||
|
||||
elf_header_size = 0x40
|
||||
elf_phentry_size = 0x38
|
||||
)
|
||||
|
||||
const (
|
||||
segment_start = 0x400000
|
||||
placeholder = 0
|
||||
sevens = 0x77777777
|
||||
)
|
||||
|
||||
/*
|
||||
struct Header64 {
|
||||
ident u8 // File identification.
|
||||
@type u16 // File type.
|
||||
machine u16 // Machine architecture.
|
||||
version u32 // ELF format version.
|
||||
entry u64 // Entry point.
|
||||
phoff u64 // Program header file offset.
|
||||
shoff u64 // Section header file offset.
|
||||
flags u32 // Architecture-specific flags.
|
||||
ehsize u16 // Size of ELF header in bytes.
|
||||
phentsize u16 // Size of program header entry.
|
||||
phnum u16 // Number of program header entries.
|
||||
shentsize u16 // Size of section header entry.
|
||||
shnum u16 // Number of section header entries.
|
||||
shstrndx u16 // Section name strings section.
|
||||
}
|
||||
*/
|
||||
|
||||
pub fn (mut g Gen) generate_elf_header() {
|
||||
g.buf << [byte(native.mag0), native.mag1, native.mag2, native.mag3]
|
||||
g.buf << native.elfclass64 // file class
|
||||
g.buf << native.elfdata2lsb // data encoding
|
||||
g.buf << native.ev_current // file version
|
||||
g.buf << native.elf_osabi
|
||||
g.write64(0) // et_rel) // et_rel for .o
|
||||
g.write16(2) // e_type
|
||||
elf_type := native.elf_type_dyn // PIE (use _exec for non-relocatable executables)
|
||||
|
||||
g.buf << '\x7fELF'.bytes()
|
||||
g.buf << native.elf_class64
|
||||
g.buf << native.elf_data_le
|
||||
g.buf << native.elf_version
|
||||
g.buf << native.elf_osabi_none
|
||||
g.write64(0) // abiversion(1)+pad(6)+nident(1)
|
||||
g.write16(elf_type)
|
||||
|
||||
if g.pref.arch == .arm64 {
|
||||
g.write16(native.e_machine_arm64)
|
||||
g.write16(native.elf_arm64)
|
||||
} else {
|
||||
g.write16(native.e_machine_amd64)
|
||||
g.write16(native.elf_amd64)
|
||||
}
|
||||
g.write32(native.ev_current) // e_version
|
||||
eh_size := 0x40
|
||||
phent_size := 0x38
|
||||
g.write64(native.segment_start + eh_size + phent_size) // e_entry
|
||||
g.write64(0x40) // e_phoff
|
||||
g.write32(native.elf_version)
|
||||
g.write64(native.segment_start + native.elf_header_size + native.elf_phentry_size) // e_entry
|
||||
g.write64(native.elf_header_size) // e_phoff
|
||||
g.write64(0) // e_shoff
|
||||
g.write32(0) // e_flags
|
||||
g.write16(eh_size) // e_ehsize
|
||||
g.write16(phent_size) // e_phentsize
|
||||
g.write16(native.elf_header_size)
|
||||
g.write16(native.elf_phentry_size)
|
||||
g.write16(1) // e_phnum
|
||||
g.write16(0) // e_shentsize
|
||||
// e_shnum := g.buf.len
|
||||
g.write16(0) // e_shnum (number of sections)
|
||||
g.write16(0) // e_shstrndx
|
||||
// Elf64_Phdr
|
||||
|
@ -68,6 +92,16 @@ pub fn (mut g Gen) generate_elf_header() {
|
|||
g.write64(0) // p_filesz PLACEHOLDER, set to file_size later // addr: 060
|
||||
g.write64(0) // p_memsz
|
||||
g.write64(0x1000) // p_align
|
||||
// write sections
|
||||
/*
|
||||
sections := []string{}
|
||||
g.write16_at(e_shnum, sections.len) // e_shnum (number of sections)
|
||||
|
||||
for section in sections {
|
||||
// write section data
|
||||
println('section $section')
|
||||
}
|
||||
*/
|
||||
// user code starts here at
|
||||
// address: 00070 and a half
|
||||
if g.pref.is_verbose {
|
||||
|
@ -79,24 +113,30 @@ pub fn (mut g Gen) generate_elf_header() {
|
|||
g.println('; call fn main')
|
||||
}
|
||||
|
||||
pub fn (mut g Gen) generate_elf_footer() {
|
||||
// Return 0
|
||||
/*
|
||||
g.mov(.edi, 0) // ret value
|
||||
g.mov(.eax, 60)
|
||||
g.syscall()
|
||||
*/
|
||||
// Strings table
|
||||
// Loop thru all strings and set the right addresses
|
||||
for i, s in g.strings {
|
||||
g.write64_at(native.segment_start + g.buf.len, int(g.str_pos[i]))
|
||||
g.write_string(s)
|
||||
g.write8(0)
|
||||
fn (mut g Gen) elf_string_table() {
|
||||
for _, s in g.strs {
|
||||
match s.typ {
|
||||
.abs64 {
|
||||
// g.write64_at(native.segment_start + g.buf.len, int(g.str_pos[i]))
|
||||
g.write64_at(s.pos, g.buf.len)
|
||||
}
|
||||
// Now we know the file size, set it
|
||||
.rel32 {
|
||||
g.write32_at(s.pos, g.buf.len - s.pos - 4)
|
||||
}
|
||||
else {
|
||||
g.n_error('unsupported string reloc type')
|
||||
}
|
||||
}
|
||||
g.write_string(s.str)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn (mut g Gen) generate_elf_footer() {
|
||||
g.elf_string_table()
|
||||
// file_size holds the address at the end of the code and const strings table
|
||||
file_size := g.buf.len
|
||||
g.write64_at(file_size, g.file_size_pos) // set file size 64 bit value
|
||||
g.write64_at(file_size, g.file_size_pos + 8)
|
||||
g.write64_at(g.file_size_pos, file_size) // set file size 64 bit value
|
||||
g.write64_at(g.file_size_pos + 8, file_size)
|
||||
if g.pref.arch == .arm64 {
|
||||
bl_next := u32(0x94000001)
|
||||
g.write32_at(g.code_start_pos, int(bl_next))
|
||||
|
|
|
@ -31,9 +31,7 @@ mut:
|
|||
buf []byte
|
||||
sect_header_name_pos int
|
||||
offset i64
|
||||
str_pos []i64
|
||||
stackframe_size int
|
||||
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
|
||||
|
@ -50,9 +48,18 @@ mut:
|
|||
strs []String
|
||||
}
|
||||
|
||||
enum RelocType {
|
||||
rel8
|
||||
rel16
|
||||
rel32
|
||||
rel64
|
||||
abs64
|
||||
}
|
||||
|
||||
struct String {
|
||||
str string
|
||||
pos int
|
||||
typ RelocType
|
||||
}
|
||||
|
||||
struct CallPatch {
|
||||
|
@ -168,7 +175,10 @@ pub fn (mut g Gen) generate_footer() {
|
|||
.raw {
|
||||
g.create_executable()
|
||||
}
|
||||
else {}
|
||||
else {
|
||||
eprintln('Unsupported target file format')
|
||||
exit(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -224,7 +234,7 @@ fn (mut g Gen) write64(n i64) {
|
|||
g.buf << byte(n >> 56)
|
||||
}
|
||||
|
||||
fn (mut g Gen) write64_at(n i64, at i64) {
|
||||
fn (mut g Gen) write64_at(at i64, n i64) {
|
||||
// write 8 bytes
|
||||
g.buf[at] = byte(n)
|
||||
g.buf[at + 1] = byte(n >> 8)
|
||||
|
@ -244,11 +254,17 @@ fn (mut g Gen) write32_at(at i64, n int) {
|
|||
g.buf[at + 3] = byte(n >> 24)
|
||||
}
|
||||
|
||||
fn (mut g Gen) write16_at(at i64, n int) {
|
||||
// write 2 bytes
|
||||
g.buf[at] = byte(n)
|
||||
g.buf[at + 1] = byte(n >> 8)
|
||||
}
|
||||
|
||||
fn (mut g Gen) write_string(s string) {
|
||||
for c in s {
|
||||
g.write8(int(c))
|
||||
}
|
||||
// g.write8(0) // null terminated strings
|
||||
g.zeroes(1)
|
||||
}
|
||||
|
||||
fn (mut g Gen) write_string_with_padding(s string, max int) {
|
||||
|
@ -288,15 +304,16 @@ pub fn (mut g Gen) gen_print_from_expr(expr ast.Expr, name string) {
|
|||
g.gen_print_reg(.rax, 3, fd)
|
||||
}
|
||||
ast.IntegerLiteral {
|
||||
g.mov64(.rax, g.allocate_string('$expr.val\n', 2))
|
||||
g.learel(.rax, g.allocate_string('$expr.val\n', 3, .rel32))
|
||||
g.gen_print_reg(.rax, 3, fd)
|
||||
}
|
||||
ast.BoolLiteral {
|
||||
// register 'true' and 'false' strings // g.expr(expr)
|
||||
// XXX mov64 shuoldnt be used for addressing
|
||||
if expr.val {
|
||||
g.mov64(.rax, g.allocate_string('true', 2))
|
||||
g.learel(.rax, g.allocate_string('true', 3, .rel32))
|
||||
} else {
|
||||
g.mov64(.rax, g.allocate_string('false', 2))
|
||||
g.learel(.rax, g.allocate_string('false', 3, .rel32))
|
||||
}
|
||||
g.gen_print_reg(.rax, 3, fd)
|
||||
}
|
||||
|
@ -310,7 +327,7 @@ pub fn (mut g Gen) gen_print_from_expr(expr ast.Expr, name string) {
|
|||
mut off := 0
|
||||
for f in s.fields {
|
||||
if f.name == field_name {
|
||||
g.mov64(.rax, g.allocate_string('$off\n', 2))
|
||||
g.learel(.rax, g.allocate_string('$off\n', 3, .rel32))
|
||||
g.gen_print_reg(.rax, 3, fd)
|
||||
break
|
||||
}
|
||||
|
@ -591,7 +608,7 @@ fn (mut g Gen) stmt(node ast.Stmt) {
|
|||
ast.StringLiteral {
|
||||
s = e0.val.str()
|
||||
g.expr(node.exprs[0])
|
||||
g.mov64(.rax, g.allocate_string(s, 2))
|
||||
g.mov64(.rax, g.allocate_string(s, 2, .abs64))
|
||||
}
|
||||
ast.Ident {
|
||||
g.expr(e0)
|
||||
|
@ -643,7 +660,7 @@ fn (mut g Gen) gen_syscall(node ast.CallExpr) {
|
|||
match expr.expr {
|
||||
ast.StringLiteral {
|
||||
s := expr.expr.val.replace('\\n', '\n')
|
||||
g.allocate_string(s, 2)
|
||||
g.allocate_string(s, 2, .abs64)
|
||||
g.mov64(ra[i], 1)
|
||||
done = true
|
||||
}
|
||||
|
@ -660,7 +677,7 @@ fn (mut g Gen) gen_syscall(node ast.CallExpr) {
|
|||
node.pos)
|
||||
}
|
||||
s := expr.val.replace('\\n', '\n')
|
||||
g.allocate_string(s, 2)
|
||||
g.allocate_string(s, 2, .abs64)
|
||||
g.mov64(ra[i], 1)
|
||||
}
|
||||
else {
|
||||
|
|
|
@ -395,16 +395,23 @@ fn (mut g Gen) write_symbol(s Symbol) {
|
|||
fn (mut g Gen) sym_string_table() int {
|
||||
begin := g.buf.len
|
||||
g.zeroes(1)
|
||||
for i, s in g.strings {
|
||||
pos := g.buf.len - int(g.str_pos[i])
|
||||
for _, s in g.strs {
|
||||
pos := g.buf.len - s.pos - 4
|
||||
match s.typ {
|
||||
.rel32 {
|
||||
g.write32_at(s.pos, pos)
|
||||
}
|
||||
else {
|
||||
if g.pref.os == .windows {
|
||||
g.write32_at(int(g.str_pos[i]), pos - 4) // 0x402028 + pos)
|
||||
// that should be .rel32, not windows-specific
|
||||
g.write32_at(s.pos, pos)
|
||||
} else {
|
||||
baddr := i64(0x100000000)
|
||||
g.write64_at(g.buf.len + baddr, int(g.str_pos[i]))
|
||||
g.write64_at(s.pos, g.buf.len + baddr)
|
||||
}
|
||||
g.write_string(s)
|
||||
g.write8(0)
|
||||
}
|
||||
}
|
||||
g.write_string(s.str)
|
||||
}
|
||||
return g.buf.len - begin
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue