Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reorg: CallSite emitting does not work as intended #171

Open
kohanis opened this issue Mar 10, 2024 · 11 comments
Open

Reorg: CallSite emitting does not work as intended #171

kohanis opened this issue Mar 10, 2024 · 11 comments
Labels

Comments

@kohanis
Copy link

kohanis commented Mar 10, 2024

Description

Something goes wrong when emitting a CallSite referencing double (and possibly more types) on CoreCLR/frameworks. And on mono it crashes during emitting

Example
using System;
using Mono.Cecil;
using Mono.Cecil.Cil;
using MonoMod.Cil;
using MonoMod.Utils;

namespace MonoModTest;

public static class Program
{
    public static double Fn1() => default;
        
    public static void Main(string[] _)
    {
        var original = typeof(Program).GetMethod("Fn1");
            
        var originalParameters = original.GetParameters();
        var offset = original.IsStatic ? 0 : 1;
        var parameterTypes = new Type[originalParameters.Length + offset];
        if (!original.IsStatic) 
            parameterTypes[0] = original.GetThisParamType();
        for (var i = 0; i < originalParameters.Length; i++) 
            parameterTypes[i + offset] = originalParameters[i].ParameterType;

        var dmd = new DynamicMethodDefinition(
            "Proxy",
            original.ReturnType,
            parameterTypes
        );
            
        var il = new ILContext(dmd.Definition);
        var c = new ILCursor(il);
            
        var originalRef = il.Module.ImportReference(original);

        var callsite = new CallSite(originalRef.ReturnType)
        {
            HasThis = originalRef.HasThis,
            ExplicitThis = originalRef.ExplicitThis,
            CallingConvention = originalRef.CallingConvention
        };
        foreach (var param in originalRef.Parameters)
            callsite.Parameters.Add(param);
        
        for (var i = 0; i < parameterTypes.Length; i++)
            c.EmitLdarg(i);
        c.EmitLdftn(original);
        c.Emit(OpCodes.Calli, callsite);
        c.EmitRet();

        var method = dmd.Generate();
        method.Invoke(null, []);
    }
}
CoreCLR/frameworks
Unhandled Exception: System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.TypeLoadException: Could not load type 'System.Runtime.CompilerServices.IsReadOnlyAttribute' from assembly 'MonoMod.Utils, Version=25.0.4.0, Culture=neutral, PublicKeyToken=null' due to value type mismatch.
   at Proxy()
   --- End of inner exception stack trace ---
Mono
Unhandled Exception:
System.NotImplementedException: The method or operation is not implemented.
  at MonoMod.Utils._DMDEmit.<_EmitCallSite>g__GetTokenForSig|17_3 (System.Byte[] v, MonoMod.Utils._DMDEmit+<>c__DisplayClass17_0& ) [0x00010] in <3a2a1307c411416a9ec924fe3fc8ad8f>:0 
  at MonoMod.Utils._DMDEmit._EmitCallSite (System.Reflection.Emit.DynamicMethod dm, System.Reflection.Emit.ILGenerator il, System.Reflection.Emit.OpCode opcode, Mono.Cecil.CallSite csite) [0x0024f] in <3a2a1307c411416a9ec924fe3fc8ad8f>:0 
  at MonoMod.Utils._DMDEmit.Generate (MonoMod.Utils.DynamicMethodDefinition dmd, System.Reflection.MethodBase _mb, System.Reflection.Emit.ILGenerator il) [0x006d1] in <3a2a1307c411416a9ec924fe3fc8ad8f>:0 
  at MonoMod.Utils.DMDEmitDynamicMethodGenerator.GenerateCore (MonoMod.Utils.DynamicMethodDefinition dmd, System.Object context) [0x00342] in <3a2a1307c411416a9ec924fe3fc8ad8f>:0 
  at MonoMod.Utils.DMDGenerator`1[TSelf].Generate (MonoMod.Utils.DynamicMethodDefinition dmd, System.Object context) [0x00013] in <3a2a1307c411416a9ec924fe3fc8ad8f>:0 
  at MonoMod.Utils.DynamicMethodDefinition.Generate (System.Object context) [0x00149] in <3a2a1307c411416a9ec924fe3fc8ad8f>:0 
  at MonoMod.Utils.DynamicMethodDefinition.Generate () [0x00000] in <3a2a1307c411416a9ec924fe3fc8ad8f>:0 
@kohanis kohanis added the bug label Mar 10, 2024
@nike4613
Copy link
Contributor

Can you dump the dmd to disk and send it?

@kohanis
Copy link
Author

kohanis commented Mar 10, 2024

The thing is, there's no problem with il, as far as I can tell. The problem is that runtime tries to use attributes like IsReadOnlyAttribute and NullableAttribute from MonoMod.Utils when calli got invoked. I don't have enough understanding of how runtime works to understand the cause.
net48.zip
net7.0.zip
Also, apparently net8.0 is also crashing:

Unhandled exception. System.NullReferenceException: Object reference not set to an instance of an object.
   at MonoMod.Utils._DMDEmit._EmitCallSite(DynamicMethod dm, ILGenerator il, OpCode opcode, CallSite csite)
   at MonoMod.Utils._DMDEmit.Generate(DynamicMethodDefinition dmd, MethodBase _mb, ILGenerator il)
   at MonoMod.Utils.DMDEmitDynamicMethodGenerator.GenerateCore(DynamicMethodDefinition dmd, Object context)
   at MonoMod.Utils.DMDGenerator`1.Generate(DynamicMethodDefinition dmd, Object context)
   at MonoMod.Utils.DynamicMethodDefinition.Generate(Object context)
   at MonoMod.Utils.DynamicMethodDefinition.Generate()
   at MonoModTest.Program.Main(String[] _) in F:\projects\dotnet\MonoModTest\Program.cs:line 52

EDIT:
Basically, that's 3 separate issues:
NullReferenceException in _EmitCallSite in net8.0
NotImplementedException in _EmitCallSite in mono
Something with strange runtime type resolution in result from DynamicMethodDefinition

@nike4613
Copy link
Contributor

It looks like .NET 8 is failing because of changes in ILGenerator that we never caught. On older runtimes, it looks like there might be an issue with the ldftn? I don't think it's actually the calli that's causing the problem, but its quite hard to actually tell...

@nike4613
Copy link
Contributor

8761d94 seems to fix Framework and Core, but not Mono. Doing some investigation, it seems like Mono just... doesn't implement DynamicILInfo.GetTokenFor(byte[]). We'll probably need to do some significant work to bypass this. (I also think this isn't new in reorg either; this comes from old MMC code mostly.)

@kohanis
Copy link
Author

kohanis commented Mar 11, 2024

Well, that partially solves the problem. Because based on the changes, original problem has simply shifted. Still happens with reference to IntPtr/UIntPtr that contain members with EmbeddedAttribute

@nike4613
Copy link
Contributor

[U]IntPtr containing members? What do you mean?

What's the repro for this new case?

@kohanis
Copy link
Author

kohanis commented Mar 11, 2024

System.[U]IntPtr does

public static double Fn1() => default;

to

public static IntPtr Fn1() => default;

@nike4613
Copy link
Contributor

I see. My tests show only IntPtr failing, and it's because I messed up the CorElementTypes list a bit. I missed one in the offset of IntPtr

@kohanis
Copy link
Author

kohanis commented Mar 11, 2024

Oh, indeed, my bad. 0x1A is also missing between U and FNPTR.

nike4613 added a commit that referenced this issue Mar 11, 2024
@kohanis
Copy link
Author

kohanis commented Mar 11, 2024

Related. delegate*<> is IntPtr before net8.0, but it's own type(s) on net8.0. And MonoFNPtrFakeClass on mono... And it's actually FNPTR, not I.
This is also a separate issue altogether, as MonoMod.Utils.ReflectionHelper.ResolveReflection does not work in that case in net8.0 because TypeReference.FullName is an empty string.
And in mono cecil simply cannot import MonoFNPtrFakeClass because it is not a class at all

@nike4613
Copy link
Contributor

That's a different issue altogether I think. Pre-.NET 8, there are actually no reflection APIs to work with function pointers, and even on .NET 8, they're rather strange.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants