C#语句能否生成未连接的MSIL

时间:2019-04-23 14:33:42

标签: c# compiler-construction cil

问题与C# language specificationCIL language specification以及Microsoft和Mono的C#编译器行为有关。

我正在构建一些可在CIL上运行的代码分析工具(无论如何)。

考虑一些代码示例,我注意到代码语句(try / catch,ifelse,ifthen,loop,...)生成了MSIL的连接块。

但是我想确定我不能编写产生非连接MSIL的C#代码构造。更具体地说,我是否可以编写任何C#statement来翻译为(类似):

IL_0000: 
IL_0001: 
IL_0002: 

// hole

IL_001a: 
IL_001b:

我已经使用goto和嵌套循环尝试了一些奇怪的东西,但是也许我不像某些用户那样生气。

2 个答案:

答案 0 :(得分:13)

当然,这很容易做到。像这样:

static void M(bool x)
{
    if (x)
        return;
    else
        M(x);
    return;
}

如果在调试模式下进行编译,则会得到

    IL_0000: nop
    IL_0001: ldarg.0
    IL_0002: stloc.0
    IL_0003: ldloc.0
    IL_0004: brfalse.s IL_0008
    IL_0006: br.s IL_0011
    IL_0008: ldarg.0
    IL_0009: call void A::M(bool)
    IL_000e: nop
    IL_000f: br.s IL_0011
    IL_0011: ret

if语句从00010009,而if的结果是转到0011;两个return语句是相同的代码,因此在nop的主体和结果之间存在一个包含if的“空”和无条件分支。

更一般而言,对于C#编译器生成的IL的布局,您永远不应假设任何事情。编译器不保证所产生的IL将是合法的,并且在安全的情况下可以验证的。


您说您正在编写一些代码分析工具;作为C#分析器重要部分的作者,以及在Coverity从事第三方分析工具工作的人,一个忠告是:对于您通常想回答的有关C#程序的大多数问题,罗斯林产生的解析树就是您要分析的实体,而不是IL。解析树是一个具体的语法树。它与源代码中的每个字符都是一对一的。将优化的IL映射回原始源代码可能非常困难,并且在IL分析中很容易产生误报。

换一种说法:从源到IL既保留了语义,又丢失了信息;您通常希望分析其中包含最多信息的工件。

如果出于某种原因必须在IL级别上运行分析仪,那么您的首要任务应该是找到基本块的边界,尤其是在分析可达性属性时。

“基本块”是IL的连续块,其中块的端点不“继续”执行以下指令-例如,它是分支,返回或抛出-并且存在除了第一条指令外,没有分支跳转到任何地方。

然后,您可以为每种方法形成基本方框图,指示哪些可以将控制权转移到其他哪些方框。这“提高了您的分析水平”;现在,您正在分析基本块图的作用,而不是分析IL指令序列的作用。

如果您对正在执行的分析类型有更多的看法,我可以进一步建议。

答案 1 :(得分:1)

理论上是(这是根据我的经验得出的)。您的分析工具不会直接处理c#,而只能在IL代码上使用。 IL可以由任何人生成,不仅可以由Visual Studio生成,还可以由其他语言编译器(如Visual Basic,Python)生成。净...和混淆器!混淆器是真正的罪魁祸首:虽然其他编译器尝试遵守规范,但混淆器会尽力利用这些规范和目标运行时。

混淆的代码可能违反某些常识模式。考虑这种情况:某些智能混淆器会产生非法的msil,但抖动会消化掉它,因为碰巧最后将不执行无效部分。

在构建分析工具时,除非您的目标是构建去混淆器,否则您将无法处理这些情况。