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

native: skip linking when no library functions get called (#15930)

This commit is contained in:
Spydr
2022-10-01 05:47:16 +02:00
committed by GitHub
parent d78dfabfe0
commit 43d0d0f322
2 changed files with 168 additions and 33 deletions

View File

@ -44,6 +44,9 @@ const (
elf_pt_phdr = 6 elf_pt_phdr = 6
elf_pt_tls = 7 elf_pt_tls = 7
// offset of e_entry field in the elf header
elf_e_entry_offset = 24
// alignment of program headers // alignment of program headers
elf_p_align = 0x1000 elf_p_align = 0x1000
@ -659,13 +662,12 @@ fn (mut g Gen) gen_section_data(sections []Section) {
} }
} }
pub fn (mut g Gen) generate_elf_header() { pub fn (mut g Gen) generate_linkable_elf_header() {
elf_type := native.elf_type_rel // PIE (use _exec for non-relocatable executables) elf_type := native.elf_type_rel // PIE (use _exec for non-relocatable executables)
// generate program headers // generate program headers
mut program_headers := []ProgramHeader{} mut program_headers := []ProgramHeader{}
program_headers << g.create_program_header(native.elf_pt_load, 5, native.elf_p_align) program_headers << g.create_program_header(native.elf_pt_load, 5, native.elf_p_align)
// generate sections // generate sections
mut sections := [ mut sections := [
Section{}, // null section as first section Section{}, // null section as first section
@ -735,6 +737,56 @@ pub fn (mut g Gen) generate_elf_header() {
g.debug_pos = g.buf.len g.debug_pos = g.buf.len
} }
pub fn (mut g Gen) generate_simple_elf_header() {
elf_type := native.elf_type_exec
mut phdr := g.create_program_header(native.elf_pt_load, 5, native.elf_p_align)
phdr.vaddr = native.segment_start
phdr.paddr = native.segment_start
mut elf_header := g.default_elf_header()
elf_header.typ = i16(elf_type)
elf_header.phnum = i16(1)
elf_header.shoff = i64(0)
elf_header.shentsize = i16(0)
elf_header.shnum = i16(0)
elf_header.shstrndx = i16(0)
elf_header.entry = native.segment_start + native.elf_header_size + native.elf_phentry_size
g.gen_elf_header(elf_header)
// write program header
g.gen_program_header(phdr)
// user code starts here
if g.pref.is_verbose {
eprintln('code_start_pos = $g.buf.len.hex()')
}
g.code_start_pos = g.pos()
g.debug_pos = int(g.pos())
g.call(native.placeholder)
g.println('; call main.main')
// generate exit syscall
match g.pref.arch {
.arm64 {
g.mov_arm(.x16, 0)
g.mov_arm(.x0, 0)
g.svc()
}
.amd64 {
g.mov(.edi, 0)
g.mov(.eax, g.nsyscall_exit())
g.syscall()
}
else {
g.n_error('unsupported platform $g.pref.arch')
}
}
}
fn (mut g Gen) elf_string_table() { fn (mut g Gen) elf_string_table() {
for _, s in g.strs { for _, s in g.strs {
match s.typ { match s.typ {
@ -773,7 +825,9 @@ pub fn (mut g Gen) generate_elf_footer() {
} }
// write size of text section into section header // 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.write64_at(g.elf_text_header_addr + 32, g.pos() - g.code_start_pos)
}
g.create_executable() g.create_executable()
} }
@ -789,7 +843,19 @@ pub fn (mut g Gen) prepend_vobjpath(paths []string) []string {
} }
pub fn (mut g Gen) find_o_path(fname string) string { pub fn (mut g Gen) find_o_path(fname string) string {
opaths := g.prepend_vobjpath(['/usr/lib/x86_64-linux-gnu', '/usr/lib64', '/usr/lib']) opaths := match g.pref.arch {
.amd64 {
g.prepend_vobjpath(['/usr/lib/x86_64-linux-gnu', '/usr/lib64', '/usr/lib'])
}
.arm64 {
g.prepend_vobjpath(['/usr/lib/aarch64-linux-gnu', '/usr/lib'])
}
else {
g.n_error('unknown architecture $g.pref.arch')
['/dev/null']
}
}
for opath in opaths { for opath in opaths {
fpath := os.join_path_single(opath, fname) fpath := os.join_path_single(opath, fname)
if os.is_file(fpath) { if os.is_file(fpath) {
@ -800,8 +866,19 @@ pub fn (mut g Gen) find_o_path(fname string) string {
} }
pub fn (mut g Gen) get_lpaths() string { pub fn (mut g Gen) get_lpaths() string {
lpaths := g.prepend_vobjpath(['/usr/lib/x86_64-linux-gnu', '/usr/lib64', '/lib64', '/usr/lib', lpaths := match g.pref.arch {
.amd64 {
g.prepend_vobjpath(['/usr/lib/x86_64-linux-gnu', '/usr/lib64', '/lib64', '/usr/lib',
'/lib']) '/lib'])
}
.arm64 {
g.prepend_vobjpath(['/usr/lib/aarch64-linux-gnu', '/usr/lib', '/lib'])
}
else {
g.n_error('unknown architecture $g.pref.arch')
['/dev/null']
}
}
return lpaths.map('-L$it').join(' ') return lpaths.map('-L$it').join(' ')
} }
@ -810,12 +887,32 @@ pub fn (mut g Gen) link_elf_file(obj_file string) {
crti := g.find_o_path('crti.o') crti := g.find_o_path('crti.o')
crtn := g.find_o_path('crtn.o') crtn := g.find_o_path('crtn.o')
lpaths := g.get_lpaths() lpaths := g.get_lpaths()
arch := match g.pref.arch {
.amd64 {
'elf_x86_64'
}
.arm64 {
'aarch64elf'
}
else {
g.n_error('unknown architecture $g.pref.arch')
'elf_x86_64' // default to x86_64
}
}
dynamic_linker := match g.pref.arch {
.amd64 { '/lib64/ld-linux-x86-64.so.2' }
.arm64 { '/lib/ld-linux-aarch64.so.1' }
else { '/dev/null' }
}
linker_args := [ linker_args := [
'-v', '-v',
lpaths, lpaths,
'-m elf_x86_64', '-m $arch',
'-dynamic-linker', '-dynamic-linker',
'/lib64/ld-linux-x86-64.so.2', dynamic_linker,
crt1, crt1,
crti, crti,
'-lc', '-lc',
@ -827,20 +924,13 @@ pub fn (mut g Gen) link_elf_file(obj_file string) {
] ]
slinker_args := linker_args.join(' ') slinker_args := linker_args.join(' ')
mut ldlld := 'ld' mut ld := 'ld'
/*
match g.pref.os {
.linux { ldlld = 'ld.lld' }
.windows { ldlld = 'lld-link' }
.macos { ldlld = 'ld64.lld' }
else {}
}
*/
custom_linker := os.getenv('VLINKER') custom_linker := os.getenv('VLINKER')
if custom_linker != '' { if custom_linker != '' {
ldlld = custom_linker ld = custom_linker
} }
linker_path := os.real_path(ldlld) linker_path := os.real_path(ld)
linker_cmd := '${os.quoted_path(linker_path)} $slinker_args' linker_cmd := '${os.quoted_path(linker_path)} $slinker_args'
if g.pref.is_verbose { if g.pref.is_verbose {
println(linker_cmd) println(linker_cmd)
@ -848,11 +938,11 @@ pub fn (mut g Gen) link_elf_file(obj_file string) {
res := os.execute(linker_cmd) res := os.execute(linker_cmd)
if res.exit_code != 0 { if res.exit_code != 0 {
g.n_error('ELF linking failed ($ldlld):\n$res.output') g.n_error('ELF linking failed ($ld):\n$res.output')
return return
} }
if g.pref.is_verbose { if g.pref.is_verbose {
println('linking with $ldlld finished successfully:\n$res.output') println('linking with $ld finished successfully:\n$res.output')
} }
} }

View File

@ -6,6 +6,7 @@ module native
import os import os
import strings import strings
import v.ast import v.ast
import v.ast.walker
import v.util import v.util
import v.mathutil as mu import v.mathutil as mu
import v.token import v.token
@ -26,6 +27,7 @@ mut:
pub struct Gen { pub struct Gen {
out_name string out_name string
pref &pref.Preferences = unsafe { nil } // Preferences shared from V struct pref &pref.Preferences = unsafe { nil } // Preferences shared from V struct
files []&ast.File
mut: mut:
code_gen CodeGen code_gen CodeGen
table &ast.Table = unsafe { nil } table &ast.Table = unsafe { nil }
@ -33,7 +35,7 @@ mut:
sect_header_name_pos int sect_header_name_pos int
offset i64 offset i64
file_size_pos i64 file_size_pos i64
elf_text_header_addr i64 elf_text_header_addr i64 = -1
main_fn_addr i64 main_fn_addr i64
main_fn_size i64 main_fn_size i64
start_symbol_addr i64 start_symbol_addr i64
@ -59,6 +61,8 @@ mut:
// macho specific // macho specific
macho_ncmds int macho_ncmds int
macho_cmdsize int macho_cmdsize int
requires_linking bool
} }
enum RelocType { enum RelocType {
@ -218,6 +222,7 @@ pub fn gen(files []&ast.File, table &ast.Table, out_name string, pref &pref.Pref
sect_header_name_pos: 0 sect_header_name_pos: 0
out_name: exe_name out_name: exe_name
pref: pref pref: pref
files: files
// TODO: workaround, needs to support recursive init // TODO: workaround, needs to support recursive init
code_gen: get_backend(pref.arch) or { code_gen: get_backend(pref.arch) or {
eprintln('No available backend for this configuration. Use `-a arm64` or `-a amd64`.') eprintln('No available backend for this configuration. Use `-a arm64` or `-a amd64`.')
@ -232,7 +237,7 @@ pub fn gen(files []&ast.File, table &ast.Table, out_name string, pref &pref.Pref
g.init_builtins() g.init_builtins()
g.calculate_all_size_align() g.calculate_all_size_align()
g.calculate_enum_fields() g.calculate_enum_fields()
for file in files { for file in g.files {
/* /*
if file.warnings.len > 0 { if file.warnings.len > 0 {
eprintln('warning: ${file.warnings[0]}') eprintln('warning: ${file.warnings[0]}')
@ -253,7 +258,33 @@ pub fn (mut g Gen) typ(a int) &ast.TypeSymbol {
return g.table.type_symbols[a] return g.table.type_symbols[a]
} }
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 {
if node is ast.Expr && (node as ast.Expr) is ast.CallExpr
&& ((node as ast.Expr) as ast.CallExpr).language != .v {
unsafe {
*&bool(data) = true // an external function was found
}
return false
}
return true
})
if has_external_fn {
break
}
}
return has_external_fn
}
pub fn (mut g Gen) generate_header() { pub fn (mut g Gen) generate_header() {
g.requires_linking = g.ast_has_external_functions()
match g.pref.os { match g.pref.os {
.macos { .macos {
g.generate_macho_header() g.generate_macho_header()
@ -262,7 +293,11 @@ pub fn (mut g Gen) generate_header() {
g.generate_pe_header() g.generate_pe_header()
} }
.linux { .linux {
g.generate_elf_header() if g.requires_linking {
g.generate_linkable_elf_header()
} else {
g.generate_simple_elf_header()
}
} }
.raw { .raw {
if g.pref.arch == .arm64 { if g.pref.arch == .arm64 {
@ -270,19 +305,28 @@ pub fn (mut g Gen) generate_header() {
} }
} }
else { else {
g.n_error('only `raw`, `linux` and `macos` are supported for -os in -native') g.n_error('only `raw`, `linux`, `windows` and `macos` are supported for -os in -native')
} }
} }
} }
pub fn (mut g Gen) create_executable() { pub fn (mut g Gen) create_executable() {
obj_name := match g.pref.os { obj_name := match g.pref.os {
.linux { g.out_name + '.o' } .linux {
else { g.out_name } if g.requires_linking {
g.out_name + '.o'
} else {
g.out_name
}
}
else {
g.out_name
}
} }
os.write_file_array(obj_name, g.buf) or { panic(err) } os.write_file_array(obj_name, g.buf) or { panic(err) }
if g.requires_linking {
match g.pref.os { match g.pref.os {
// TEMPORARY // TEMPORARY
.linux { // TEMPORARY .linux { // TEMPORARY
@ -290,6 +334,7 @@ pub fn (mut g Gen) create_executable() {
} // TEMPORARY } // TEMPORARY
else {} // TEMPORARY else {} // TEMPORARY
} // TEMPORARY } // TEMPORARY
}
os.chmod(g.out_name, 0o775) or { panic(err) } // make it executable os.chmod(g.out_name, 0o775) or { panic(err) } // make it executable
if g.pref.is_verbose { if g.pref.is_verbose {