需要帮助解读一行汇编代码,来自.NET JIT编译后的代码

4
在C#构造函数中,如果最终调用了this(...),那么实际调用会被转换为以下内容:
0000003d  call        dword ptr ds:[199B88E8h]

这里的DS寄存器内容是什么?我知道它是数据段,但这是通过VMT表或类似方式调用的吗?我怀疑不是,因为this(...)不会是对虚拟方法的调用,只是另一个构造函数。
我问这个问题是因为该位置的值在某种程度上似乎有问题,如果我按F11,跟踪(Visual Studio 2008)该调用指令,程序将崩溃并显示访问冲突。
代码深入到第三方控件库中,虽然我有源代码,但我没有编译足够调试信息的程序集,所以只能通过反汇编器进行跟踪,然后将其与实际代码匹配。
涉及的C#代码如下:
public AxisRangeData(AxisRange range) : this(range, range.Axis) {
}

反射器向我展示了这段IL代码:

.maxstack 8
L_0000: ldarg.0 
L_0001: ldarg.1 
L_0002: ldarg.1 
L_0003: callvirt instance class DevExpress.XtraCharts.AxisBase DevExpress.XtraCharts.AxisRange::get_Axis()
L_0008: call instance void DevExpress.XtraCharts.Native.AxisRangeData::.ctor(class DevExpress.XtraCharts.ChartElement, class DevExpress.XtraCharts.AxisBase)
L_000d: ret 

这里是最后一次调用同一个类的另一个构造函数失败了。调试器从未进入另一个方法,它只是崩溃了。

JIT编译后该方法的反汇编代码如下:

00000000  push        ebp  
00000001  mov         ebp,esp 
00000003  sub         esp,14h 
00000006  mov         dword ptr [ebp-4],ecx 
00000009  mov         dword ptr [ebp-8],edx 
0000000c  cmp         dword ptr ds:[18890E24h],0 
00000013  je          0000001A 
00000015  call        61843511 
0000001a  mov         eax,dword ptr [ebp-4] 
0000001d  mov         dword ptr [ebp-0Ch],eax 
00000020  mov         eax,dword ptr [ebp-8] 
00000023  mov         dword ptr [ebp-10h],eax 
00000026  mov         ecx,dword ptr [ebp-8] 
00000029  cmp         dword ptr [ecx],ecx 
0000002b  call        dword ptr ds:[1889D0DCh]   // range.Axis
00000031  mov         dword ptr [ebp-14h],eax 
00000034  push        dword ptr [ebp-14h] 
00000037  mov         edx,dword ptr [ebp-10h] 
0000003a  mov         ecx,dword ptr [ebp-0Ch] 
0000003d  call        dword ptr ds:[199B88E8h]   // this(range, range.Axis)?
00000043  nop              
00000044  mov         esp,ebp 
00000046  pop         ebp  
00000047  ret              

我问的基本上是这样的:

  • ds:[ADDR]间接寻址的目的是什么?VMT表只用于虚拟,这是构造函数
  • 构造函数可能尚未被JIT编译,这是否意味着调用实际上会通过JIT shim调用?我有点无助,任何信息都可能有所帮助。

编辑:好吧,问题变得更糟了,或者更好了,或者不管怎样。

我们在Visual Studio 2008解决方案的C#项目中开发.NET功能,并通过Visual Studio进行调试和开发。

然而,最终,这段代码将加载到由Win32 Delphi应用程序托管的.NET运行时中。

为了方便对这些功能进行实验,我们还可以配置Visual Studio项目/解决方案/调试器,将生成的dll复制到Delphi应用程序目录中,然后通过Visual Studio调试器执行Delphi应用程序。

事实证明,如果我在调试器之外运行程序,问题就会消失,但是在调试期间,它每次都会出现。

不确定这是否有帮助,但由于该代码距离预计的生产发布还有大约6个月左右,因此在即将到来的测试发布中,它可以减轻一些压力。

我稍后会深入研究内存部分,但可能要等到周末才行,并发布跟进结果。


你能找出ds:[199B88E8h]指向的实际地址并对其进行反汇编吗? - vgru
这是一个存储在那个位置的地址,该地址指向未映射的内存,因此导致访问冲突。 - Lasse V. Karlsen
1个回答

3
数据段通常是编译器放置全局变量和导入地址表的地方。
00000029  cmp         dword ptr [ecx],ecx 
0000002b  call        dword ptr ds:[1889D0DCh]

第一行实际上是一个空指针检查,如果ECX寄存器中的指针无效,则最终会引发NullReferenceException异常。
在调用实际方法之前,callvirt MSIL指令必须执行空值检查。也就是说,我们可以安全地假设这两行汇编代码具有以下MSIL代码表示形式:
class DevExpress.XtraCharts.AxisBase DevExpress.XtraCharts.AxisRange::get_Axis()

以下是被注释的汇编代码:

00000026  mov         ecx,dword ptr [ebp-8]      // store the pointer to the 'range' in ECX
00000029  cmp         dword ptr [ecx],ecx        // null-check
0000002b  call        dword ptr ds:[1889D0DCh]   // range.get_Axis()
00000031  mov         dword ptr [ebp-14h],eax    // store the result in a local variable
00000034  push        dword ptr [ebp-14h]        // push the result onto a stack
00000037  mov         edx,dword ptr [ebp-10h]    // this variable was previously loaded with the 'range' pointer
0000003a  mov         ecx,dword ptr [ebp-0Ch]    // here seems to be stored the actual 'this' pointer
0000003d  call        dword ptr ds:[199B88E8h]   // call the this(...) ctor

我不清楚为什么它会崩溃,你是否尝试查看内存位置的内容 (DS:[199B88E8h])?


是的,我明天会发布我从各种调用指令中看到的值。可以说与所有其他指令相比,它看起来很奇怪(在这一点上,我停止了每个调用指令)。有问题的那个是唯一一个第一个数字非零的指令。 - Lasse V. Karlsen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接