简体   繁体   English

合并两个方法的IL时dnlib.DotNet.Writer.ModuleWriterException

[英]dnlib.DotNet.Writer.ModuleWriterException when merging IL of two methods

I am trying to merge the IL of two methods: one method is from an already existing assembly ( MyAssembly.dll ) and one is in the same project as my dnlib code. 我正在尝试合并两种方法的IL:一种方法来自现有程序集( MyAssembly.dll ),另一种方法与dnlib代码位于同一项目中。 I'm trying to read both methods, merge their instructions and then write it to a new assembly. 我正在尝试阅读这两种方法,合并它们的说明,然后将其写入新程序集。 This works, however when I add if statements (probably also other statements) to the new method (so the one from my project) I get an exception when writing the instructions to the new assembly: 这是可行的,但是当我将if语句(可能还有其他语句)添加到新方法(因此是我项目中的那个)时,在向新程序集编写指令时会出现异常:

Unhandled Exception: dnlib.DotNet.Writer.ModuleWriterException: Found some other method's instruction or a removed instruction. You probably removed an instruction that is the target of a branch instruction or an instruction that's the first/last inst
ruction in an exception handler.
   at dnlib.DotNet.DummyLogger.Log(Object sender, LoggerEvent loggerEvent, String format, Object[] args)
   at dnlib.DotNet.Writer.ModuleWriterBase.dnlib.DotNet.ILogger.Log(Object sender, LoggerEvent loggerEvent, String format, Object[] args)
   at dnlib.DotNet.Writer.MetaData.Error(String message, Object[] args)
   at dnlib.DotNet.Writer.MetaData.dnlib.DotNet.Writer.IWriterError.Error(String message)
   at dnlib.DotNet.Writer.MethodBodyWriter.ErrorImpl(String message)
   at dnlib.DotNet.Writer.MethodBodyWriterBase.Error(String message)
   at dnlib.DotNet.Writer.MethodBodyWriterBase.GetOffset(Instruction instr)
   at dnlib.DotNet.Writer.MethodBodyWriterBase.WriteShortInlineBrTarget(BinaryWriter writer, Instruction instr)
   at dnlib.DotNet.Writer.MethodBodyWriterBase.WriteOperand(BinaryWriter writer, Instruction instr)
   at dnlib.DotNet.Writer.MethodBodyWriterBase.WriteInstruction(BinaryWriter writer, Instruction instr)
   at dnlib.DotNet.Writer.MethodBodyWriterBase.WriteInstructions(BinaryWriter writer)
   at dnlib.DotNet.Writer.MethodBodyWriter.WriteFatHeader()
   at dnlib.DotNet.Writer.MethodBodyWriter.Write()
   at dnlib.DotNet.Writer.MetaData.WriteMethodBodies()
   at dnlib.DotNet.Writer.MetaData.Create()
   at dnlib.DotNet.Writer.MetaData.CreateTables()
   at dnlib.DotNet.Writer.ModuleWriter.WriteImpl()
   at dnlib.DotNet.Writer.ModuleWriterBase.Write(Stream dest)
   at dnlib.DotNet.Writer.ModuleWriterBase.Write(String fileName)
   at dnlib.DotNet.ModuleDef.Write(String filename, ModuleWriterOptions options)
   at dnlib.DotNet.ModuleDef.Write(String filename)

It does not throw an exception when the original code (the code from MyAssembly.dll) contains an if statement. 当原始代码(来自MyAssembly.dll的代码)包含if语句时,它不会引发异常。

MyAssembly.dll's code: MyAssembly.dll的代码:

string str = GameManager.m_GameVersionString;
if (GameManager.m_Changelist != string.Empty)
{
    str = str + " (" + GameManager.m_Changelist + ")";
}
return str + " Release ";

MyAssembly.dll's IL: MyAssembly.dll的IL:

IL_0000: ldsfld System.String GameManager::m_GameVersionString
IL_0005: stloc.0
IL_0006: ldsfld System.String GameManager::m_Changelist
IL_000B: ldsfld System.String System.String::Empty
IL_0010: call System.Boolean System.String::op_Inequality(System.String,System.String)
IL_0015: brfalse IL_0030
IL_001A: ldloc.0
IL_001B: ldstr " ("
IL_0020: ldsfld System.String GameManager::m_Changelist
IL_0025: ldstr ")"
IL_002A: call System.String System.String::Concat(System.String,System.String,System.String,System.String)
IL_002F: stloc.0
IL_0030: ldloc.0
IL_0031: ldstr " Release "
IL_0036: call System.String System.String::Concat(System.String,System.String)
IL_003B: stloc.0
IL_003C: ldloc.0
IL_003D: ret

My project's code: 我项目的代码:

string check = "Hello".Trim();

if (check == "Hello")
{
    Console.WriteLine("Hello");
}

My project's IL 我项目的IL

IL_0000: ldstr "Hello"
IL_0005: call System.String System.String::Trim()
IL_000A: ldstr "Hello"
IL_000F: call System.Boolean System.String::op_Equality(System.String,System.String)
IL_0014: brfalse.s IL_0020
IL_0016: ldstr "Hello"
IL_001B: call System.Void System.Console::WriteLine(System.String)
IL_0020: ret

Merged IL 合并的IL

IL_0000: ldstr "Hello"
IL_0005: call System.String System.String::Trim()
IL_000A: ldstr "Hello"
IL_000F: call System.Boolean System.String::op_Equality(System.String,System.String)
IL_0014: brfalse.s IL_0020
IL_0016: ldstr "Hello"
IL_001B: call System.Void System.Console::WriteLine(System.String)
IL_0020: ldsfld System.String GameManager::m_GameVersionString
IL_0025: stloc.0
IL_0026: ldsfld System.String GameManager::m_Changelist
IL_002B: ldsfld System.String System.String::Empty
IL_0030: call System.Boolean System.String::op_Inequality(System.String,System.String)
IL_0035: brfalse.s IL_004D
IL_0037: ldloc.0
IL_0038: ldstr " ("
IL_003D: ldsfld System.String GameManager::m_Changelist
IL_0042: ldstr ")"
IL_0047: call System.String System.String::Concat(System.String,System.String,System.String,System.String)
IL_004C: stloc.0
IL_004D: ldloc.0
IL_004E: ldstr " Release "
IL_0053: call System.String System.String::Concat(System.String,System.String)
IL_0058: stloc.0
IL_0059: ldloc.0
IL_005A: ret

The merged IL looks identical to the IL I generated with dnSpy: https://owo.whats-th.is/a0783e.png 合并的IL看起来与我用dnSpy生成的IL相同: https ://owo.whats-th.is/a0783e.png

However, an error is thrown when writing the merged IL to a file. 但是,将合并的IL写入文件时会引发错误。 My code: 我的代码:

//Patch method

...

var patchModule = ModuleDefMD.Load(patchClass.Module);
var patchMethod = FindMethod(patchModule, patchClass, method.Name);

var assemblyModule = ModuleDefMD.Load(assemblyPath);
var assemblyMethod = FindMethod(assemblyModule, attribute.Type, attribute.MethodName,  attribute.Parameters);

assemblyMethod.Body = MergeMethods(patchMethod, assemblyMethod, attribute.CodeMode, attribute.CustomPos);

assemblyModule.Write(finalPath); //Error is thrown here

...

private static CilBody MergeMethods(MethodDef original, MethodDef target, AmityPatch.Mode mode, int mergeLoc = 0)
{

    original.FreeMethodBody();
    target.FreeMethodBody();

    var originalBody = original.Body;
    var targetBody = target.Body;

    var targetModule = target.Module;

    var originalInstructions = originalBody.Instructions;
    var targetInstructions = targetBody.Instructions;

    Console.WriteLine("=original method=");
    Console.WriteLine();
    foreach (var originalInstruction in originalInstructions)
    {
        Console.WriteLine(originalInstruction);
    }
    Console.WriteLine();
    Console.WriteLine("=target method=");
    Console.WriteLine();
    foreach (var targetInstruction in targetInstructions)
    {
        Console.WriteLine(targetInstruction);
    }

    RemoveReturn(ref originalInstructions, true);
    if (mode == AmityPatch.Mode.Postfix) mergeLoc = targetInstructions.Count - 1;

    var localOffset = targetBody.Variables.Count;

    for (var i = 0; i < originalBody.Variables.Count; i++)
    {
        targetBody.Variables.Add(
            new Local(originalBody.Variables[i].Type, originalBody.Variables[i].Name));
    }

    for (var i = originalInstructions.Count - 1; i >= 0; i--)
    {
        var o = originalInstructions[i];
        var c = new Instruction(o.OpCode, o.Operand);

        switch (o.Operand)
        {
            case IType type:
                c.Operand = targetModule.Import(type);
                break;
            case IMethod method:
                c.Operand = targetModule.Import(method);
                break;
            case IField field:
                c.Operand = targetModule.Import(field);
                break;
        }

        if (IsStloc(o.OpCode))
        {
            c.OpCode = OpCodes.Stloc;
            c.Operand = targetBody.Variables[StlocIndex(o) + localOffset];
        }
        else if (IsLdloc(o.OpCode))
        {
            c.OpCode = OpCodes.Ldloc;
            c.Operand = targetBody.Variables[LdlocIndex(o) + localOffset];
        }
        else if (IsLdloca(o.OpCode))
        {
            c.OpCode = OpCodes.Ldloca;
            c.Operand = targetBody.Variables[LdlocIndex(o) + localOffset];
        }

        targetInstructions.Insert(mergeLoc, c);
    }

    targetBody.OptimizeMacros();
    targetBody.OptimizeBranches();

    Console.WriteLine();
    Console.WriteLine("=merged method=");
    Console.WriteLine();

    foreach (var instruction in targetBody.Instructions)
    {
        Console.WriteLine(instruction);
    }

    Console.WriteLine(targetBody.Variables.Count);

    return targetBody;
}

I don't know what causes this issue. 我不知道是什么原因导致了这个问题。 All labels in the IL seem to be correct. IL中的所有标签似乎都是正确的。 When removing the if statement dnlib writes the IL to the assembly just fine. 删除if语句时,dnlib会将IL写入程序集。

如果要合并的方法来自其他程序集,则可能需要创建/转换令牌。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM