Skip to content

fix: harden hook and signature resolution for optimized IL2CPP builds#262

Open
hazre wants to merge 1 commit into
BepInEx:masterfrom
ResoniteModding:fix/il2cppinterop-hook-resolution
Open

fix: harden hook and signature resolution for optimized IL2CPP builds#262
hazre wants to merge 1 commit into
BepInEx:masterfrom
ResoniteModding:fix/il2cppinterop-hook-resolution

Conversation

@hazre
Copy link
Copy Markdown

@hazre hazre commented May 5, 2026

While trying to get Resonite's upcoming IL2CPP renderer builds (prerelease branch on steam) working, it kept crashing with errors like this:

[Message: Preloader] BepInEx 6.0.0-be.755 - Renderite.Renderer (28/4/2026 6:19:56 PM)
[Message: Preloader] Built from commit 3fab71a1914132a1ce3a545caf3192da603f2258
[Info   :   BepInEx] System platform: Windows 10 64-bit
[Info   :   BepInEx] Process bitness: 64-bit (x64)
[Info   :   BepInEx] Running under Unity 2019.4.19f1
[Info   :   BepInEx] Runtime version: 6.0.7
[Info   :   BepInEx] Runtime information: .NET 6.0.7
[Info   : Preloader] 0 patcher plugins loaded
[Info   : Preloader] 0 assemblies discovered
[Message:AssemblyPatcher] Executing 0 patch(es)
[Message:   BepInEx] Chainloader initialized
[Fatal  :   BepInEx] Unable to execute IL2CPP chainloader
[Error  :   BepInEx] System.InvalidOperationException: Sequence contains no elements
   at System.Linq.ThrowHelper.ThrowNoElementsException()
   at Il2CppInterop.Runtime.Injection.Hooks.MetadataCache_GetTypeInfoFromTypeDefinitionIndex_Hook.FindGetTypeInfoFromTypeDefinitionIndex(Boolean forceICallMethod) in /home/runner/work/Il2CppInterop/Il2CppInterop/Il2CppInterop.Runtime/Injection/Hooks/MetadataCache_GetTypeInfoFromTypeDefinitionIndex_Hook.cs:line 31
   at Il2CppInterop.Runtime.Injection.Hooks.MetadataCache_GetTypeInfoFromTypeDefinitionIndex_Hook.FindGetTypeInfoFromTypeDefinitionIndex(Boolean forceICallMethod) in /home/runner/work/Il2CppInterop/Il2CppInterop/Il2CppInterop.Runtime/Injection/Hooks/MetadataCache_GetTypeInfoFromTypeDefinitionIndex_Hook.cs:line 79
   at Il2CppInterop.Runtime.Injection.Hooks.MetadataCache_GetTypeInfoFromTypeDefinitionIndex_Hook.FindTargetMethod() in /home/runner/work/Il2CppInterop/Il2CppInterop/Il2CppInterop.Runtime/Injection/Hooks/MetadataCache_GetTypeInfoFromTypeDefinitionIndex_Hook.cs:line 119
   at Il2CppInterop.Runtime.Injection.Hook`1.ApplyHook() in /home/runner/work/Il2CppInterop/Il2CppInterop/Il2CppInterop.Runtime/Injection/Hook.cs:line 30
   at Il2CppInterop.Runtime.Injection.InjectorHelpers.Setup() in /home/runner/work/Il2CppInterop/Il2CppInterop/Il2CppInterop.Runtime/Injection/InjectorHelpers.cs:line 78
   at Il2CppInterop.Runtime.Injection.ClassInjector.RegisterTypeInIl2Cpp(Type type, RegisterTypeOptions options) in /home/runner/work/Il2CppInterop/Il2CppInterop/Il2CppInterop.Runtime/Injection/ClassInjector.cs:line 196
   at Il2CppInterop.Runtime.DelegateSupport.ConvertDelegate[TIl2Cpp](Delegate delegate) in /home/runner/work/Il2CppInterop/Il2CppInterop/Il2CppInterop.Runtime/DelegateSupport.cs:line 244
   at UnityEngine.Application.LogCallback.op_Implicit(Action`3 )
   at BepInEx.Unity.IL2CPP.Logging.IL2CPPUnityLogSource..ctor() in /home/runner/work/BepInEx/BepInEx/Runtimes/Unity/BepInEx.Unity.IL2CPP/Logging/IL2CPPUnityLogSource.cs:line 11
   at BepInEx.Unity.IL2CPP.IL2CPPChainloader.OnInvokeMethod(IntPtr method, IntPtr obj, IntPtr parameters, IntPtr exc) in /home/runner/work/BepInEx/BepInEx/Runtimes/Unity/BepInEx.Unity.IL2CPP/IL2CPPChainloader.cs:line 88
[Fatal  :   BepInEx] Unable to execute IL2CPP chainloader
[Error  :   BepInEx] System.InvalidOperationException: Sequence contains no elements
   at System.Linq.ThrowHelper.ThrowNoElementsException()
   at Il2CppInterop.Runtime.Injection.Hooks.MetadataCache_GetTypeInfoFromTypeDefinitionIndex_Hook.FindGetTypeInfoFromTypeDefinitionIndex(Boolean forceICallMethod) in /home/runner/work/Il2CppInterop/Il2CppInterop/Il2CppInterop.Runtime/Injection/Hooks/MetadataCache_GetTypeInfoFromTypeDefinitionIndex_Hook.cs:line 31
   at Il2CppInterop.Runtime.Injection.Hooks.MetadataCache_GetTypeInfoFromTypeDefinitionIndex_Hook.FindGetTypeInfoFromTypeDefinitionIndex(Boolean forceICallMethod) in /home/runner/work/Il2CppInterop/Il2CppInterop/Il2CppInterop.Runtime/Injection/Hooks/MetadataCache_GetTypeInfoFromTypeDefinitionIndex_Hook.cs:line 79
   at Il2CppInterop.Runtime.Injection.Hooks.MetadataCache_GetTypeInfoFromTypeDefinitionIndex_Hook.FindTargetMethod() in /home/runner/work/Il2CppInterop/Il2CppInterop/Il2CppInterop.Runtime/Injection/Hooks/MetadataCache_GetTypeInfoFromTypeDefinitionIndex_Hook.cs:line 119
   at Il2CppInterop.Runtime.Injection.Hook`1.ApplyHook() in /home/runner/work/Il2CppInterop/Il2CppInterop/Il2CppInterop.Runtime/Injection/Hook.cs:line 30
   at Il2CppInterop.Runtime.Injection.InjectorHelpers.Setup() in /home/runner/work/Il2CppInterop/Il2CppInterop/Il2CppInterop.Runtime/Injection/InjectorHelpers.cs:line 78
   at Il2CppInterop.Runtime.Injection.ClassInjector.RegisterTypeInIl2Cpp(Type type, RegisterTypeOptions options) in /home/runner/work/Il2CppInterop/Il2CppInterop/Il2CppInterop.Runtime/Injection/ClassInjector.cs:line 196
   at Il2CppInterop.Runtime.DelegateSupport.ConvertDelegate[TIl2Cpp](Delegate delegate) in /home/runner/work/Il2CppInterop/Il2CppInterop/Il2CppInterop.Runtime/DelegateSupport.cs:line 244
   at UnityEngine.Application.LogCallback.op_Implicit(Action`3 )
   at BepInEx.Unity.IL2CPP.Logging.IL2CPPUnityLogSource..ctor() in /home/runner/work/BepInEx/BepInEx/Runtimes/Unity/BepInEx.Unity.IL2CPP/Logging/IL2CPPUnityLogSource.cs:line 11
   at BepInEx.Unity.IL2CPP.IL2CPPChainloader.OnInvokeMethod(IntPtr method, IntPtr obj, IntPtr parameters, IntPtr exc) in /home/runner/work/BepInEx/BepInEx/Runtimes/Unity/BepInEx.Unity.IL2CPP/IL2CPPChainloader.cs:line 106

I've looked around and it seems to be a commonly reported issue, that is supposedly been fixed? #119 #183 BepInEx/BepInEx#820 BepInEx/BepInEx#1109

After investigating it it seems like:

  1. BepInEx couldn't find a function it was looking for because the compiler had inlined it.
    Fix: if it looks inlined, just use the parent function instead.
  2. A byte pattern scan got a false positive and returned a completely garbage memory address, crashing the runtime when it tried to use it.
    Fix: make the bounds check more stricter, so that the address actually lives inside the DLL before trusting it.

I've not tested this with other games, if there is a small list of games (ideally free or smth) I can test against to make sure it works everywhere and it doesn't break already working stuff, I can do that.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant