C#IL代码修改 - 保持堆栈完整

And*_*ike 6 c# cil static-analysis metaprogramming opcode

这个问题是关于自定义C#IL代码的静态堆栈分析以及如何设计操作码来满足编译器.

我有代码通过附加我自己的代码来修改现有的C#方法.为了避免原始方法在执行代码之前返回,它将所有RET操作码替换为BR endlabel,并将该标签添加到原始代码的末尾.然后我在那里添加更多代码,最后是RET.

这一切都很好,但某些方法失败了.这是一个简单的例子:

public static string SomeMethod(int val)
{
    switch (val)
    {
        case 0:
            return "string1".convert();
        case 1:
            return "string2".convert();
        case 2:
            return "string3".convert();
        // ...
    }
    return "";
}
Run Code Online (Sandbox Code Playgroud)

这个IL代码表示:

.method public hidebysig static string SomeMethod(int32 val) cil managed
{
    .maxstack 1
    .locals val ([0] int32 num)
    L_0000: ldarg.0 
    L_0001: stloc.0 
    L_0002: ldloc.0 
    L_0003: switch (L_002e, L_004f, L_0044, ...)
    L_002c: br.s L_0091
    L_002e: ldstr "string1"
    L_0033: call string Foo::convert(string)
    L_0038: ret 
    L_0039: ldstr "string2"
    L_003e: call string Foo::convert(string)
    L_0043: ret 
    L_0044: ldstr "string3"
    L_0049: call string Foo::convert(string)
    L_004e: ret 
    ... 
    L_0091: ldstr ""
    L_0096: ret 
}
Run Code Online (Sandbox Code Playgroud)

我的程序修改后,代码如下所示:

.method public hidebysig static string SomeMethod(int32 val) cil managed
{
    .maxstack 1
    .locals val ([0] int32 num)
    L_0000: ldarg.0 
    L_0001: stloc.0 
    L_0002: ldloc.0 
    L_0003: switch (L_002e, L_004f, L_0044, ...)
    L_002c: br.s L_0091
    L_002e: ldstr "string1"
    L_0033: call string Foo::convert(string)
    L_0038: br L_009b // was ret 
    L_0039: ldstr "string2"
    L_003e: call string Foo::convert(string)
    L_0043: br L_009b // was ret 
    L_0044: ldstr "string3"
    L_0049: call string Foo::convert(string)
    L_004e: br L_009b // was ret 
    ... 
    L_0091: ldstr ""
    L_0096: br L_009b // was ret
    L_009b: my code here
    ...
    L_0200: ret
}
Run Code Online (Sandbox Code Playgroud)

我得到一个编译错误:

无法执行post-long-event操作.异常:System.TypeInitializationException:FooBar的类型初始化程序抛出异常---> System.InvalidProgramException:(包装器动态方法)中的无效IL代码Foo:SomeMethod(int):IL_0000:ldnull

有没有简单的方法以通用方式替换RET并保持静态分析器满意?

And*_*ike 3

问题是,所有短跳转指令可能会变得太远,因为插入BR而不是RET会增加操作码大小。

我通过将所有以“_S”结尾的操作码替换为相应的长跳转版本来解决这个问题。有关此内容的更多详细信息,请查看对我的项目的此提交:Fixed Illegal Short Jumps

  • @IlianPinzon,在 stackoverflow 允许我接受我自己的答案之前,我正在等待 24 小时的冷静期。 (2认同)