MonoMod.Common icon indicating copy to clipboard operation
MonoMod.Common copied to clipboard

Failed to create proxy when inside an AssemblyLoadContext in net6

Open cc004 opened this issue 3 years ago • 2 comments

stacktrace

System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation.
 ---> System.IO.FileLoadException: Could not load file or assembly 'MonoMod.Common, Version=22.3.5.1, Culture=neutral, PublicKeyToken=null'. Operation is not supported. (0x80131515)
File name: 'MonoMod.Common, Version=22.3.5.1, Culture=neutral, PublicKeyToken=null'
 ---> System.NotSupportedException: Resolving to a collectible assembly is not supported.
   at System.Reflection.RuntimeAssembly.GetType(QCallAssembly assembly, String name, Boolean throwOnError, Boolean ignoreCase, ObjectHandleOnStack type, ObjectHandleOnStack keepAlive, ObjectHandleOnStack assemblyLoadContext)
   at System.Reflection.RuntimeAssembly.GetType(String name, Boolean throwOnError, Boolean ignoreCase)
   at System.Reflection.Assembly.GetType(String name)
   at MonoMod.Utils.Cil.ILGeneratorShim.ILGeneratorBuilder.GenerateProxy()
   at MonoMod.Utils.Cil.ILGeneratorShim.GetProxy()
   at HarmonyLib.MethodPatcher..ctor(MethodBase original, MethodBase source, List`1 prefixes, List`1 postfixes, List`1 transpilers, List`1 finalizers, Boolean debug)
   at HarmonyLib.PatchFunctions.UpdateWrapper(MethodBase original, PatchInfo patchInfo)
   at HarmonyLib.PatchProcessor.Patch()
   at Localizer.Utils.Patch(Harmony instance, String class, String method, Boolean exactMatch, HarmonyMethod prefix, HarmonyMethod postfix, HarmonyMethod transpiler) in Localizer\Helpers\Utils.cs:line 426
   at Localizer.Localizer..ctor() in Localizer.cs:line 59
   at System.RuntimeType.CreateInstanceDefaultCtor(Boolean publicOnly, Boolean wrapExceptions)
   --- End of inner exception stack trace ---
   at System.RuntimeType.CreateInstanceDefaultCtor(Boolean publicOnly, Boolean wrapExceptions)
   at Terraria.ModLoader.Core.AssemblyManager.Instantiate(ModLoadContext mod) in tModLoader\Terraria\ModLoader\Core\AssemblyManager.cs:line 188
   at Terraria.ModLoader.Core.AssemblyManager.<>c__DisplayClass11_0.<InstantiateMods>b__1(ModLoadContext mod) in tModLoader\Terraria\ModLoader\Core\AssemblyManager.cs:line 260
   at System.Linq.Enumerable.SelectListIterator`2.ToList()
   at Terraria.ModLoader.Core.AssemblyManager.InstantiateMods(List`1 modsToLoad, CancellationToken token) in tModLoader\Terraria\ModLoader\Core\AssemblyManager.cs:line 258
   at Terraria.ModLoader.Core.ModOrganizer.LoadMods(CancellationToken token) in tModLoader\Terraria\ModLoader\Core\ModOrganizer.cs:line 243
   at Terraria.ModLoader.ModLoader.Load(CancellationToken token) in tModLoader\Terraria\ModLoader\ModLoader.cs:line 116

cc004 avatar Jun 15 '22 08:06 cc004

This happens when assembly MonoMod.Utils is loaded in a collectible AssemblyLoadContext which prevents the non-collectible MonoMod.Utils.Cil.ILGeneratorProxy to be loaded.

Potential fix is to load MonoMod.Utils.Cil.ILGeneratorProxy in the same AssemblyLoadContext as MonoMod.Utils. This line should look like this (simplified, in reality we will need to use reflection on older target frameworks):

var utilsAssembly = Assembly.GetExecutingAssembly();
var context = AssemblyLoadContext.GetLoadContext(utilsAssembly);
asm = context.LoadFromStream(copy);

Without this fix it is a bug which got patched in the .NET Runtime 7 and now throws the exception OP posted.

andriivitiv avatar Mar 10 '23 22:03 andriivitiv

I'm honestly not sure supporting MonoMod being loaded in a collectible ALC is a good idea. In fact, I'm fairly certain that it is a terrible idea. MonoMod relies on being able to root objects in statics, and must be able to keep detours alive and correct. If the runtime were to unload it, that could (in fact, would) leave detours in invalid states and be liable to inducing runtime crashes.

If we did want to support it, the only safe support we can have is allocating a GC handle to some MonoMod object as long as there are references in the DynamicReferenceManager. This should effectively root all relevant MonoMod types, preventing them from being collected. This does also defeat the purpose of unloadable ALCs though.


I do think it's a good idea to load our dynamically generated assemblies in the same ALC that created them though. That would require some more work than just the proposed change however.

nike4613 avatar Mar 11 '23 04:03 nike4613