ikvmnet / ikvm

A Java Virtual Machine and Bytecode-to-IL Converter for .NET
Other
1.27k stars 120 forks source link

Dll dependency not being found #547

Open dlif-v opened 4 months ago

dlif-v commented 4 months ago

Using IKVM 8.8.1 in a .NET 6.0 Windows Application, I have a jar that calls System.loadLibrary("ABC"); where ABC depends on XYZ and each dll is in its own directory (eg C:\abc\ABC.dll and C:\xyz\XYZ.dll) and both directories are in PATH (and appear as expected in java.library.path). I get the following exception:

java.lang.UnsatisfiedLinkError: C:\abc\ABC.dll: Can't find dependent libraries
Exception thrown: 'java.lang.UnsatisfiedLinkError' in IKVM.Java.dll

If I use the jar in a java app then it loads both dlls fine. If I copy both dlls to the exe's dir then it loads both dlls fine (but that's not right or practical). If I DllImport any function from ABC and call the function in C# then it loads both dlls fine (but that is hacky, etc). If I add System.loadLibrary("XYZ"); in the java first then it loads both dlls fine (but this is not a cross-platform solution since on Android XYZ is named something else so it will fail there).

Expected: loading a lib from java should work the same as it does in java (and the regular .net app)

AliveDevil commented 4 months ago

~Probably dependency issue with VCRT.~ ~Check ABC.dll and XYZ.dll with Dependencies for direct dependencies (i.e. msvcrt, vcruntime).~

copy both dlls to the exe's dir then it loads both dlls fine

scratch what I wrote.

wasabii commented 4 months ago

Would probably need more details on the specific DLL or something. There's actually not much in this process that IKVM is responsible for. We call LoadLibraryExW(wfilename, 0, LOAD_LIBRARY_SEARCH_DEFAULT_DIRS | LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR); and present the error back as an exception. Everything that LoadLibraryExW does is up to Windows itself. We have very little control over how it goes about locating dependencies.

One thing I can sort of imagine is that maybe it depends on something else that is different in a native Java. But I can't know that without seeing it.

dlif-v commented 4 months ago

Thanks for the hint. I traced this issue back to a commit last June switching from LoadLibrary (which works fine in my case) to LoadLibraryEx with LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR, and then LOAD_LIBRARY_SEARCH_DEFAULT_DIRS was added later. Note the documentation for both options say "Directories in the standard search path are not searched" which is what's breaking things.

The commit mentions it's trying to fix loading libs from a given path so my suggestion would be to revert back to using the regular LoadLibrary and call AddDllDirectory at some point to add the extra given paths.

wasabii commented 4 months ago

Ahhhhh. Okay, so, yeah, the PATH env is left out.

So we have a problem here. Because IKVM runs in process with .NET, and is loaded as an assembly, I'm a bit hesitent to change anything related to the process overall. For instance, by calling AddDllDirectory. That effects the search order of not just IKVM loaded JNI stuff, but also anything loaded by .NET (other P/Invoke calls) or other dlls, etc.

As to LoadLibrary: the reason I replaced it with LoadLibraryEx, is so dependent DLLs in the same folder as their parent are found. LoadLibrary doesn't allow this. But it was necessary because of the load order of some of the built in JVM DLLs (libiava, libjvm, etc). Since these load directly out of the executable path on a normal JVM (java.exe is located in the same dir as net.dll, etc), but in the .NET case, the process application path may differ (foo.exe using IKVM against a ikvm home dir located elsewhere).

So I need to come up with a better solution it seems. Something that makes the dependent search path for JDK DLLs work, regardless where the process is loaded form, but also doesn't override the user expectations that dependents can be on PATH.