From fd269cad875a864190c5cf4f46190de20ef00319 Mon Sep 17 00:00:00 2001 From: kbkpbot Date: Wed, 10 Jan 2024 16:59:52 +0800 Subject: [PATCH] cgen, dl: add windows dll support, fix (#20447) (#20459) --- .../modules/library/library.v | 4 ++- examples/dynamic_library_loader/use_test.v | 3 +-- vlib/dl/dl_windows.c.v | 18 +++++++++++++ vlib/v/gen/c/cgen.v | 27 ++++++++++++++----- 4 files changed, 43 insertions(+), 9 deletions(-) diff --git a/examples/dynamic_library_loader/modules/library/library.v b/examples/dynamic_library_loader/modules/library/library.v index 2b545c12a08d47..a77189a6d3b429 100644 --- a/examples/dynamic_library_loader/modules/library/library.v +++ b/examples/dynamic_library_loader/modules/library/library.v @@ -10,7 +10,9 @@ module library // `library__add_1`, if not for the export: tag). @[export: 'add_1'] 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. diff --git a/examples/dynamic_library_loader/use_test.v b/examples/dynamic_library_loader/use_test.v index 1451f9168fcaa9..214a0edf440a63 100644 --- a/examples/dynamic_library_loader/use_test.v +++ b/examples/dynamic_library_loader/use_test.v @@ -1,7 +1,5 @@ module main -// vtest flaky: true -// vtest retry: 3 import os import dl @@ -31,6 +29,7 @@ fn test_can_compile_main_program() { assert os.is_file(library_file_path) result := v_compile('run use_shared_library.v') // dump(result) + assert result.output.contains('hello from add_1 , num = 4') assert result.output.contains('res: 4') os.rm(library_file_path) or {} } diff --git a/vlib/dl/dl_windows.c.v b/vlib/dl/dl_windows.c.v index a034db53f92279..9502f677b5a40d 100644 --- a/vlib/dl/dl_windows.c.v +++ b/vlib/dl/dl_windows.c.v @@ -13,14 +13,32 @@ fn C.GetProcAddress(handle voidptr, procname &u8) voidptr 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. pub fn open(filename string, flags int) voidptr { 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 } // close frees the loaded a given module. 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) } diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index a0cf6bce751c1d..e6fc4a53a54702 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -5912,8 +5912,9 @@ fn (mut g Gen) write_init_function() { 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. + // shared object does not need this g.writeln('#if __STDC_HOSTED__ == 1\n\tsignal(11, v_segmentation_fault_handler);\n#endif') } 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... g.write('\tas_cast_type_indexes = ') g.writeln(g.as_cast_name_table()) - g.writeln('\tbuiltin_init();') + if !g.pref.is_shared { + // shared object does not need this + g.writeln('\tbuiltin_init();') + } if g.nr_closures > 0 { 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)) } - needs_constructor := g.pref.is_shared && g.pref.os != .windows - if needs_constructor { + if g.pref.is_shared { // shared libraries need a way to call _vinit/2. For that purpose, // provide a constructor/destructor pair, ensuring that all constants // are initialized just once, and that they will be freed too. // Note: os.args in this case will be []. - g.writeln('__attribute__ ((constructor))') + 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('void _vinit_caller() {') g.writeln('\tstatic bool once = false; if (once) {return;} once = true;') g.writeln('\t_vinit(0,0);') g.writeln('}') - g.writeln('__attribute__ ((destructor))') + 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('void _vcleanup_caller() {') g.writeln('\tstatic bool once = false; if (once) {return;} once = true;') g.writeln('\t_vcleanup();')