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

Joe*_*ite 5 debugging mixed-mode visual-studio

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

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

"Hello".Equals("hello", StringComparison.OrdinalIgnoreCase);
Run Code Online (Sandbox Code Playgroud)

然后调出Debug> Windows> Disassembly,进入Equals调用,然后逐步进入callx86指令.但是当我尝试使用"Step Into"时call(我从Reflector知道是nativeCompareOrdinal调用),它不会像我想要的那样步入nativeCompareOrdinal中的第一条指令 - 它会转而直接进入等于的下一个x86指令.

我正在构建为x86,因为x64应用程序不支持混合模式调试.我在工具>选项>调试中取消选中"只是我的代码",并且在项目属性>调试选项卡中选中了"启用非托管代码调试",但它仍然会跳过call.我还尝试启动该过程,然后附加调试器,并显式附加托管和本机调试器,但它仍然不会进入该InternalCall方法.

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

Han*_*ant 5

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

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

            "Hello".Equals("hello", StringComparison.OrdinalIgnoreCase);
00000025  call        6E53D5D0 
0000002a  nop              
Run Code Online (Sandbox Code Playgroud)

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

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

调试+ Windows +调用堆栈,然后双击非托管堆栈框架.现在,您可以在"反汇编"窗口的"地址"框中输入计算出的地址,前缀为0x.

6EEDD640  push        ebp  
6EEDD641  mov         ebp,esp 
6EEDD643  push        edi  
6EEDD644  push        esi  
6EEDD645  push        ebx  
6EEDD646  sub         esp,18h 
etc...
Run Code Online (Sandbox Code Playgroud)

宾果,如果你看到堆栈框架设置代码,你就会知道你很好.在其上设置断点并按F5.


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