C# 语句可以生成非连接的 MSIL
Can a C# statement generate non connected MSIL
问题是关于 C# language specification and CIL language specification,以及 Microsoft 和 Mono 的 C# 编译器行为。
我正在构建一些在 CIL 上运行的代码分析工具(无论如何)。
考虑一些代码示例,我注意到代码语句(try/catch、ifelse、ifthen、循环...)生成 MSIL 的连接块。
但我想确定我无法编写生成非连接 MSIL 的 C# 代码结构。更具体地说,我可以编写任何 C# statement 转换为(类似的东西):
IL_0000:
IL_0001:
IL_0002:
// hole
IL_001a:
IL_001b:
我已经使用 goto
和嵌套循环尝试了一些奇怪的东西,但也许我不像某些用户那样疯狂。
理论上是的(这是我的经验)。您的分析工具不直接处理 c#,而是仅适用于 IL 代码。 IL 可以由任何人生成,不仅可以由 visual studio,还可以由其他语言编译器(如 visual basic,python)生成。网络...和混淆器!混淆器是真正的 culprit:while 其他编译器试图遵守规范,混淆器尽最大努力利用规范和目标运行时。
混淆代码可能违反某些常识模式。考虑这种情况:某些智能混淆器产生了非法的 msil,但抖动消化了它,因为恰好无效部分最终没有被执行。
构建分析工具时,您无法处理这些情况,除非您的目标是构建去混淆器。
当然,这是微不足道的可能。类似于:
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
语句从 0001
到 0009
,if
的结果是转到 0011
; return
语句是相同的代码,因此 "hole" 包含 nop
和 if
主体与结果之间的无条件分支。
更一般地说,您不应该对 C# 编译器生成的 IL 布局做出任何假设。除了所生成的 IL 是合法的,并且如果安全的话,可验证之外,编译器不作任何保证。
你说你在写一些代码分析工具;作为 C# 分析器重要部分的作者,以及在 Coverity 从事第三方分析工具工作的人,有一句忠告:对于您通常希望回答的有关 C# 程序的大多数问题,Roslyn 生成的解析树是您希望分析的实体,而不是 IL。解析树是具体的语法树;它与源代码中的每个字符都是一对一的。将优化的 IL 映射回原始源代码可能非常困难,并且在 IL 分析中很容易产生误报。
换句话说:source-to-IL 是语义保留但也是信息丢失;您通常希望分析其中包含最多信息的工件。
如果出于某种原因必须在 IL 级别操作分析器,那么您的首要任务可能应该是找到基本块的边界,尤其是在分析可达性属性时。
A "basic block" 是一个连续的 IL 块,其中块的终点不 "carry on" 到后续指令——因为它是一个分支,return 或抛出,例如——除了第一条指令之外,块中没有分支到任何地方。
然后您可以为每个方法形成一个基本块图,指示哪些方法可以将控制权转移到其他哪些块。这个"raises the level"你的分析;现在您分析的不是一系列 IL 指令的影响,而是分析基本块图的影响。
如果你能详细说明你在做什么类型的分析,我可以提供进一步的建议。
问题是关于 C# language specification and CIL language specification,以及 Microsoft 和 Mono 的 C# 编译器行为。
我正在构建一些在 CIL 上运行的代码分析工具(无论如何)。
考虑一些代码示例,我注意到代码语句(try/catch、ifelse、ifthen、循环...)生成 MSIL 的连接块。
但我想确定我无法编写生成非连接 MSIL 的 C# 代码结构。更具体地说,我可以编写任何 C# statement 转换为(类似的东西):
IL_0000:
IL_0001:
IL_0002:
// hole
IL_001a:
IL_001b:
我已经使用 goto
和嵌套循环尝试了一些奇怪的东西,但也许我不像某些用户那样疯狂。
理论上是的(这是我的经验)。您的分析工具不直接处理 c#,而是仅适用于 IL 代码。 IL 可以由任何人生成,不仅可以由 visual studio,还可以由其他语言编译器(如 visual basic,python)生成。网络...和混淆器!混淆器是真正的 culprit:while 其他编译器试图遵守规范,混淆器尽最大努力利用规范和目标运行时。
混淆代码可能违反某些常识模式。考虑这种情况:某些智能混淆器产生了非法的 msil,但抖动消化了它,因为恰好无效部分最终没有被执行。
构建分析工具时,您无法处理这些情况,除非您的目标是构建去混淆器。
当然,这是微不足道的可能。类似于:
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
语句从 0001
到 0009
,if
的结果是转到 0011
; return
语句是相同的代码,因此 "hole" 包含 nop
和 if
主体与结果之间的无条件分支。
更一般地说,您不应该对 C# 编译器生成的 IL 布局做出任何假设。除了所生成的 IL 是合法的,并且如果安全的话,可验证之外,编译器不作任何保证。
你说你在写一些代码分析工具;作为 C# 分析器重要部分的作者,以及在 Coverity 从事第三方分析工具工作的人,有一句忠告:对于您通常希望回答的有关 C# 程序的大多数问题,Roslyn 生成的解析树是您希望分析的实体,而不是 IL。解析树是具体的语法树;它与源代码中的每个字符都是一对一的。将优化的 IL 映射回原始源代码可能非常困难,并且在 IL 分析中很容易产生误报。
换句话说:source-to-IL 是语义保留但也是信息丢失;您通常希望分析其中包含最多信息的工件。
如果出于某种原因必须在 IL 级别操作分析器,那么您的首要任务可能应该是找到基本块的边界,尤其是在分析可达性属性时。
A "basic block" 是一个连续的 IL 块,其中块的终点不 "carry on" 到后续指令——因为它是一个分支,return 或抛出,例如——除了第一条指令之外,块中没有分支到任何地方。
然后您可以为每个方法形成一个基本块图,指示哪些方法可以将控制权转移到其他哪些块。这个"raises the level"你的分析;现在您分析的不是一系列 IL 指令的影响,而是分析基本块图的影响。
如果你能详细说明你在做什么类型的分析,我可以提供进一步的建议。