vlang / v

Simple, fast, safe, compiled language for developing maintainable software. Compiles itself in <1s with zero library dependencies. Supports automatic C => V translation. https://vlang.io
MIT License
35.8k stars 2.17k forks source link

Missing main entry point in shared libraries #6983

Open Ekopalypse opened 3 years ago

Ekopalypse commented 3 years ago

Unlike executable files libraries do not use a main entry point, at least on Windows. No entry point means _vinit is not called at all, hence consts etc... do not get initialized. On Windows I would suggest to generate the DllMain function which calls then _vinit and possibly a user-main function, which gets the parameters from DllMain forwarded.

V version: OS: windows, Microsoft Windows 7 Professional v7601 64-bit Processor: 4 cpus, 64bit, little endian, Intel(R) Core(TM) i5-2500 CPU @ 3.30GHz CC version: N/A

vroot: D:\ProgramData\Compiler\v vexe: D:\ProgramData\Compiler\v\v.exe vexe mtime: 2020-11-27 18:44:30 is vroot writable: true V full version: V 0.1.29 e0d6490.1891f55

Git version: git version 2.19.1.windows.1 Git vroot status: latest-commit-414-g1891f55c .git/config present: true thirdparty/tcc status: master 17d18c13

What did you do? build shared library

What did you expect to see? The function DllMain created

What did you see instead? DllMain is missing

Ekopalypse commented 3 years ago

Here is a sample code that shows the effect of the missing _vinit function a bit more clearly.

code for dll build:

// v -cc msvc -shared FILE_NAME
module testdll

// fn C._vinit(int, voidptr)
// fn C._vcleanup()
// fn C.GC_INIT()

const (
    r = 0.14
    s = 3 + r
)

[windows_stdcall]
[export: get_pi]
pub fn return_pi() f32 {
    return s
}

// [windows_stdcall]
// [export: DllMain]
// fn main(hinst voidptr, fdw_reason int, lp_reserved voidptr) bool{
    // match fdw_reason {
        // C.DLL_PROCESS_ATTACH {
            // $if static_boehm ? {
                // C.GC_INIT()
            // }
            // C._vinit(0, 0)
        // }
        // C.DLL_THREAD_ATTACH {
        // }
        // C.DLL_THREAD_DETACH {}
        // C.DLL_PROCESS_DETACH {
            // C._vcleanup()
        // }
        // else { return false }
    // }
    // return true
// }

and the code to test

import dl

const ( handle = dl.open('./test_dll.dll', 0) )
type FUNCPTR = fn()f32

fn main() {
    f := dl.sym(handle, 'get_pi')
    assert f != 0
    func := FUNCPTR(f)
    println(func())
}

If the code for the dll remains commented, the result from the to-test code is 0. If the lines in the dll code are commented out, then the result is the expected 3.14

felipensp commented 1 year ago

Here is a sample code that shows the effect of the missing _vinit function a bit more clearly.

What command are you using to build both sources?

Ekopalypse commented 1 year ago

Hi, to create the DLL I used v -cc msvc -shared test_dll.v (-cc gcc/clang could also be used) and for the test application simply v run test.v.

esquerbatua commented 1 month ago

Updated example:

lib, compile with: v -shared <file_name>.v -o lib.so

module testdll

const r = 0.14
const s = 3 + r

@[callconv: stdcall]
@[export: get_pi]
pub fn return_pi() f32 {
    return s
}

main: run with: v run <file_name>.v

import dl

const handle = dl.open('./lib.so', 0)
type FUNCPTR = fn()f32

fn main() {
    f := dl.sym(handle, 'get_pi')
    assert f != 0
    func := FUNCPTR(f)
    println(func())
}