1
0
mirror of https://github.com/vlang/v.git synced 2023-08-10 21:13:21 +03:00

native: support simple library calls (#15958)

This commit is contained in:
Spydr 2022-10-03 16:49:22 +02:00 committed by GitHub
parent 9fc64de94b
commit 6ac9552d39
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 167 additions and 65 deletions

View File

@ -866,6 +866,20 @@ fn (mut g Gen) call(addr int) i64 {
return c_addr
}
fn (mut g Gen) extern_call(addr int) {
match g.pref.os {
.linux {
g.write8(0xff)
g.write8(0x15)
g.write32(0)
g.println('call *@GOTPCREL(%rip)')
}
else {
g.n_error('extern calls are not implemented for $g.pref.os')
}
}
}
fn (mut g Gen) syscall() {
// g.write(0x050f)
g.write8(0x0f)
@ -1725,7 +1739,10 @@ pub fn (mut g Gen) call_fn_amd64(node ast.CallExpr) {
for i in 0 .. reg_size {
g.pop(native.fn_arg_registers[i])
}
if addr == 0 {
if node.name in g.extern_symbols {
g.extern_fn_calls[g.pos()] = node.name
g.extern_call(int(addr))
} else if addr == 0 {
g.delay_fn_call(n)
g.call(int(0))
} else {

View File

@ -87,7 +87,7 @@ const (
elf_sh_symtab_entsize = elf_symtab_size
elf_sh_symtab_align = 8
// elf symbol bining
// elf symbol binding
elf_stb_local = u8(0)
elf_stb_global = u8(1)
@ -123,6 +123,7 @@ const (
elf_r_amd64_gotpc32 = 26
elf_r_amd64_size32 = 32
elf_r_amd64_size64 = 33
elf_r_amd64_gotpcrelx = 0x29
)
const (
@ -288,6 +289,8 @@ mut:
info int // Extra section information.
addralign i64 // Section Alignment (must be power of two).
entsize i64 // Section entry size.
pos i64 // not part of the actual data
}
struct SymbolTableSection {
@ -301,13 +304,14 @@ mut:
size i64 // Symbol size.
}
fn (mut g Gen) create_symbol_table_section(str_name string, info u8, bind u8, other i8, value i64, size i64) SymbolTableSection {
fn (mut g Gen) create_symbol_table_section(str_name string, info u8, bind u8, other i8, value i64, size i64, shndx i16) SymbolTableSection {
return SymbolTableSection{
str_name: str_name
info: i8(info | bind << 4)
other: other
value: value
size: size
shndx: shndx
}
}
@ -330,14 +334,16 @@ fn (mut g Gen) create_progbits_section(bytes []u8) ProgBitsSection {
}
struct RelASection {
name string // not in the actual data
mut:
offset i64 // Location at which to apply the relocation action.
info i64 // Symbol table index and type of relocation to apply.
addend i64 // Constant addent for computing the value of the relocation field.
}
fn (mut g Gen) create_rela_section(offset i64, sym u32, typ u32, addend i64) RelASection {
fn (mut g Gen) create_rela_section(name string, offset i64, sym int, typ u32, addend i64) RelASection {
return RelASection{
name: name
offset: offset
info: i64((u64(sym) << 32) + typ)
addend: addend
@ -426,11 +432,11 @@ type SectionData = DynSymSection
| NoBitsSection
| NoteSection
| ProgBitsSection
| RelASection
| RelSection
| ShLibSection
| StringTableSection
| []DynamicSection
| []RelASection
| []SymbolTableSection
struct Section {
@ -476,15 +482,15 @@ fn (mut g Gen) create_shstrtab(mut sections []Section) {
fn (mut g Gen) create_symtab(mut sections []Section, mut table []SymbolTableSection) {
mut names := []string{len: table.len}
mut offset := 1
text_section := g.find_section_header('.text', sections)
mut local_symbols := 0
for i, mut entry in table {
names[i] = entry.str_name
entry.name = offset
if entry.name != 1 {
entry.shndx = i16(text_section) // i16(sections.len + 1)
if (entry.info >> 4) == native.elf_stb_local {
local_symbols++
}
offset += entry.str_name.len + 1
@ -493,10 +499,18 @@ fn (mut g Gen) create_symtab(mut sections []Section, mut table []SymbolTableSect
sections << g.create_section('.strtab', native.elf_sht_strtab, 0, 0, 1, 0, g.create_string_table_section(names))
sections << // index of .strtab
g.create_section('.symtab', native.elf_sht_symtab, sections.len - 1, table.len - 1,
g.create_section('.symtab', native.elf_sht_symtab, sections.len - 1, local_symbols,
native.elf_sh_symtab_align, native.elf_sh_symtab_entsize, table)
}
fn (mut g Gen) create_relocation(name string, mut sections []Section, table []RelASection) Section {
mut section := g.create_section(name, native.elf_sht_rela, g.find_section_header('.symtab',
sections), 1, 8, 24, table)
section.header.flags = i64(native.elf_shf_info_link)
sections << section
return section
}
fn (mut g Gen) create_progbits(name string, flags u64, data []u8) Section {
mut section := g.create_section(name, native.elf_sht_progbits, 0, 0, 1, data.len,
ProgBitsSection{data})
@ -514,7 +528,7 @@ fn (mut g Gen) find_section_header(name string, sections []Section) int {
}
fn (mut g Gen) gen_section_header(mut sh SectionHeader) {
sh_offset := g.pos()
sh.pos = g.pos()
g.write32(sh.name) // sh_name
g.println('; sh_name')
@ -525,8 +539,8 @@ fn (mut g Gen) gen_section_header(mut sh SectionHeader) {
g.write64(sh.addr) // sh_addr
g.println('; sh_name')
if sh.offset == 0 {
g.write64(sh_offset)
sh.offset = sh_offset
g.write64(sh.pos)
sh.offset = sh.pos
} else {
g.write64(sh.offset)
} // sh_offset
@ -566,7 +580,7 @@ fn (mut g Gen) gen_symtab_data(section Section, data []SymbolTableSection) {
}
size := native.elf_symtab_size * data.len
g.write64_at(section.header.offset + 32, i64(size))
g.write64_at(section.header.pos + 32, i64(size))
}
fn (mut g Gen) gen_section_data(sections []Section) {
@ -574,7 +588,7 @@ fn (mut g Gen) gen_section_data(sections []Section) {
data := section.data
// write the actual offset of the section data
g.write64_at(section.header.offset + 24, i64(g.pos()))
g.write64_at(section.header.pos + 24, i64(g.pos()))
match data {
StringTableSection {
@ -589,7 +603,7 @@ fn (mut g Gen) gen_section_data(sections []Section) {
g.write8(0) // null-postfixed
size := g.pos() - start
g.write64_at(section.header.offset + 32, i64(size))
g.write64_at(section.header.pos + 32, i64(size))
}
[]SymbolTableSection {
g.gen_symtab_data(section, data)
@ -597,14 +611,21 @@ fn (mut g Gen) gen_section_data(sections []Section) {
ProgBitsSection {
// progbits have to be handled by the user.
}
RelASection {
g.write64(data.offset)
g.write64(data.info)
g.write64(data.addend)
g.println('; SHT_RELA ($data.offset, $data.info, $data.addend)')
[]RelASection {
if data.len == 0 {
continue
}
size := native.elf_rela_size
g.write64_at(section.header.offset + 32, i64(size))
for rela in data {
g.write64(rela.offset)
g.fn_addr[rela.name] = rela.offset
g.write64(rela.info)
g.write64(rela.addend)
g.println('; SHT_RELA `$rela.name` ($rela.offset, $rela.info, $rela.addend)')
}
size := native.elf_rela_size * data.len
g.write64_at(section.header.pos + 32, i64(size))
}
HashSection {
// TODO
@ -618,7 +639,7 @@ fn (mut g Gen) gen_section_data(sections []Section) {
}
size := native.elf_dynamic_size * data.len
g.write64_at(section.header.offset + 32, i64(size))
g.write64_at(section.header.pos + 32, i64(size))
}
NoteSection {
start := g.pos()
@ -636,7 +657,7 @@ fn (mut g Gen) gen_section_data(sections []Section) {
g.write8(0)
size := g.pos() - start
g.write64_at(section.header.offset + 32, i64(size))
g.write64_at(section.header.pos + 32, i64(size))
}
NoBitsSection {
// Nothing to do
@ -647,13 +668,13 @@ fn (mut g Gen) gen_section_data(sections []Section) {
g.println('; SHT_REL ($data.offset, $data.info)')
size := native.elf_rel_size
g.write64_at(section.header.offset + 32, i64(size))
g.write64_at(section.header.pos + 32, i64(size))
}
ShLibSection {
g.write(data.data)
size := data.data.len
g.write64_at(section.header.offset + 32, i64(size))
g.write64_at(section.header.pos + 32, i64(size))
}
DynSymSection {
g.gen_symtab_data(section, data.symbols)
@ -662,6 +683,15 @@ fn (mut g Gen) gen_section_data(sections []Section) {
}
}
pub fn (mut g Gen) symtab_get_index(symbols []SymbolTableSection, name string) int {
for i, sym in symbols {
if sym.str_name == name {
return i
}
}
return 0
}
pub fn (mut g Gen) generate_linkable_elf_header() {
elf_type := native.elf_type_rel // PIE (use _exec for non-relocatable executables)
@ -676,16 +706,20 @@ pub fn (mut g Gen) generate_linkable_elf_header() {
g.create_progbits('.bss', native.elf_shf_write | native.elf_shf_alloc, []),
]
mut symbols := [
g.symbol_table = [
SymbolTableSection{}, // first is null
g.create_symbol_table_section('test.v', native.elf_stt_file, native.elf_stb_local,
native.elf_stv_default, 0, 0), // source file TODO: replace test.v with actual source file name
g.create_symbol_table_section('.text', native.elf_stt_section, native.elf_stb_local,
native.elf_stv_default, 0, 0), // .text section
g.create_symbol_table_section('main', native.elf_stt_func, native.elf_stb_global,
native.elf_stv_default, 0, 0), // main label points to entry point address
g.create_symbol_table_section('main', native.elf_stt_notype, native.elf_stb_global,
native.elf_stv_default, 0, 0, i16(g.find_section_header('.text', sections))), // main label points to entry point address
g.create_symbol_table_section('_GLOBAL_OFFSET_TABLE_', native.elf_stt_notype,
native.elf_stb_global, native.elf_stv_default, 0, 0, 0),
]
g.create_symtab(mut sections, mut symbols) // create the .symtab section
for symbol in g.extern_symbols {
g.symbol_table << g.create_symbol_table_section(symbol[2..], native.elf_stt_notype,
native.elf_stb_global, native.elf_stv_default, 0, 0, 0)
}
g.create_symtab(mut sections, mut g.symbol_table) // create the .symtab section
g.create_relocation('.rela.text', mut sections, [])
g.create_shstrtab(mut sections) // create the .shstrtab section (this must be the last section!)
mut elf_header := g.default_elf_header()
@ -709,6 +743,8 @@ pub fn (mut g Gen) generate_linkable_elf_header() {
// write sections
g.gen_section_data(sections)
g.elf_rela_section = sections[g.find_section_header('.rela.text', sections)]
// user code starts here
if g.pref.is_verbose {
eprintln('code_start_pos = $g.buf.len.hex()')
@ -717,7 +753,7 @@ pub fn (mut g Gen) generate_linkable_elf_header() {
g.code_start_pos = g.pos()
g.debug_pos = int(g.pos())
// if g.start_symbol_addr > 0 {
// g.write64_at(g.start_symbol_addr + native.elf_symtab_size - 16, g.code_start_pos + native.segment_start)
// g.write64_at(g.start_symbol_addr + native.elf_symtab_size - 16, g.code_start_pos)
//}
text_section := sections[g.find_section_header('.text', sections)]
@ -733,7 +769,6 @@ pub fn (mut g Gen) generate_linkable_elf_header() {
g.ret()
g.println('; return 0')
g.write64_at(g.start_symbol_addr + native.elf_symtab_size - 8, g.pos() - g.code_start_pos) // write 'main' function size
g.debug_pos = g.buf.len
}
@ -787,7 +822,7 @@ pub fn (mut g Gen) generate_simple_elf_header() {
}
}
fn (mut g Gen) elf_string_table() {
pub fn (mut g Gen) elf_string_table() {
for _, s in g.strs {
match s.typ {
.abs64 {
@ -805,9 +840,29 @@ fn (mut g Gen) elf_string_table() {
}
}
pub fn (mut g Gen) gen_rela_section() {
mut relocations := []RelASection{}
for call_pos, symbol in g.extern_fn_calls {
relocations << g.create_rela_section(symbol, call_pos - g.code_start_pos + 2,
g.symtab_get_index(g.symbol_table, symbol[2..]), native.elf_r_amd64_gotpcrelx,
-4)
}
g.elf_rela_section.data = relocations
g.gen_section_data([g.elf_rela_section])
}
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
// write size of text section into section header
if g.elf_text_header_addr != -1 {
g.write64_at(g.elf_text_header_addr + 32, g.pos() - g.code_start_pos)
}
if g.extern_symbols.len != 0 {
g.gen_rela_section()
}
file_size := g.buf.len
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)
@ -824,11 +879,6 @@ pub fn (mut g Gen) generate_elf_footer() {
g.write32_at(g.code_start_pos + 1, int(g.main_fn_addr - g.code_start_pos) - 5)
}
// write size of text section into section header
if g.elf_text_header_addr != -1 {
g.write64_at(g.elf_text_header_addr + 32, g.pos() - g.code_start_pos)
}
g.create_executable()
}

View File

@ -36,10 +36,14 @@ mut:
offset i64
file_size_pos i64
elf_text_header_addr i64 = -1
elf_rela_section Section
main_fn_addr i64
main_fn_size i64
start_symbol_addr i64
code_start_pos i64 // location of the start of the assembly instructions
symbol_table []SymbolTableSection
extern_symbols []string
extern_fn_calls map[i64]string
fn_addr map[string]i64
var_offset map[string]int // local var stack offset
var_alloc_size map[string]int // local var allocation size
@ -259,27 +263,25 @@ pub fn (mut g Gen) typ(a int) &ast.TypeSymbol {
}
pub fn (mut g Gen) ast_has_external_functions() bool {
mut has_external_fn := false
for file in g.files {
walker.inspect(file, unsafe { &mut has_external_fn }, fn (node &ast.Node, data voidptr) bool {
walker.inspect(file, unsafe { &mut g }, fn (node &ast.Node, data voidptr) bool {
if node is ast.Expr && (node as ast.Expr) is ast.CallExpr
&& ((node as ast.Expr) as ast.CallExpr).language != .v {
call := node as ast.CallExpr
unsafe {
*&bool(data) = true // an external function was found
mut g := &Gen(data)
if call.name !in g.extern_symbols {
g.extern_symbols << call.name
}
}
return false
return true
}
return true
})
if has_external_fn {
break
}
}
return has_external_fn
return g.extern_symbols.len != 0
}
pub fn (mut g Gen) generate_header() {
@ -878,19 +880,15 @@ g.expr
}
}
fn (mut g Gen) extern_fn_decl(node ast.FnDecl) {
// declarations like: fn C.malloc()
// TODO: implement extern function calls here
// Must store an address where the function label is located in the RelA elf section
panic('C. functions are not implemented yet')
}
fn (mut g Gen) fn_decl(node ast.FnDecl) {
name := if node.is_method {
'${g.table.get_type_name(node.receiver.typ)}.$node.name'
} else {
node.name
}
if node.no_body {
return
}
if g.pref.is_verbose {
println(term.green('\n$name:'))
}
@ -900,10 +898,6 @@ fn (mut g Gen) fn_decl(node ast.FnDecl) {
if node.is_builtin {
g.warning('fn_decl: $name is builtin', node.pos)
}
if node.no_body {
g.extern_fn_decl(node)
return
}
g.stack_var_pos = 0
g.register_function_address(name)

View File

@ -0,0 +1,38 @@
// because of an issue with checker, all C.* functions have to be declared first
fn C.isalpha(c int) int
fn C.isdigit(c int) int
fn main() {
charutil()
}
fn charutil() {
$if linux {
// ascii for `V`
v_is_alpha := C.isalpha(86)
if v_is_alpha != 0 {
println('ok 1')
} else {
assert false
}
null_is_alpha := C.isalpha(0)
if null_is_alpha == 0 {
println('ok 2')
} else {
assert false
}
// ascii for `3`
three_is_digit := C.isdigit(51)
if three_is_digit != 0 {
println('ok 3')
} else {
assert false
}
} $else {
println('ok 1')
println('ok 2')
println('ok 3')
}
}

View File

@ -0,0 +1,3 @@
ok 1
ok 2
ok 3