ikvmnet / ikvm

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

Minecraft 1.14.4 (LWJGL 3.2.2) java.lang.ExceptionInInitializerError due to unsafe object scanning #336

Closed TheLastRar closed 1 year ago

TheLastRar commented 1 year ago

IKVM version : image-netcoreapp3.1-win7-x64 built using develop commit https://github.com/ikvmnet/ikvm/commit/b26343bf7001ea226b132ea4f9f15a0f67d33439 with https://github.com/ikvmnet/ikvm/commit/8cd5f0bef208c366618ec4d3ffd5b035d9a0d57a cherry picked on top Without https://github.com/ikvmnet/ikvm/commit/8cd5f0bef208c366618ec4d3ffd5b035d9a0d57 you would get the FieldInfo must be a runtime FieldInfo object. exception

Tested with both official launcher and Prism Launcher 6.3

java.lang.ExceptionInInitializerError: java.lang.RuntimeException: java.lang.ExceptionInInitializerError: java.lang.UnsupportedOperationException: java.lang.InternalError: cli.System.NullReferenceException
    at cyg.<init>(SourceFile:151)
    at cyc.au(SourceFile:440)
    at cyc.b(SourceFile:394)
    at org.prismlauncher.launcher.impl.StandardLauncher.launch(StandardLauncher.java:89)
    at org.prismlauncher.EntryPoint.listen(EntryPoint.java:128)
    at org.prismlauncher.EntryPoint.main(EntryPoint.java:71)
    at java.lang.reflect.Method.invoke(Method.java:486)
    at IKVM.Java.Externs.ikvm.runtime.Launcher.run(Launcher.cs:43)
wasabii commented 1 year ago

Any change you can try this on the latest hotfix branch? 8.5.2?

TheLastRar commented 1 year ago

No change

I did however give the issue another look

I saw that only a few places could throw a java.lang.InternalError with a cli exception type (unsafe and one other class), so I had built a version of IKVM with some extra logging for these exceptions and got this stacktrace

java.lang.InternalError: cli.System.NullReferenceException
    at IKVM.Java.Externs.sun.misc.Unsafe.getLong0(Unsafe.cs:662)
    at sun.misc.Unsafe.getLong0(Native Method)
    at sun.misc.Unsafe.getLong(Unsafe.java:348)
    at org.lwjgl.system.MemoryUtil.getAddressOffset(MemoryUtil.java:2687)
    at org.lwjgl.system.MemoryUtil.<clinit>(MemoryUtil.java:113)
    at org.lwjgl.system.Pointer$Default.<clinit>(Pointer.java:67)
    at org.lwjgl.system.windows.WindowsLibrary.<clinit>(WindowsLibrary.java:23)
    at org.lwjgl.system.windows.WindowsLibrary.<init>(WindowsLibrary.java:32)
    at org.lwjgl.system.APIUtil.apiCreateLibrary(APIUtil.java:121)
    at org.lwjgl.system.Library.loadNative(Library.java:335)
    at org.lwjgl.system.Library.loadNative(Library.java:281)
    at org.lwjgl.system.Library.loadNative(Library.java:205)
    at org.lwjgl.glfw.GLFW.<clinit>(GLFW.java:674)
    at cli.System.Runtime.CompilerServices.RuntimeHelpers._RunClassConstructor(Unknown Source)
    at cli.System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(Unknown Source)
    at IKVM.Internal.TypeWrapper.RunClassInit(TypeWrapper.cs:1267)
    at IKVM.Java.Externs.sun.misc.Unsafe.ensureClassInitialized(Unsafe.cs:1585)
    at sun.misc.Unsafe.ensureClassInitialized(Native Method)
    at java.lang.invoke.DirectMethodHandle$EnsureInitialized.computeValue(DirectMethodHandle.java:318)
    at java.lang.invoke.DirectMethodHandle$EnsureInitialized.computeValue(DirectMethodHandle.java:314)
    at java.lang.ClassValue.getFromHashMap(ClassValue.java:227)
    at java.lang.ClassValue.getFromBackup(ClassValue.java:209)
    at java.lang.ClassValue.get(ClassValue.java:115)
    at java.lang.invoke.DirectMethodHandle.checkInitialized(DirectMethodHandle.java:338)
    at java.lang.invoke.DirectMethodHandle.ensureInitialized(DirectMethodHandle.java:328)
    at java.lang.invoke.DirectMethodHandle.ensureInitialized(DirectMethodHandle.java:360)
    at cue.<clinit>(SourceFile:36)
    at cyg.<init>(SourceFile:151)
    at cyc.au(SourceFile:440)
    at cyc.b(SourceFile:394)
    at org.prismlauncher.launcher.impl.StandardLauncher.launch(StandardLauncher.java:89)
    at org.prismlauncher.EntryPoint.listen(EntryPoint.java:128)
    at org.prismlauncher.EntryPoint.main(EntryPoint.java:71)
    at java.lang.reflect.Method.invoke(Method.java:486)
    at IKVM.Runtime.Accessors.Java.Lang.Reflect.MethodAccessor.InvokeInvoke(MethodAccessor.cs:35)

This indicated that the problematic code was in LWJGL, Minecraft 1.14.4 uses version 3.2.2, newer than 1.13.2, but older then the version I looked at previously.

LWJGL 3.2.2 uses unsafe to get the offset various fields of a DirectByteBuffer class, using an unusual method, as follows

Create a DirectByteBuffer instance with the aid of JNI with fields set to known values use unsafe.getLong() or unsafe.getInt() to scan for field until it finds the value it set previously.

For long fields, the process is done by reading every 8 bytes/offset, starting from an offset of 8. For integer fields, the process is done by reading every 4 bytes/offset, starting from an offset of 4.

For example, for the address field, it creates a DirectByteBuffer pointing to 0xDEADBEEF8BADF00DL, then loops though reading a long until it finds the matching value (in JVM, it reads offset 8, then 16 where it finds the value)

wasabii commented 1 year ago

Oh. That will never work.

I read that they removed that code.

https://github.com/LWJGL/lwjgl3/issues/632

TheLastRar commented 1 year ago

I was kindof expecting that response tbf.

That code got removed by 3.2.3 I think, which is unfortunately newer than what this version of minecraft (and a few later versions) shipped with.

Some launchers (such as the one I've been using) allow you to relatively easily change the version of LWJGL, but I don't know how easy that is to do with the official launcher.

wasabii commented 1 year ago

Yeah. This is pretty much an impossibility. Even if we switch away from returning GCHandles, the object layout in the CLR just can't be guarenteed to be the same as Hotspot. This really can't be fixed. And frankly it's not our bug to fix. =/

Closing for that reason. Sorry.