簡體   English   中英

合並兩個方法的IL時dnlib.DotNet.Writer.ModuleWriterException

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

我正在嘗試合並兩種方法的IL:一種方法來自現有程序集( MyAssembly.dll ),另一種方法與dnlib代碼位於同一項目中。 我正在嘗試閱讀這兩種方法,合並它們的說明,然后將其寫入新程序集。 這是可行的,但是當我將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)

當原始代碼(來自MyAssembly.dll的代碼)包含if語句時,它不會引發異常。

MyAssembly.dll的代碼:

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

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

我項目的代碼:

string check = "Hello".Trim();

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

我項目的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

合並的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

合並的IL看起來與我用dnSpy生成的IL相同: https ://owo.whats-th.is/a0783e.png

但是,將合並的IL寫入文件時會引發錯誤。 我的代碼:

//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;
}

我不知道是什么原因導致了這個問題。 IL中的所有標簽似乎都是正確的。 刪除if語句時,dnlib會將IL寫入程序集。

如果要合並的方法來自其他程序集,則可能需要創建/轉換令牌。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM