使用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!";
}
代码:
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