1

我正在尝试解析 IL 以发出一种方法。我在 string[] 中获得了一个方法的 IL 代码,其中每个字符串都是一条 IL 指令。我正在循环这个数组并使用 ILGenerator 添加操作码:

        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 有关,但我不确定它是如何工作的。

编辑顺便说一下,上面看到的 IL 是针对这种简单方法的,

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

3 回答 3

5

这段代码:

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

清楚地说“在此处标记标签”,然后在标记标签的位置添加说明。

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

MarkLabel 将当前位置标记为标签的目标,即你输出的下一条指令的位置。

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

我把“你想要的”放在引号里,因为我不明白那个分支指令的意义。机器会很高兴地自行“移动”到下一条指令,无需为此添加“分支到下一条指令”指令。

于 2011-07-20T18:49:04.073 回答
4

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

有趣的是,整个方法很容易是:

ldstr "Hello, World!"
ret

无论编译器发出原始代码,都需要对其作者进行 LARTed。

于 2011-07-20T18:47:06.510 回答
0

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

我假设你用来监视 Hello World 方法的 IL 指令的任何 API 都是在调试模式下完成的,因为添加了 nop 和分支指令以帮助确保调试器覆盖每一步。

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

“Hello World”方法只需要 2 条指令(而且非常直观)

Ldstr "Hello, World!"
Ret
于 2016-04-21T02:22:44.567 回答