Skip to content

Commit c49b9da

Browse files
authored
native: add support for (elf) globals (#25016)
1 parent 5ec3cc1 commit c49b9da

File tree

8 files changed

+159
-28
lines changed

8 files changed

+159
-28
lines changed

vlib/v/gen/native/amd64.v

Lines changed: 41 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -334,7 +334,7 @@ fn (mut c Amd64) cmp_var_reg(var Var, reg Register, config VarConfig) {
334334
c.g.println('cmp var `${var.name}`, ${reg}')
335335
}
336336
GlobalVar {
337-
// TODO
337+
c.g.n_error('${@LOCATION} unsupported var type ${var}')
338338
}
339339
ExternVar {
340340
c.g.n_error('${@LOCATION} unsupported var type ${var}')
@@ -383,7 +383,7 @@ fn (mut c Amd64) cmp_var(var Var, val i32, config VarConfig) {
383383
c.g.println('cmp var `${var.name}` ${val}')
384384
}
385385
GlobalVar {
386-
// TODO
386+
c.g.n_error('${@LOCATION} unsupported var type ${var}')
387387
}
388388
ExternVar {
389389
c.g.n_error('${@LOCATION} unsupported var type ${var}')
@@ -433,7 +433,7 @@ fn (mut c Amd64) dec_var(var Var, config VarConfig) {
433433
c.g.println('dec_var `${var.name}`')
434434
}
435435
GlobalVar {
436-
// TODO
436+
c.g.n_error('${@LOCATION} unsupported var type ${var}')
437437
}
438438
ExternVar {
439439
c.g.n_error('${@LOCATION} unsupported var type ${var}')
@@ -507,8 +507,7 @@ fn (mut c Amd64) inc_var(var Var, config VarConfig) {
507507
c.g.println('inc_var ${size_str} `${var.name}`')
508508
}
509509
GlobalVar {
510-
c.g.n_error('${@LOCATION} Global variables incrementation is not supported yet')
511-
// TODO
510+
c.g.n_error('${@LOCATION} unsupported var type ${var}')
512511
}
513512
ExternVar {
514513
c.g.n_error('${@LOCATION} unsupported var type ${var}')
@@ -794,8 +793,24 @@ fn (mut c Amd64) mov_reg_to_var(var Var, r Register, config VarConfig) {
794793
c.g.println('mov ${size_str} PTR [rbp-${int(offset).hex2()}],${reg} ; `${var.name}`')
795794
}
796795
GlobalVar {
797-
// TODO
798-
c.g.n_error('${@LOCATION} Unsupported GlobalVar')
796+
size := match c.g.get_type_size(var.typ) {
797+
1 { Size._8 }
798+
2 { Size._16 }
799+
4 { Size._32 }
800+
8 { Size._64 }
801+
else { c.g.n_error('${@LOCATION} unsupported size of global var') }
802+
}
803+
mut addr_reg := Amd64Register.rdx
804+
if reg == .rdx || reg == .edx {
805+
addr_reg = .rax
806+
}
807+
c.push(addr_reg)
808+
c.g.global_vars[c.g.pos() + 2] = var.name // +2 for the mov64 instruction
809+
c.mov64(addr_reg, i64(0))
810+
c.g.println('; will get patched by relocs')
811+
c.mov_store(addr_reg, reg, size)
812+
c.pop(addr_reg)
813+
c.g.println('; mov global:`${var.name}` ${reg}')
799814
}
800815
ExternVar {
801816
c.g.n_error('${@LOCATION} unsupported var type ${var}')
@@ -886,7 +901,7 @@ fn (mut c Amd64) mov_int_to_var(var Var, integer i32, config VarConfig) {
886901
}
887902
}
888903
GlobalVar {
889-
// TODO
904+
c.g.n_error('${@LOCATION} unsupported var type ${var}')
890905
}
891906
ExternVar {
892907
c.g.n_error('${@LOCATION} unsupported var type ${var}')
@@ -1009,7 +1024,20 @@ fn (mut c Amd64) mov_var_to_reg(reg Register, var Var, config VarConfig) {
10091024
c.g.println('${instruction} ${reg}, ${size_str} PTR [rbp-${int(offset).hex2()}] ; `${var.name}`')
10101025
}
10111026
GlobalVar {
1012-
c.g.n_error('${@LOCATION} Unsupported GlobalVar')
1027+
if c.g.get_type_size(var.typ) > 8 {
1028+
c.g.n_error('${@LOCATION} unsupported size of global var')
1029+
}
1030+
mut addr_reg := Amd64Register.rdx
1031+
if reg as Amd64Register == .rdx || reg as Amd64Register == .edx {
1032+
addr_reg = .rax
1033+
}
1034+
c.push(addr_reg)
1035+
c.g.global_vars[c.g.pos() + 2] = var.name // +2 for the mov64 instruction
1036+
c.mov64(addr_reg, i64(0))
1037+
c.g.println('; will get patched by relocs')
1038+
c.mov_deref(reg, addr_reg, var.typ)
1039+
c.pop(addr_reg)
1040+
c.g.println('; mov ${reg} global:`${var.name}`')
10131041
}
10141042
ExternVar {
10151043
c.g.n_error('${@LOCATION} unsupported var type ${var}')
@@ -3859,7 +3887,7 @@ fn (mut c Amd64) init_struct(var Var, init ast.StructInit) {
38593887
}
38603888
}
38613889
GlobalVar {
3862-
c.g.n_error('${@LOCATION} GlobalVar not implemented for ast.StructInit')
3890+
c.g.n_error('${@LOCATION} unsupported var type ${var}')
38633891
}
38643892
ExternVar {
38653893
c.g.n_error('${@LOCATION} unsupported var type ${var}')
@@ -3932,7 +3960,7 @@ fn (mut c Amd64) init_array(var Var, node ast.ArrayInit) {
39323960
}
39333961
}
39343962
GlobalVar {
3935-
c.g.n_error('${@LOCATION} GlobalVar not implemented for ast.ArrayInit')
3963+
c.g.n_error('${@LOCATION} unsupported var type ${var}')
39363964
}
39373965
ExternVar {
39383966
c.g.n_error('${@LOCATION} unsupported var type ${var}')
@@ -4265,7 +4293,7 @@ fn (mut c Amd64) mov_ssereg_to_var(var Var, reg Amd64SSERegister, config VarConf
42654293
c.g.println('${inst} [rbp-${int(offset).hex2()}], ${reg}')
42664294
}
42674295
GlobalVar {
4268-
// TODO
4296+
c.g.n_error('${@LOCATION} unsupported var type ${var}')
42694297
}
42704298
ExternVar {
42714299
c.g.n_error('${@LOCATION} unsupported var type ${var}')
@@ -4321,7 +4349,7 @@ fn (mut c Amd64) mov_var_to_ssereg(reg Amd64SSERegister, var Var, config VarConf
43214349
c.g.println('${inst} ${reg}, [rbp-${int(offset).hex2()}]')
43224350
}
43234351
GlobalVar {
4324-
// TODO
4352+
c.g.n_error('${@LOCATION} unsupported var type ${var}')
43254353
}
43264354
ExternVar {
43274355
c.g.n_error('${@LOCATION} unsupported var type ${var}')

vlib/v/gen/native/blacklist.v

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,13 @@ const blacklist = {
5555
'string.last_index': true
5656
'string.last_index_u8': false
5757
'string.contains_u8': false
58+
'malloc_noscan': true
59+
'vmemcpy': false
60+
'eprint': false
61+
'eprintln': false
62+
'_write_buf_to_fd': false
63+
'_writeln_to_fd': false
64+
'_memory_panic': true
5865
}
5966

6067
const windows_blacklist = {

vlib/v/gen/native/elf.v

Lines changed: 77 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
module native
55

66
import os
7+
import ast
78

89
const elf_class32 = 1
910
const elf_class64 = 2
@@ -300,6 +301,8 @@ mut:
300301
size i64 // Symbol size.
301302
}
302303

304+
// see for more details: https://gist.github.com/x0nu11byt3/bcb35c3de461e5fb66173071a2379779
305+
// https://www.etherington.xyz/elfguide#symtab-section-entries
303306
fn (mut g Gen) create_symbol_table_section(str_name string, info u8, bind u8, other i8, value i64, size i64,
304307
shndx i16) SymbolTableSection {
305308
return SymbolTableSection{
@@ -615,7 +618,6 @@ fn (mut g Gen) gen_section_data(sections []Section) {
615618
for rela in data {
616619
g.write64(rela.offset)
617620
g.fn_addr[rela.name] = rela.offset // that's wierd it's the call offset, not the fn
618-
g.fn_names << rela.name
619621
g.write64(rela.info)
620622
g.write64(rela.addend)
621623
g.println('; SHT_RELA `${rela.name}` (${rela.offset}, ${rela.info}, ${rela.addend})')
@@ -711,6 +713,23 @@ pub fn (mut g Gen) generate_linkable_elf_header() {
711713
elf_stv_default, 0, 0, 0),
712714
]
713715

716+
mut sym_data_offset := 0 // offset from the beggining of the data section
717+
for f in g.files {
718+
for s in f.stmts {
719+
if s is ast.GlobalDecl {
720+
for fi in s.fields {
721+
size := g.get_type_size(fi.typ)
722+
if size > 0 {
723+
g.symbol_table << g.create_symbol_table_section(fi.name, elf_stt_object,
724+
elf_stb_global, elf_stv_default, sym_data_offset, size, i16(g.find_section_header('.data',
725+
sections)))
726+
}
727+
sym_data_offset += size
728+
}
729+
}
730+
}
731+
}
732+
714733
for symbol in g.extern_symbols {
715734
g.symbol_table << g.create_symbol_table_section(symbol[2..], elf_stt_notype, elf_stb_global,
716735
elf_stv_default, 0, 0, 0)
@@ -742,6 +761,37 @@ pub fn (mut g Gen) generate_linkable_elf_header() {
742761

743762
g.elf_rela_section = sections[g.find_section_header('.rela.text', sections)]
744763

764+
// Init data section
765+
// TODO: use .bss for uninitialized data or generate the data
766+
data_section := sections[g.find_section_header('.data', sections)]
767+
g.elf_data_header_addr = data_section.header.offset
768+
769+
data_pos := g.pos()
770+
g.println('\ndata_start_pos = ${data_pos.hex()}')
771+
g.write64_at(g.elf_data_header_addr + 24, data_pos)
772+
for f in g.files {
773+
for s in f.stmts {
774+
if s is ast.GlobalDecl {
775+
for fi in s.fields {
776+
size := g.get_type_size(fi.typ)
777+
match size {
778+
1 { g.write8(0xF) }
779+
2 { g.write16(0xF) }
780+
4 { g.write32(0xF) }
781+
8 { g.write64(i64(0xF)) }
782+
else { println('${@LOCATION} unsupported size ${size} for global ${fi}') }
783+
}
784+
g.println('; global ${fi.name}, size: ${size}')
785+
}
786+
}
787+
}
788+
}
789+
g.write64_at(g.elf_data_header_addr + 32, g.pos() - data_pos)
790+
// TODO: pre-calculate .data instead of genetating it at runtime
791+
792+
// It is wierd, the init section has something in it but I did not find
793+
// where does it come from
794+
745795
// user code starts here
746796
if g.pref.is_verbose {
747797
eprintln('code_start_pos = ${g.buf.len.hex()}')
@@ -752,11 +802,29 @@ pub fn (mut g Gen) generate_linkable_elf_header() {
752802
// if g.start_symbol_addr > 0 {
753803
// g.write64_at(g.start_symbol_addr + native.elf_symtab_size - 16, g.code_start_pos)
754804
//}
755-
756805
text_section := sections[g.find_section_header('.text', sections)]
757806
g.elf_text_header_addr = text_section.header.offset
758807
g.write64_at(g.elf_text_header_addr + 24, g.pos()) // write the code start pos to the text section
759808

809+
g.println('; fill .data')
810+
for f in g.files {
811+
for s in f.stmts {
812+
if s is ast.GlobalDecl {
813+
for fi in s.fields {
814+
size := g.get_type_size(fi.typ)
815+
if size > 8 || size <= 0 {
816+
println('${@LOCATION} unsupported size ${size} for global ${fi}')
817+
} else if fi.expr !is ast.EmptyExpr {
818+
g.expr(fi.expr)
819+
g.code_gen.mov_reg_to_var(GlobalVar{fi.name, fi.typ}, g.code_gen.main_reg())
820+
g.println('; global ${fi.name}, size: ${size}')
821+
}
822+
}
823+
}
824+
}
825+
}
826+
827+
g.elf_main_call_pos = g.pos()
760828
g.code_gen.call(placeholder)
761829
g.println('; call main.main')
762830
g.code_gen.mov64(g.code_gen.main_reg(), i64(0))
@@ -848,9 +916,13 @@ pub fn (mut g Gen) gen_rela_section() {
848916
g.symtab_get_index(g.symbol_table, symbol[2..]), elf_r_amd64_gotpcrelx, -4)
849917
}
850918
for var_pos, symbol in g.extern_vars {
851-
relocations << g.create_rela_section(symbol, var_pos - g.code_start_pos + 2, g.symtab_get_index(g.symbol_table,
919+
relocations << g.create_rela_section(symbol, var_pos - g.code_start_pos, g.symtab_get_index(g.symbol_table,
852920
symbol[2..]), elf_r_amd64_64, 0)
853921
}
922+
for var_pos, symbol in g.global_vars {
923+
relocations << g.create_rela_section(symbol, var_pos - g.code_start_pos, g.symtab_get_index(g.symbol_table,
924+
symbol), elf_r_amd64_64, 0)
925+
}
854926
g.elf_rela_section.data = relocations
855927
g.gen_section_data([g.elf_rela_section])
856928
}
@@ -872,15 +944,15 @@ pub fn (mut g Gen) generate_elf_footer() {
872944
g.write64_at(g.file_size_pos + 8, file_size)
873945
if g.pref.arch == .arm64 {
874946
bl_next := u32(0x94000001)
875-
g.write32_at(g.code_start_pos, i32(bl_next))
947+
g.write32_at(g.elf_main_call_pos, i32(bl_next))
876948
} else {
877949
// amd64
878950
// call main function, it's not guaranteed to be the first
879951
// we generated call(0) ("e8 0")
880952
// now need to replace "0" with a relative address of the main function
881953
// +1 is for "e8"
882954
// -5 is for "e8 00 00 00 00"
883-
g.write32_at(g.code_start_pos + 1, i32(g.main_fn_addr - g.code_start_pos) - 5)
955+
g.write32_at(g.elf_main_call_pos + 1, i32(g.main_fn_addr - g.elf_main_call_pos) - 5)
884956
}
885957
g.create_executable()
886958
}

vlib/v/gen/native/expr.v

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ fn (mut g Gen) local_var_ident(ident ast.Ident, var LocalVar) {
196196
fn (mut g Gen) extern_var_ident(var ExternVar) {
197197
if g.pref.os == .linux {
198198
main_reg := g.code_gen.main_reg()
199-
g.extern_vars[g.pos()] = var.name
199+
g.extern_vars[g.pos() + 2] = var.name // + 2 for the mov64 instruction
200200
g.code_gen.mov64(main_reg, Number(i64(0)))
201201
g.code_gen.mov_deref(main_reg, main_reg, ast.u64_type_idx)
202202
} else if g.pref.os == .macos {

vlib/v/gen/native/gen.v

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,9 @@ mut:
3939
extern_symbols []string
4040
linker_include_paths []string
4141
linker_libs []string
42-
extern_vars map[i64]string
43-
extern_fn_calls map[i64]string
42+
global_vars map[i64]string // used to patch
43+
extern_vars map[i64]string // used to patch
44+
extern_fn_calls map[i64]string // used to patch
4445
fn_addr map[string]i64
4546
fn_names []string
4647
var_offset map[string]i32 // local var stack offset
@@ -65,7 +66,9 @@ mut:
6566
return_type ast.Type
6667
comptime_omitted_branches []ast.IfBranch
6768
// elf specific
69+
elf_main_call_pos i64
6870
elf_text_header_addr i64 = -1
71+
elf_data_header_addr i64 = -1
6972
elf_rela_section Section
7073
// macho specific
7174
macho_ncmds i32
@@ -253,7 +256,10 @@ struct PreprocVar {
253256
val i64
254257
}
255258

256-
struct GlobalVar {}
259+
struct GlobalVar {
260+
name string
261+
typ ast.Type
262+
}
257263

258264
@[params]
259265
struct VarConfig {
@@ -300,7 +306,9 @@ fn (mut g Gen) get_var_from_ident(ident ast.Ident) IdentVar {
300306
return PreprocVar{ident.info.typ, ident.name, preprocessed_val}
301307
}
302308
mut obj := ident.obj
303-
if obj !in [ast.Var, ast.ConstField, ast.GlobalField, ast.AsmRegister] {
309+
if obj is ast.GlobalField {
310+
return GlobalVar{obj.name, obj.typ}
311+
} else if obj !in [ast.Var, ast.ConstField, ast.AsmRegister] {
304312
obj = ident.scope.find(ident.name) or {
305313
g.n_error('${@LOCATION} unknown variable ${ident.name}')
306314
}
@@ -389,6 +397,7 @@ pub fn gen(files []&ast.File, mut table ast.Table, out_name string, pref_ &pref.
389397
g.init_builtins()
390398
g.calculate_all_size_align()
391399
g.calculate_enum_fields()
400+
g.fn_names = g.table.fns.keys()
392401
for file in g.files {
393402
/*
394403
if file.warnings.len > 0 {
@@ -1175,7 +1184,6 @@ fn (mut g Gen) fn_decl(node ast.FnDecl) {
11751184
g.stack_var_pos = 0
11761185
g.stack_depth = 0
11771186
g.register_function_address(name)
1178-
g.fn_names << name
11791187
g.labels = &LabelTable{}
11801188
g.defer_stmts.clear()
11811189
g.return_type = node.return_type
@@ -1225,6 +1233,7 @@ fn (mut g Gen) println(comment string) {
12251233
@[noreturn]
12261234
pub fn (mut g Gen) n_error(s string) {
12271235
print_backtrace()
1236+
flush_stdout()
12281237
util.verror('native error', s)
12291238
}
12301239

vlib/v/gen/native/stmt.c.v

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,8 +106,10 @@ fn (mut g Gen) stmt(node ast.Stmt) {
106106
g.gen_assert(node)
107107
}
108108
ast.GlobalDecl {
109-
if !g.is_builtin_mod && !g.pref.experimental {
110-
g.warning('globals are not supported yet', node.pos)
109+
if g.pref.os == .linux {
110+
// handled in elf generator
111+
} else if !g.is_builtin_mod && !g.pref.experimental {
112+
g.warning('global variables are not supported', node.pos)
111113
}
112114
}
113115
ast.Import {} // do nothing here

0 commit comments

Comments
 (0)