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
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 168 additions and 33 deletions

View File

@ -44,6 +44,9 @@ const (
elf_pt_phdr = 6
elf_pt_tls = 7
// offset of e_entry field in the elf header
elf_e_entry_offset = 24
// alignment of program headers
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)
// generate program headers
mut program_headers := []ProgramHeader{}
program_headers << g.create_program_header(native.elf_pt_load, 5, native.elf_p_align)
// generate sections
mut sections := [
Section{}, // null section as first section
@ -735,6 +737,56 @@ pub fn (mut g Gen) generate_elf_header() {
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() {
for _, s in g.strs {
match s.typ {
@ -773,7 +825,9 @@ pub fn (mut g Gen) generate_elf_footer() {
}
// write size of text section into section header
g.write64_at(g.elf_text_header_addr + 32, g.pos() - g.code_start_pos)
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()
}
@ -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 {
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 {
fpath := os.join_path_single(opath, fname)
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 {
lpaths := g.prepend_vobjpath(['/usr/lib/x86_64-linux-gnu', '/usr/lib64', '/lib64', '/usr/lib',
'/lib'])
lpaths := match g.pref.arch {
.amd64 {
g.prepend_vobjpath(['/usr/lib/x86_64-linux-gnu', '/usr/lib64', '/lib64', '/usr/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(' ')
}
@ -810,12 +887,32 @@ pub fn (mut g Gen) link_elf_file(obj_file string) {
crti := g.find_o_path('crti.o')
crtn := g.find_o_path('crtn.o')
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 := [
'-v',
lpaths,
'-m elf_x86_64',
'-m $arch',
'-dynamic-linker',
'/lib64/ld-linux-x86-64.so.2',
dynamic_linker,
crt1,
crti,
'-lc',
@ -827,20 +924,13 @@ pub fn (mut g Gen) link_elf_file(obj_file string) {
]
slinker_args := linker_args.join(' ')
mut ldlld := 'ld'
/*
match g.pref.os {
.linux { ldlld = 'ld.lld' }
.windows { ldlld = 'lld-link' }
.macos { ldlld = 'ld64.lld' }
else {}
}
*/
mut ld := 'ld'
custom_linker := os.getenv('VLINKER')
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'
if g.pref.is_verbose {
println(linker_cmd)
@ -848,11 +938,11 @@ pub fn (mut g Gen) link_elf_file(obj_file string) {
res := os.execute(linker_cmd)
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
}
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 strings
import v.ast
import v.ast.walker
import v.util
import v.mathutil as mu
import v.token
@ -26,6 +27,7 @@ mut:
pub struct Gen {
out_name string
pref &pref.Preferences = unsafe { nil } // Preferences shared from V struct
files []&ast.File
mut:
code_gen CodeGen
table &ast.Table = unsafe { nil }
@ -33,7 +35,7 @@ mut:
sect_header_name_pos int
offset i64
file_size_pos i64
elf_text_header_addr i64
elf_text_header_addr i64 = -1
main_fn_addr i64
main_fn_size i64
start_symbol_addr i64
@ -59,6 +61,8 @@ mut:
// macho specific
macho_ncmds int
macho_cmdsize int
requires_linking bool
}
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
out_name: exe_name
pref: pref
files: files
// TODO: workaround, needs to support recursive init
code_gen: get_backend(pref.arch) or {
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.calculate_all_size_align()
g.calculate_enum_fields()
for file in files {
for file in g.files {
/*
if file.warnings.len > 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]
}
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() {
g.requires_linking = g.ast_has_external_functions()
match g.pref.os {
.macos {
g.generate_macho_header()
@ -262,7 +293,11 @@ pub fn (mut g Gen) generate_header() {
g.generate_pe_header()
}
.linux {
g.generate_elf_header()
if g.requires_linking {
g.generate_linkable_elf_header()
} else {
g.generate_simple_elf_header()
}
}
.raw {
if g.pref.arch == .arm64 {
@ -270,26 +305,36 @@ pub fn (mut g Gen) generate_header() {
}
}
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() {
obj_name := match g.pref.os {
.linux { g.out_name + '.o' }
else { g.out_name }
.linux {
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) }
match g.pref.os {
// TEMPORARY
.linux { // TEMPORARY
g.link(obj_name)
if g.requires_linking {
match g.pref.os {
// TEMPORARY
.linux { // TEMPORARY
g.link(obj_name)
} // TEMPORARY
else {} // TEMPORARY
} // TEMPORARY
else {} // TEMPORARY
} // TEMPORARY
}
os.chmod(g.out_name, 0o775) or { panic(err) } // make it executable
if g.pref.is_verbose {