使用Br_S OpCode使用Reflection.Emit.Label指向下一个指令

本文关键字:使用 下一个 指令 Label Reflection Br OpCode Emit | 更新日期: 2023-09-27 18:03:10

我正在尝试解析IL以发出一个方法。我已经得到了字符串[]中方法的IL代码,其中每个字符串都是IL指令。我正在循环此数组并使用ILGenerator添加OpCodes:

        foreach (string ins in instructions) //string representations of IL          
        {
            string opCode = ins.Split(':').ElementAt(1);
            // other conditions omitted
            if (opCode.Contains("br.s"))
            {
                Label targetInstruction = ilGenerator.DefineLabel();
                ilGenerator.MarkLabel(targetInstruction);
                ilGenerator.Emit(OpCodes.Br_S, targetInstruction); 
            }

这是我需要复制的IL:

Source IL:
IL_0000: nop
IL_0001: ldstr "Hello, World!"
IL_0006: stloc.0
IL_0007: br.s IL_0009
IL_0009: ldloc.0
IL_000a: ret

下面是我得到的输出:

Target IL:
IL_0000: nop
IL_0001: ldstr "Hello, World!"
IL_0006: stloc.0
IL_0007: br.s IL_0007   // this is wrong -- needs to point to IL_0009
IL_0009: ldloc.0
IL_000a: ret

可以看到br。S调用指向自身,这当然会导致无限循环。我如何让它指向源代码中的以下指令?这与使用Reflection.Emit.Label有关,但我不确定它是如何工作的。

EDIT顺便说一下,上面看到的IL是用于这个简单方法的,

    public string HelloWorld()
    {
            return "Hello, World!";
    }

使用Br_S OpCode使用Reflection.Emit.Label指向下一个指令

代码:

ilGenerator.MarkLabel(targetInstruction);
ilGenerator.Emit(OpCodes.Br_S, targetInstruction); 

清楚地表示"在这里标记标签",然后在标记标签的地方添加指令

如果这不是你想要的,你为什么要这样做?

MarkLabel标记当前位置,这意味着您输出的下一个指令的位置,作为标签的目标。

在这种情况下,要得到"你想要的",只需将这两行反向,在标记标签之前输出分支指令。

我把"what you want"放在引号里,因为我不明白那个分支指令的意思。机器会很高兴地自己"移动"到下一条指令,不需要添加"分支到下一条指令"指令来实现这一点。

您需要在发出想要将跳转到的操作码之前立即放置ilGenerator.MarkLabel()调用。你把它放在分支之前,这意味着它会自己分支,有效地创造一个无限循环。但正如Lasse所说,如果你正确地发射IL,它将是无操作的。

有趣的是,整个方法可以很容易地:
ldstr "Hello, World!"
ret

无论哪个编译器发出原始代码,都需要将其作者LARTed。

调用ILGenerator上的MarkLabel()方法可用于标记分支点,然后使用Emit(OpCodes.Br_S, [label])来分支到该点。

我假设无论您使用什么API来监视Hello World方法的IL指令,都是在Debug模式下完成的,因为添加的nop和分支指令被添加以帮助确保调试器覆盖每一步。

在DynamicMethod中,不需要附加调试器,并且根据平台的不同,在发布模式下使用额外的指令运行它可能会导致InvalidProgramException。

"Hello World"方法只需要2个指令(而且很直观)

Ldstr "Hello, World!"
Ret