如何调试非托管BCL(InternalCall)方法?

时间:2010-09-23 12:57:30

标签: visual-studio debugging mixed-mode

我想调试一个[MethodImpl(MethodImplOptions.InternalCall)] BCL方法的实现,这个方法可能是用C ++实现的。 (在这种特殊情况下,我正在看System.String.nativeCompareOrdinal。)这主要是因为我很爱管闲事,想知道它是如何实现的。

但是,Visual Studio调试器拒绝进入该方法。我可以在这个电话上设置一个断点:

"Hello".Equals("hello", StringComparison.OrdinalIgnoreCase);

然后调出Debug> Windows>反汇编,进入Equals调用,然后逐步进入call x86指令。但是当我尝试在call上使用“Step Into”时(我从Reflector知道的是nativeCompareOrdinal调用),它并没有像我想要的那样步入nativeCompareOrdinal中的第一条指令 - 而是跨步,然后直接进入Equals中的下一个x86指令。

我正在构建为x86,因为x64应用程序不支持混合模式调试。我在工具>中取消选中“Just My Code”选项>调试,我在项目属性中检查了“启用非托管代码调试”>调试选项卡,但它仍然跨越call。我还尝试启动该过程,然后附加调试器,并显式附加托管和本机调试器,但它仍然不会进入该InternalCall方法。

如何让Visual Studio调试器进入非托管方法?

1 个答案:

答案 0 :(得分:5)

是的,这很棘手。您在CALL指令中看到的偏移量是假的。此外,当当前焦点位于托管函数上时,它不会让您导航到非托管代码地址。

首先启用非托管代码调试并在调用上设置断点。运行代码,当断点命中时,使用Debug + Windows + Disassembly:

            "Hello".Equals("hello", StringComparison.OrdinalIgnoreCase);
00000025  call        6E53D5D0 
0000002a  nop              

调试器尝试显示绝对地址但是错误,因为它使用伪造的增量地址而不是真实的指令地址。所以首先恢复真正的相对值:0x6E53D5D0 - 0x2A = 0x6E53D5A6。

接下来,您需要找到真正的代码地址。调试+ Windows +寄存器并查看EIP寄存器的值。在我的情况下0x009A0095。添加5以获取nop,然后添加相对偏移量:0x9A0095 + 5 + 0x6E53D5A6 = 0x6EEDD640。函数的真实地址。

Debug + Windows + Call Stack并双击非托管堆栈框架。现在,您可以在“反汇编”窗口的“地址”框中输入计算出的地址,前缀为0x。

6EEDD640  push        ebp  
6EEDD641  mov         ebp,esp 
6EEDD643  push        edi  
6EEDD644  push        esi  
6EEDD645  push        ebx  
6EEDD646  sub         esp,18h 
etc...
宾果,如果你看到堆栈框架设置代码,你就会知道你很好。在其上设置断点并按F5。


当然,由于没有可用的源代码,您将步进机器代码。通过查看SSCLI20源代码,您将更好地了解此代码的作用。不能保证它将与当前版本的CLR中的实际代码匹配,但我的经验是,自1.0以来一直存在的这些低级代码块是高度保守的。实现在clr \ src \ classlibnative \ nls中,不确定哪个源代码文件。它不会被命名为“nativeCompareOrdinal”,这只是ecall.cpp使用的内部名称。