【问题标题】:How is a decompiler able to recognize a compiled constant?反编译器如何能够识别已编译的常量?
【发布时间】:2019-04-10 18:14:40
【问题描述】:

我正在使用 ILSpy 反编译 .Net 的程序集并查看代码。当我在WindowsBase.dll 中浏览System.Windows.Vector.AngleBetween(Vector, Vector) 的代码时,我偶然发现了一些奇怪的东西。

这是函数的完整代码:

public static double AngleBetween(Vector vector1, Vector vector2)
{
    double y = vector1._x * vector2._y - vector2._x * vector1._y;
    double x = vector1._x * vector2._x + vector1._y * vector2._y;
    return Math.Atan2(y, x) * (180.0 / Math.PI);
}

显然 ILSpy 可以识别Math.PI,这是一个常数。

这就是Microsoft Docs 所说的 C# 中的常量:

事实上,当编译器在 C# 源代码中遇到常量标识符时,它会将文字值直接替换为它生成的中间语言 (IL) 代码。

基于此,ILSpy 所做的似乎是不可能的。

注意:即使“使用调试符号中的变量名(如果可用)”和“显示调试符号中的信息(如果可用)”,也会出现此行为 设置中的选项未选中。

【问题讨论】:

  • 可能 ILSpy 只知道常见常量(如 Math.PI)的值。

标签: c# decompiling compile-time-constant ilspy


【解决方案1】:

正如您在此ILSpy issue 和相应的pull request 中所见,这是专门为Math.PI 等众所周知的值实现(硬编码)的。

来自 GitHub 问题:

我想通过以下方式计算 pi 系数: c = Math.PI / constant。如果我们得到“好”值(正好等于 1.0、2.0、0.5、1/180 等),我们只需用符号表达式替换它(Math.PI、Math.PI * 2、Math.PI / 2、Math .PI / 180 等)。

【讨论】:

  • 那么 ILSpy 是如何识别不知名的常量的?
  • 你有它识别的非知名常量的例子吗?
  • @DmytroMukalov 它可能可以从调试符号(PDB 文件)中识别非众所周知的常量,但我没有对此进行测试。
  • @DmytroMukalov 我刚刚测试过,它无法识别Math.PI * 254.543,即使开启了“使用调试符号”
【解决方案2】:

LINQPad 中的快速测试显示生成的 IL 代码存在一些差异(从 here 复制的 PI 常量值)。

来源:

void Main()
{
    double a = Math.PI;
    double b = 3.14159265358979;
}

IL:

IL_0000:  nop         
IL_0001:  ldc.r8      18 2D 44 54 FB 21 09 40 
IL_000A:  stloc.0     // a
IL_000B:  ldc.r8      11 2D 44 54 FB 21 09 40 
IL_0014:  stloc.1     // b
IL_0015:  ret   

在常量和字面值之间生成的 IL 代码似乎确实存在细微差别,但我还不确定它的确切含义。


MSDN documentation 中的上述值似乎与.NET Reference Source 中的信息相矛盾(参见 cmets)。从源代码调整后,IL 是相同的。

来源:

void Main()
{
    var a = Math.PI;
    var b = 3.14159265358979323846;
}

IL:

IL_0000:  nop         
IL_0001:  ldc.r8      18 2D 44 54 FB 21 09 40 
IL_000A:  stloc.0     // a
IL_000B:  ldc.r8      18 2D 44 54 FB 21 09 40 
IL_0014:  stloc.1     // b
IL_0015:  ret   

顺便说一句,似乎有一个open issue 来解决文档/源代码不一致问题。

【讨论】:

  • Math.PI 绝对是一个常量,它的声明:public const double PI = 3.1415926535897931;
  • 我只是从 docs.microsoft.com/en-us/dotnet/api/… 查看 MSDN 文档,它把它列为一个字段。
  • 您给出的值也与链接文档中的值不同。
  • 根据C# Reference Source,您是正确的。它被声明为常量。我会调整我的答案。
猜你喜欢
  • 2012-02-28
  • 2015-05-14
  • 1970-01-01
  • 2012-09-06
  • 1970-01-01
  • 2022-06-28
  • 2013-07-10
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多