ikvmnet / ikvm

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

verify.dll conflicts with unique module name requirement of Windows #462

Open askids opened 6 months ago

askids commented 6 months ago

hi,

We have a .Net 6.0 console app that does batch processing and is targeted for Windows 64 bit. Within that process, I am invoking a custom Java library class using the IKVM library. This was working fine in the previous versions (8.4.x, 8.5.x upto 8.6.4). However after I upgraded from 8.6.4 to 8.7.1, the process fails to initialize, when the java class is called.

This is what I have captured from my logs. In the dependencies, I have 2 jars - one is my custom library and second is the jdbc db2 driver package (.csproj file info is given below).

For the time being, I have reverted back to 8.6.4.

Please let me know if you need any further details to investigate this further.

"System.TypeInitializationException: The type initializer for 'java.lang.Object' threw an exception. ---> System.TypeInitializationException: The type initializer for 'java.io.FileInputStream' threw an exception. ---> System.TypeInitializationException: The type initializer for 'IKVM.Runtime.LibJava' threw an exception. ---> System.TypeInitializationException: The type initializer for 'IKVM.Runtime.LibJvm' threw an exception. ---> IKVM.Runtime.InternalException: Could not load libjvm. at IKVM.Runtime.LibJvm..ctor() at IKVM.Runtime.LibJvm..cctor() --- End of inner exception stack trace --- at IKVM.Runtime.LibJava..ctor() at IKVM.Runtime.LibJava..cctor() --- End of inner exception stack trace --- at IKVM.Runtime.BootstrapClassLoader..ctor(RuntimeContext context) at IKVM.Runtime.RuntimeClassLoaderFactory.GetBootstrapClassLoader() at IKVM.Runtime.RuntimeClassLoaderFactory.GetClassLoaderWrapper(ClassLoader javaClassLoader) at IKVM.Runtime.JNI.JNIFrame.GetFuncPtr(CallerID callerID, String clazz, String name, String sig) at java.io.FileInputStream.initIDs() at java.io.FileInputStream..cctor() --- End of inner exception stack trace --- at java.io.FileInputStream.__<clinit>() at java.lang.System.initializeSystemClass() at __<MethodAccessor>__System__initializeSystemClass() at IKVM.Runtime.JVM.EnsureInitialized() at java.lang.Object..cctor() --- End of inner exception stack trace --- at java.lang.Object..ctor() at mynamespace.ZLoadRequest..ctor()

Project dependencies is as give below. ``

net6.0 MyNamspace.ZLoader MyNamspace.ZLoader MyNamspace.ZLoader enable disable x64 com.ibm.db2 11.5.8.0 11.5.8.0 MyAppNamspace.ZLoad 1.0.0.0 1.0.0.0 db2jcc4.jar

`` Thanks!

wasabii commented 6 months ago

Please try the latest version.

askids commented 6 months ago

Please try the latest version.

I knew, you would say that :) I did see the later versions on GitHub. But we are in a restricted development environment and don't have access to direct nuget feed to pull latest version. Instead, we use some cached feed (post some scanning etc). So i will need to wait a few more days before the new version reflects in the internal nuget feed. BTW, was this a known issue in 8.7.1 which was fixed in subsequent release?

wasabii commented 6 months ago

Maybe. There's been a few issues with the new native library stuff in 8.7 that I've fixed in subsequent versions.

askids commented 6 months ago

Ok. Once I have the new version available, I will try it and report back if the issue is resolved or still happening.

Thanks

askids commented 6 months ago

I tried it with 8.7.3 and still getting same error as originally reported.

danielcweber commented 6 months ago

I had a look into this. The folder from which IKVM tries to load all the native libaries looks like this:

image

The load that fails is done by LibJvm._JVM_LoadLibrary(...). I threw all the files into the method, and they produce seemingly valid handles, except for

iava.dll net.dll nio.dll unpack.dll

The failure to load iava.dll produces the error that we see.

Edit: Path prefix for me is net8.0\ikvm\win-x64\bin.

wasabii commented 6 months ago

So one of the issues I had found for 8.7.0 was that the default RID for Framework was win-x86. So, it would publish win-x86.

But then run 64-bit anyways.

So, I adjusted it so that if it publishes win-x86, and Prefer32Bit != 'true', then it includes both win-x86 and win-64 libraries. So, regardless what it happens to run as, the native libs should be available.

But, maybe this is a different issue.

I do think I probably need to see your project output at this point. Or some reproduction. To see what is where and what it's running as.

danielcweber commented 6 months ago

I'll do my best to isolate something and test the latest SDK from Github packages. It might take me a while.

danielcweber commented 6 months ago

I was able to strip things down to the likely culprit: https://github.com/danielcweber/IKVM_Repro

It's a xUnit test project with a library that references IKVM. The library itself contains no code, so the test project on it's own with a reference to IKVM is probably already sufficient.

It can be run simply with dotnet test or within Visual Studio. Tests start failing when code from the snapshot utility VerifyTests is loaded (AppDomains?!). Tests will succeed in isolation if no code from VerifyTests has run before. I can't tell yet whether there's something weird going on at runtime or whether the reference to VerifyTests already confuses the rids in IKVM....

I'd understand if you'd consider this an edge case due to some weird behaviour by a third party library. Maybe this can provide some insight though.

Thanks a lot!

wasabii commented 6 months ago

@danielcweber Is this a new thing for you, or related to the thing the OP is talking about?

wasabii commented 6 months ago

What's fun is to reverse the order of VerifySettings and new java.lang.Object.

I'm currently baffled.

wasabii commented 6 months ago

Okay. So, Verify. I've at least figured this much. Verify.dll has a ModuleInitializer that looks for Verify.*.dll for plugins, and loads them all. And the plugins all have module initializers themselves. And perhaps one of these loads IKVM.Java/IKVM.Runtime, and somehow enumerates something in it which causes a static ctor to be invoked before the JVM is initialized.

I think maybe the OP's issue is just that he needs to force the JVM to initialize before calling into it: by calling new java.lang.Object at the top of his program.

danielcweber commented 6 months ago

@wasabii Wow. Thanks for investigating.

Is this a new thing for you, or related to the thing the OP is talking about?

I think it's pretty much the same thing, although it would be interesting if @askids could strip their repro to the minimal parts to see what else is causing such behaviour.

wasabii commented 6 months ago

Can you give 8.7.4-pre.16 a go?

danielcweber commented 6 months ago

I will give it a try as soon as I am able to. Thank you very much for tackling this!

danielcweber commented 5 months ago

@wasabii It works in the repro project, but not in my actual project. So I guess I will again carefully strip it down to the culprit.

danielcweber commented 5 months ago

Made the repro fail again. Creating something from a Maven dependency makes it fail. That's maybe only because the Maven SDK is from Nuget.org and has not received any experimental fixes.

askids commented 5 months ago

Made the repro fail again. Creating something from a Maven dependency makes it fail. That's maybe only because the Maven SDK is from Nuget.org and has not received any experimental fixes.

In my case, I am not using maven reference. Its a local jar reference to both jars (one my custom jar and 2nd from IBM).

askids commented 5 months ago

Tried it on recent 8.7.5 release and still seeing same issue.

wasabii commented 5 months ago

@askids In your case do you have all the folders and libraries in the right spot for the architecture you're trying to run on?

wasabii commented 5 months ago

And have you tried the workaround of forcing the JVM to initialize before VerifySettings by ... doing anything with it really, but new java.lang.Object() is the easiest.

askids commented 5 months ago

And have you tried the workaround of forcing the JVM to initialize before VerifySettings by ... doing anything with it really, but new java.lang.Object() is the easiest.

Tried add a dummy line before call to my actual java class. But the error is still generated from the old line of code only.

var dummy = new java.lang.Object();
var zLoadRunner = new ZLoadRunner(GetZLoadRequest(request));
var response = zLoadRunner.Load(); // ---------> error is generated pointing to this line in stacktrace.
askids commented 5 months ago

@askids In your case do you have all the folders and libraries in the right spot for the architecture you're trying to run on?

Didn't get what you mean by this? I haven't setup anything specifically. Its same as how it was before in 8,6.4. Target project is x64 on .Net 6.0

wasabii commented 5 months ago

And have you tried the workaround of forcing the JVM to initialize before VerifySettings by ... doing anything with it really, but new java.lang.Object() is the easiest.

Tried add a dummy line before call to my actual java class. But the error is still generated from the old line of code only.

var dummy = new java.lang.Object();
var zLoadRunner = new ZLoadRunner(GetZLoadRequest(request));
var response = zLoadRunner.Load(); // ---------> error is generated pointing to this line in stacktrace.

The same error?

askids commented 5 months ago

There is one small difference. The type initializer for '<Module>' threw an exception. . Previously, it was pointing to java.lang.Object. Rest of the details in stack trace is same...says could not load libjvm.

wasabii commented 5 months ago

So, anyway you can get a repro that mirrors this?

I'm not able to. Anything related to libjvm should have broken in the first access to IKVM.Runtime, which would happen on new java.lang.Object. Except you're passed that. =/

askids commented 5 months ago

I am on a restricted client environment. It will take some time for me to share just the full stack trace :). Have requested the information to be shared. Let me try to see if I can create a stripped down version of the code, if possible.

danielcweber commented 5 months ago

Made the repro fail again. Creating something from a Maven dependency makes it fail. That's maybe only because the Maven SDK is from Nuget.org and has not received any experimental fixes.

I will give it a try the v8.7.5 and the latest Maven SDK.

askids commented 5 months ago

ok. I somehow got it working. The cs project which had the IKVM reference had PlatformTarget set to x64. When I removed the platform target and did a clean build, the process worked fine. With 8.6.4, it still works, when my project has PlatformTarget set to x64.

wasabii commented 5 months ago

@askids Okay. That's a hint then. What I would check is whether, if you add PlatformTarget, what happens to the output and stuff?

Does it generate a .exe file in the win-x86 folder? And yet, when running that exe, does it run as 64 bit? if so, it'll need 64 bit libraries. Do the 64 bit libraries get copied to the output regardless of the exe being win-x86? Etc.

I need to dig into PlatformTarget and figure out wtf it's supposed to mean.

askids commented 5 months ago

I have 3 level projects in the solution. In all 3 projects, I had platformtarget set to x64. Top level project is a console app with exe output, which references the 2nd level library project, which in turn references the 3rd level library project which has the IKVM and jar reference. I removed platformTarget only from the 3rd library that has IKVM reference. Console app and 2nd library still has X64 platformtarget. So generated app is still a x64 exeutable.

I had manually checked the ikvm folders, when the error was earlier occurring. The jvm.dll was present in both win-x86 and win-x64 bin folders under ikvm folder. This is still the case, after I removed the platformtarget from the 3rd library.

wasabii commented 5 months ago

May I ask why you are setting PlatformTarget? No combination of default options for Windows apps for Core that I can find in Visual Studio seems to add that Property.

danielcweber commented 4 months ago

@wasabii I updated the repro project to the latest IKVM internal preview/stable Maven SDK but the failure persists.

danielcweber commented 3 months ago

@wasabii I again gave it a try and updated the repro project. I observed that the error message changes from "Could not load libjava" to "Could not load libiava"...maybe this piece of information renders helpful.

Unfortunately, I haven't found a proper workaround (early loading of code in static initializers, etc.) yet.

danielcweber commented 3 months ago

I added some more tests that show that it's apparently not even about the order that ModuleInitializers are called, but about when the code is jitted. Just a guess though.

wasabii commented 3 months ago

Ok. Yeah, I can reproduce this. I'm not yet sure why it isn't and to load. I'm pushing some changes to get better logging for it though so I'll know soon.

wasabii commented 2 months ago

So, @danielcweber, I figured out an issue I believe. Still coming up with a solution.

It's because the managed assembly is named verify.dll. Which conflicts with verify.dll. If the native library loads second, the managed library is already loaded and confuses it.