Skip to content

Commit

Permalink
cgen, dl: add windows dll support, fix (#20447) (#20459)
Browse files Browse the repository at this point in the history
  • Loading branch information
kbkpbot committed Jan 10, 2024
1 parent be4f717 commit fd269ca
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 9 deletions.
4 changes: 3 additions & 1 deletion examples/dynamic_library_loader/modules/library/library.v
Expand Up @@ -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.
Expand Down
3 changes: 1 addition & 2 deletions examples/dynamic_library_loader/use_test.v
@@ -1,7 +1,5 @@
module main

// vtest flaky: true
// vtest retry: 3
import os
import dl

Expand Down Expand Up @@ -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 {}
}
Expand Down
18 changes: 18 additions & 0 deletions vlib/dl/dl_windows.c.v
Expand Up @@ -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)
}

Expand Down
27 changes: 21 additions & 6 deletions vlib/v/gen/c/cgen.v
Expand Up @@ -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 {
Expand All @@ -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();')
Expand Down Expand Up @@ -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();')
Expand Down

0 comments on commit fd269ca

Please sign in to comment.