builtin: improve musl/Alpine support (define weak backtrace/backtrace_symbols/backtrace_symbols_fd symbols) (#14250)

master
Delyan Angelov 2022-05-03 09:17:53 +03:00 committed by GitHub
parent 4da2908d63
commit 0e5c1cee48
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 164 additions and 62 deletions

View File

@ -0,0 +1,20 @@
module builtin
// These are just dummy implementations to appease linking on musl/alpine
[export: 'backtrace_symbols']
[weak]
fn vbacktrace_symbols(const_buffer &voidptr, size int) &&char {
return 0
}
[export: 'backtrace']
[weak]
fn vbacktrace(buffer &voidptr, size int) int {
return 0
}
[export: 'backtrace_symbols_fd']
[weak]
fn vbacktrace_symbols_fd(const_buffer &voidptr, size int, fd int) {
}

View File

@ -0,0 +1,6 @@
module builtin
// <execinfo.h>
fn C.backtrace(a &voidptr, size int) int
fn C.backtrace_symbols(a &voidptr, size int) &&char
fn C.backtrace_symbols_fd(a &voidptr, size int, fd int)

View File

@ -45,10 +45,13 @@ $if dynamic_boehm ? {
#flag -DBUS_PAGE_FAULT=T_PAGEFLT
#flag -DGC_PTHREADS=1
$if !tinyc {
#flag -I@VEXEROOT/thirdparty/libgc/include
#flag @VEXEROOT/thirdparty/libgc/gc.o
} $else {
}
$if tinyc {
#flag -I/usr/local/include
#flag $first_existing("/usr/local/lib/libgc.a", "/usr/lib/libgc.a")
#flag -lgc
}
#flag -lpthread
} $else $if openbsd {

View File

@ -39,13 +39,6 @@ fn C.isdigit(c int) bool
// stdio.h
fn C.popen(c &char, t &char) voidptr
// <execinfo.h>
fn C.backtrace(a &voidptr, size int) int
fn C.backtrace_symbols(a &voidptr, size int) &&char
fn C.backtrace_symbols_fd(a &voidptr, size int, fd int)
// <libproc.h>
pub fn proc_pidpath(int, voidptr, int) int

View File

@ -564,9 +564,12 @@ fn (mut c Checker) comptime_if_branch(cond ast.Expr, pos token.Pos) ComptimeBran
'test' {
return if c.pref.is_test { .eval } else { .skip }
}
'musl' {
return .unknown
}
'glibc' {
return .unknown
} // TODO
}
'threads' {
return if c.table.gostmts > 0 { .eval } else { .skip }
}

View File

@ -366,6 +366,10 @@ const c_common_macros = '
#undef VWEAK
#define VWEAK
#endif
#if defined(__MINGW32__) || defined(__MINGW64__)
#undef VWEAK
#define VWEAK
#endif
#endif
#if !defined(VNORETURN)
@ -460,11 +464,6 @@ typedef int (*qsort_callback_func)(const void*, const void*);
#if defined __has_include
#if __has_include (<execinfo.h>)
#include <execinfo.h>
#else
// Most probably musl OR __ANDROID__ ...
int backtrace (void **__array, int __size) { return 0; }
char **backtrace_symbols (void *const *__array, int __size){ return 0; }
void backtrace_symbols_fd (void *const *__array, int __size, int __fd){}
#endif
#endif
#endif

View File

@ -416,8 +416,9 @@ fn (mut g Gen) gen_fn_decl(node &ast.FnDecl, skip bool) {
}
for attr in node.attrs {
if attr.name == 'export' {
weak := if node.attrs.any(it.name == 'weak') { 'VWEAK ' } else { '' }
g.writeln('// export alias: $attr.arg -> $name')
export_alias := '$type_name $fn_attrs${attr.arg}($arg_str)'
export_alias := '$weak$type_name $fn_attrs${attr.arg}($arg_str)'
g.definitions.writeln('VV_EXPORTED_SYMBOL $export_alias; // exported fn $node.name')
g.writeln('$export_alias {')
g.write('\treturn ${name}(')
@ -2143,6 +2144,10 @@ fn (mut g Gen) write_fn_attrs(attrs []ast.Attr) string {
g.write('__NOINLINE ')
}
'weak' {
if attrs.any(it.name == 'export') {
// only the exported wrapper should be weak; otherwise x86_64-w64-mingw32-gcc complains
continue
}
// a `[weak]` tag tells the C compiler, that the next declaration will be weak, i.e. when linking,
// if there is another declaration of a symbol with the same name (a 'strong' one), it should be
// used instead, *without linker errors about duplicate symbols*.

View File

@ -0,0 +1,13 @@
VV_LOCAL_SYMBOL int main__my_other_fn(void);
VV_EXPORTED_SYMBOL VWEAK int wxyz(void); // exported fn main.my_other_fn
VWEAK int wxyz(void) {
VV_LOCAL_SYMBOL int main__my_other_fn(void) {
VV_LOCAL_SYMBOL int main__my_fn(void);
VV_EXPORTED_SYMBOL int abcd(void); // exported fn main.my_fn
int abcd(void) {
VV_LOCAL_SYMBOL int main__my_fn(void) {
println(int_str(main__my_fn()));
println(int_str(main__my_other_fn()));

View File

@ -0,0 +1,2 @@
42
11

View File

@ -0,0 +1,20 @@
// This file tests the ability to export functions to C with a fully custom name.
// It also tests, that the exported functions will be exported as weak symbols,
// if the user tagged them as such.
[export: abcd]
fn my_fn() int {
return 42
}
[export: wxyz]
[weak]
fn my_other_fn() int {
return 11
}
fn main() {
println(my_fn())
println(my_other_fn())
}

View File

@ -99,25 +99,35 @@ pub mut:
// verbosity VerboseLevel
is_verbose bool
// nofmt bool // disable vfmt
is_test bool // `v test string_test.v`
is_script bool // single file mode (`v program.v`), main function can be skipped
is_vsh bool // v script (`file.vsh`) file, the `os` module should be made global
is_livemain bool // main program that contains live/hot code
is_liveshared bool // a shared library, that will be used in a -live main program
is_shared bool // an ordinary shared library, -shared, no matter if it is live or not
is_o bool // building an .o file
is_prof bool // benchmark every function
is_glibc bool // if GLIBC will be linked
is_musl bool // if MUSL will be linked
is_test bool // `v test string_test.v`
is_script bool // single file mode (`v program.v`), main function can be skipped
is_vsh bool // v script (`file.vsh`) file, the `os` module should be made global
is_livemain bool // main program that contains live/hot code
is_liveshared bool // a shared library, that will be used in a -live main program
is_shared bool // an ordinary shared library, -shared, no matter if it is live or not
is_o bool // building an .o file
is_prof bool // benchmark every function
is_prod bool // use "-O2"
is_repl bool
is_run bool
is_debug bool // turned on by -g or -cg, it tells v to pass -g to the C backend compiler.
is_vlines bool // turned on by -g (it slows down .tmp.c generation slightly).
is_stats bool // `v -stats file_test.v` will produce more detailed statistics for the tests that were run
is_fmt bool
is_vet bool
is_vweb bool // skip _ var warning in templates
is_ios_simulator bool
is_apk bool // build as Android .apk format
is_help bool // -h, -help or --help was passed
is_cstrict bool // turn on more C warnings; slightly slower
test_runner string // can be 'simple' (fastest, but much less detailed), 'tap', 'normal'
profile_file string // the profile results will be stored inside profile_file
profile_no_inline bool // when true, [inline] functions would not be profiled
profile_fns []string // when set, profiling will be off by default, but inside these functions (and what they call) it will be on.
translated bool // `v translate doom.v` are we running V code translated from C? allow globals, ++ expressions, etc
is_prod bool // use "-O2"
obfuscate bool // `v -obf program.v`, renames functions to "f_XXX"
is_repl bool
is_run bool
is_debug bool // turned on by -g or -cg, it tells v to pass -g to the C backend compiler.
is_vlines bool // turned on by -g (it slows down .tmp.c generation slightly).
// Note: passing -cg instead of -g will set is_vlines to false and is_debug to true, thus making v generate cleaner C files,
// which are sometimes easier to debug / inspect manually than the .tmp.c files by plain -g (when/if v line number generation breaks).
sanitize bool // use Clang's new "-fsanitize" option
@ -131,7 +141,6 @@ pub mut:
dump_c_flags string // `-dump-c-flags file.txt` - let V store all C flags, passed to the backend C compiler in `file.txt`, one C flag/value per line.
use_cache bool // when set, use cached modules to speed up subsequent compilations, at the cost of slower initial ones (while the modules are cached)
retry_compilation bool = true // retry the compilation with another C compiler, if tcc fails.
is_stats bool // `v -stats file_test.v` will produce more detailed statistics for the tests that were run
// TODO Convert this into a []string
cflags string // Additional options which will be passed to the C compiler.
// For example, passing -cflags -Os will cause the C compiler to optimize the generated binaries for size.
@ -146,10 +155,8 @@ pub mut:
// Disabling `free()` insertion results in better performance in some applications (e.g. compilers)
compress bool // when set, use `upx` to compress the generated executable
// generating_vh bool
no_builtin bool // Skip adding the `builtin` module implicitly. The generated C code may not compile.
enable_globals bool // allow __global for low level code
is_fmt bool
is_vet bool
no_builtin bool // Skip adding the `builtin` module implicitly. The generated C code may not compile.
enable_globals bool // allow __global for low level code
is_bare bool // set by -freestanding
bare_builtin_dir string // Set by -bare-builtin-dir xyz/ . The xyz/ module should contain implementations of malloc, memset, etc, that are used by the rest of V's `builtin` module. That option is only useful with -freestanding (i.e. when is_bare is true).
no_preludes bool // Prevents V from generating preludes in resulting .c files
@ -162,41 +169,41 @@ pub mut:
out_name_c string // full os.real_path to the generated .tmp.c file; set by builder.
out_name string
path string // Path to file/folder to compile
// -d vfmt and -d another=0 for `$if vfmt { will execute }` and `$if another ? { will NOT get here }`
//
run_only []string // VTEST_ONLY_FN and -run-only accept comma separated glob patterns.
// Only test_ functions that match these patterns will be run. -run-only is valid only for _test.v files.
compile_defines []string // just ['vfmt']
compile_defines_all []string // contains both: ['vfmt','another']
run_args []string // `v run x.v 1 2 3` => `1 2 3`
printfn_list []string // a list of generated function names, whose source should be shown, for debugging
print_v_files bool // when true, just print the list of all parsed .v files then stop.
skip_running bool // when true, do no try to run the produced file (set by b.cc(), when -o x.c or -o x.js)
skip_warnings bool // like C's "-w", forces warnings to be ignored.
warn_impure_v bool // -Wimpure-v, force a warning for JS.fn()/C.fn(), outside of .js.v/.c.v files. TODO: turn to an error by default
warns_are_errors bool // -W, like C's "-Werror", treat *every* warning is an error
fatal_errors bool // unconditionally exit after the first error with exit(1)
reuse_tmpc bool // do not use random names for .tmp.c and .tmp.c.rsp files, and do not remove them
no_rsp bool // when true, pass C backend options directly on the CLI (do not use `.rsp` files for them, some older C compilers do not support them)
no_std bool // when true, do not pass -std=gnu99(linux)/-std=c99 to the C backend
//
// -d vfmt and -d another=0 for `$if vfmt { will execute }` and `$if another ? { will NOT get here }`
compile_defines []string // just ['vfmt']
compile_defines_all []string // contains both: ['vfmt','another']
//
run_args []string // `v run x.v 1 2 3` => `1 2 3`
printfn_list []string // a list of generated function names, whose source should be shown, for debugging
print_v_files bool // when true, just print the list of all parsed .v files then stop.
skip_running bool // when true, do no try to run the produced file (set by b.cc(), when -o x.c or -o x.js)
skip_warnings bool // like C's "-w", forces warnings to be ignored.
warn_impure_v bool // -Wimpure-v, force a warning for JS.fn()/C.fn(), outside of .js.v/.c.v files. TODO: turn to an error by default
warns_are_errors bool // -W, like C's "-Werror", treat *every* warning is an error
fatal_errors bool // unconditionally exit after the first error with exit(1)
reuse_tmpc bool // do not use random names for .tmp.c and .tmp.c.rsp files, and do not remove them
no_rsp bool // when true, pass C backend options directly on the CLI (do not use `.rsp` files for them, some older C compilers do not support them)
no_std bool // when true, do not pass -std=gnu99(linux)/-std=c99 to the C backend
//
no_parallel bool // do not use threads when compiling; slower, but more portable and sometimes less buggy
only_check_syntax bool // when true, just parse the files, then stop, before running checker
check_only bool // same as only_check_syntax, but also runs the checker
experimental bool // enable experimental features
skip_unused bool // skip generating C code for functions, that are not used
show_timings bool // show how much time each compiler stage took
//
use_color ColorOutput // whether the warnings/errors should use ANSI color escapes.
no_parallel bool
is_vweb bool // skip _ var warning in templates
only_check_syntax bool // when true, just parse the files, then stop, before running checker
check_only bool // same as only_check_syntax, but also runs the checker
experimental bool // enable experimental features
skip_unused bool // skip generating C code for functions, that are not used
show_timings bool // show how much time each compiler stage took
is_ios_simulator bool
is_apk bool // build as Android .apk format
cleanup_files []string // list of temporary *.tmp.c and *.tmp.c.rsp files. Cleaned up on successfull builds.
build_options []string // list of options, that should be passed down to `build-module`, if needed for -usecache
cleanup_files []string // list of temporary *.tmp.c and *.tmp.c.rsp files. Cleaned up on successfull builds.
build_options []string // list of options, that should be passed down to `build-module`, if needed for -usecache
cache_manager vcache.CacheManager
is_help bool // -h, -help or --help was passed
gc_mode GarbageCollectionMode = .no_gc // .no_gc, .boehm, .boehm_leak, ...
is_cstrict bool // turn on more C warnings; slightly slower
assert_failure_mode AssertFailureMode // whether to call abort() or print_backtrace() after an assertion failure
assert_failure_mode AssertFailureMode // whether to call abort() or print_backtrace() after an assertion failure
message_limit int = 100 // the maximum amount of warnings/errors/notices that will be accumulated
nofloat bool // for low level code, like kernels: replaces f32 with u32 and f64 with u64
nofloat bool // for low level code, like kernels: replaces f32 with u32 and f64 with u64
// checker settings:
checker_match_exhaustive_cutoff_limit int = 12
thread_stack_size int = 8388608 // Change with `-thread-stack-size 4194304`. Note: on macos it was 524288, which is too small for more complex programs with many nested callexprs.
@ -206,8 +213,25 @@ pub fn parse_args(known_external_commands []string, args []string) (&Preferences
return parse_args_and_show_errors(known_external_commands, args, false)
}
[if linux]
fn detect_musl(mut res Preferences) {
res.is_glibc = true
res.is_musl = false
if os.exists('/etc/alpine-release') {
res.is_musl = true
res.is_glibc = false
return
}
my_libs := os.walk_ext('/proc/self/map_files/', '').map(os.real_path(it))
if my_libs.any(it.contains('musl')) {
res.is_musl = true
res.is_glibc = false
}
}
pub fn parse_args_and_show_errors(known_external_commands []string, args []string, show_output bool) (&Preferences, string) {
mut res := &Preferences{}
detect_musl(mut res)
$if x64 {
res.m64 = true // follow V model by default
}
@ -412,6 +436,16 @@ pub fn parse_args_and_show_errors(known_external_commands []string, args []strin
'-no-retry-compilation' {
res.retry_compilation = false
}
'-musl' {
res.is_musl = true
res.is_glibc = false
res.build_options << arg
}
'-glibc' {
res.is_musl = false
res.is_glibc = true
res.build_options << arg
}
'-no-builtin' {
res.no_builtin = true
res.build_options << arg
@ -579,6 +613,10 @@ pub fn parse_args_and_show_errors(known_external_commands []string, args []strin
'-cc' {
res.ccompiler = cmdline.option(current_args, '-cc', 'cc')
res.build_options << '$arg "$res.ccompiler"'
if res.ccompiler == 'musl-gcc' {
res.is_musl = true
res.is_glibc = false
}
i++
}
'-checker-match-exhaustive-cutoff-limit' {