P/Invoke to dynamically loaded library on Mono

后端 未结 4 851

I\'m writing a cross-platform .NET library that uses some unmanaged code. In the static constructor of my class, the platform is detected and the appropriate unmanaged library

相关标签:
4条回答
  • 2021-02-02 00:59

    Not sure why you think this is related to mono, since the issue you're having is not about mono's dynamic loading facilities.

    If your updated sample works, it just means that LoadLibrary() on windows has different semantics than dlopen() on Linux: as such you either have to live with the difference or implement your own abstraction that deals with the directory issue (my hunch is that it's not the directory that is retained, but windows simply looks to see if a library with the same name was already loaded and it reuses that).

    0 讨论(0)
  • 2021-02-02 01:02

    I needed to load a native library extracted to a temporary location, and I almost found a solution. I've checked Mono's source code and figured out a way:

    [DllImport("__Internal", CharSet = CharSet.Ansi)]
    private static extern void mono_dllmap_insert(IntPtr assembly, string dll, string func, string tdll, string tfunc);
    
    // and then somewhere:
    mono_dllmap_insert(IntPtr.Zero, "somelib", null, "/path/to/libsomelib.so", null);
    

    This kind of works. The problem is, you cannot allow Mono's stupid JIT compiler to catch a whiff of any DllImported method referring this library before calling mono_dllmap_insert().

    Because if it does, strange things will happen:

    Mono: DllImport searching in: '/tmp/yc1ja5g7.emu/libsomelib.so' ('/tmp/yc1ja5g7.emu/libsomelib.so').
    Mono: Searching for 'someGreatFunc'.
    Mono: Probing 'someGreatFunc'.
    Mono: Found as 'someGreatFunc'.
    Error. ex=System.DllNotFoundException: somelib
    

    So now that I'm calling my native someGreatFunc(), Mono is able to find the library and load it (I checked), it is able to find the symbol (I checked), but because somewhen in the past when it was doing JIT it was not able to load that library, it decides to throw DllNotFoundException anyway. I guess the generated code contains a hardcoded throw statement or something :-O

    When you call another native function from the same library that happens not to have been JITted before you called mono_dllmap_insert(), it will work.

    So you can either use the manual solution added by @gordonmleigh or you must tell Mono where the library is BEFORE it JITs any of these imports. Reflection may help there.

    0 讨论(0)
  • 2021-02-02 01:08

    After much searching and head-scratching, I've discovered a solution. Full control can be exercised over the P/Invoke process by using dynamic P/Invoke to tell the runtime exactly where to find the code.


    Edit:

    Windows solution

    You need these imports:

    [DllImport("kernel32.dll")]
    protected static extern IntPtr LoadLibrary(string filename);
    
    [DllImport("kernel32.dll")]
    protected static extern IntPtr GetProcAddress(IntPtr hModule, string procname);
    

    The unmanaged library should be loaded by calling LoadLibrary:

    IntPtr moduleHandle = LoadLibrary("path/to/library.dll");
    

    Get a pointer to a function in the dll by calling GetProcAddress:

    IntPtr ptr = GetProcAddress(moduleHandle, methodName);
    

    Cast this ptr to a delegate of type TDelegate:

    TDelegate func = Marshal.GetDelegateForFunctionPointer(
        ptr, typeof(TDelegate)) as TDelegate;
    

    Linux Solution

    Use these imports:

    [DllImport("libdl.so")]
    protected static extern IntPtr dlopen(string filename, int flags);
    
    [DllImport("libdl.so")]
    protected static extern IntPtr dlsym(IntPtr handle, string symbol);
    
    const int RTLD_NOW = 2; // for dlopen's flags 
    

    Load the library:

    IntPtr moduleHandle = dlopen(modulePath, RTLD_NOW);
    

    Get the function pointer:

    IntPtr ptr = dlsym(moduleHandle, methodName);
    

    Cast it to a delegate as before:

    TDelegate func = Marshal.GetDelegateForFunctionPointer(
        ptr, typeof(TDelegate)) as TDelegate;
    

    For a helper library that I wrote, see my GitHub.

    0 讨论(0)
  • 2021-02-02 01:17

    Try running it like this from a terminal:

    export MONO_LOG_LEVEL=debug
    export MONO_LOG_MASK=dll
    mono --debug yourapp.exe
    

    Now every library lookup will be printed to the terminal, so you'll be able to find out what's going wrong.

    0 讨论(0)
提交回复
热议问题