cgen, dl: add windows dll support, fix (#20447) (#20459)

This commit is contained in:
kbkpbot 2024-01-10 16:59:52 +08:00 committed by GitHub
parent be4f7176f4
commit fd269cad87
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 43 additions and 9 deletions

View file

@ -10,7 +10,9 @@ module library
// `library__add_1`, if not for the export: tag). // `library__add_1`, if not for the export: tag).
@[export: 'add_1'] @[export: 'add_1']
pub fn add_1(x int, y int) int { pub fn add_1(x int, y int) int {
return my_private_function(x + y) num := my_private_function(x + y)
println('hello from ${@FN} , num = ${num}')
return num
} }
// this function is not exported and will not be visible to external programs. // this function is not exported and will not be visible to external programs.

View file

@ -1,7 +1,5 @@
module main module main
// vtest flaky: true
// vtest retry: 3
import os import os
import dl import dl
@ -31,6 +29,7 @@ fn test_can_compile_main_program() {
assert os.is_file(library_file_path) assert os.is_file(library_file_path)
result := v_compile('run use_shared_library.v') result := v_compile('run use_shared_library.v')
// dump(result) // dump(result)
assert result.output.contains('hello from add_1 , num = 4')
assert result.output.contains('res: 4') assert result.output.contains('res: 4')
os.rm(library_file_path) or {} os.rm(library_file_path) or {}
} }

View file

@ -13,14 +13,32 @@ fn C.GetProcAddress(handle voidptr, procname &u8) voidptr
fn C.FreeLibrary(handle voidptr) bool fn C.FreeLibrary(handle voidptr) bool
type FN_vinit_caller = fn ()
type FN_vcleanup_caller = fn ()
// open loads a given module into the address space of the calling process. // open loads a given module into the address space of the calling process.
pub fn open(filename string, flags int) voidptr { pub fn open(filename string, flags int) voidptr {
res := C.LoadLibrary(filename.to_wide()) res := C.LoadLibrary(filename.to_wide())
// Because LoadLibrary has no constructor, this is a workaround
if !isnil(res) {
vinit_caller := FN_vinit_caller(sym(res, '_vinit_caller'))
if !isnil(vinit_caller) {
vinit_caller()
}
}
return res return res
} }
// close frees the loaded a given module. // close frees the loaded a given module.
pub fn close(handle voidptr) bool { pub fn close(handle voidptr) bool {
// Because FreeLibrary has no destructor, this is a workaround
if !isnil(handle) {
vcleanup_caller := FN_vcleanup_caller(sym(handle, '_vcleanup_caller'))
if !isnil(vcleanup_caller) {
vcleanup_caller()
}
}
return C.FreeLibrary(handle) return C.FreeLibrary(handle)
} }

View file

@ -5912,8 +5912,9 @@ fn (mut g Gen) write_init_function() {
g.writeln('\tv__trace_calls__on_call(_SLIT("_vinit"));') g.writeln('\tv__trace_calls__on_call(_SLIT("_vinit"));')
} }
if g.use_segfault_handler { if g.use_segfault_handler && !g.pref.is_shared {
// 11 is SIGSEGV. It is hardcoded here, to avoid FreeBSD compilation errors for trivial examples. // 11 is SIGSEGV. It is hardcoded here, to avoid FreeBSD compilation errors for trivial examples.
// shared object does not need this
g.writeln('#if __STDC_HOSTED__ == 1\n\tsignal(11, v_segmentation_fault_handler);\n#endif') g.writeln('#if __STDC_HOSTED__ == 1\n\tsignal(11, v_segmentation_fault_handler);\n#endif')
} }
if g.pref.is_bare { if g.pref.is_bare {
@ -5928,7 +5929,10 @@ fn (mut g Gen) write_init_function() {
// calling module init functions too, just in case they do fail... // calling module init functions too, just in case they do fail...
g.write('\tas_cast_type_indexes = ') g.write('\tas_cast_type_indexes = ')
g.writeln(g.as_cast_name_table()) g.writeln(g.as_cast_name_table())
if !g.pref.is_shared {
// shared object does not need this
g.writeln('\tbuiltin_init();') g.writeln('\tbuiltin_init();')
}
if g.nr_closures > 0 { if g.nr_closures > 0 {
g.writeln('\t_closure_mtx_init();') g.writeln('\t_closure_mtx_init();')
@ -6020,19 +6024,30 @@ fn (mut g Gen) write_init_function() {
println(g.out.after(fn_vcleanup_start_pos)) println(g.out.after(fn_vcleanup_start_pos))
} }
needs_constructor := g.pref.is_shared && g.pref.os != .windows if g.pref.is_shared {
if needs_constructor {
// shared libraries need a way to call _vinit/2. For that purpose, // shared libraries need a way to call _vinit/2. For that purpose,
// provide a constructor/destructor pair, ensuring that all constants // provide a constructor/destructor pair, ensuring that all constants
// are initialized just once, and that they will be freed too. // are initialized just once, and that they will be freed too.
// Note: os.args in this case will be []. // Note: os.args in this case will be [].
if g.pref.os == .windows {
g.writeln('// workaround for windows, export _vinit_caller, let dl.open() call it')
g.writeln('// NOTE: This is hardcoded in vlib/dl/dl_windows.c.v!')
g.writeln('VV_EXPORTED_SYMBOL void _vinit_caller();')
} else {
g.writeln('__attribute__ ((constructor))') g.writeln('__attribute__ ((constructor))')
}
g.writeln('void _vinit_caller() {') g.writeln('void _vinit_caller() {')
g.writeln('\tstatic bool once = false; if (once) {return;} once = true;') g.writeln('\tstatic bool once = false; if (once) {return;} once = true;')
g.writeln('\t_vinit(0,0);') g.writeln('\t_vinit(0,0);')
g.writeln('}') g.writeln('}')
if g.pref.os == .windows {
g.writeln('// workaround for windows, export _vcleanup_caller, let dl.close() call it')
g.writeln('// NOTE: This is hardcoded in vlib/dl/dl_windows.c.v!')
g.writeln('VV_EXPORTED_SYMBOL void _vcleanup_caller();')
} else {
g.writeln('__attribute__ ((destructor))') g.writeln('__attribute__ ((destructor))')
}
g.writeln('void _vcleanup_caller() {') g.writeln('void _vcleanup_caller() {')
g.writeln('\tstatic bool once = false; if (once) {return;} once = true;') g.writeln('\tstatic bool once = false; if (once) {return;} once = true;')
g.writeln('\t_vcleanup();') g.writeln('\t_vcleanup();')