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:
@ -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')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
|
Reference in New Issue
Block a user