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

5

我想要进入一个由[MethodImpl(MethodImplOptions.InternalCall)]修饰的BCL方法的实现,这个方法很可能是用C++实现的。(特别是在我看System.String.nativeCompareOrdinal 的时候)这主要是因为我好奇,想知道它是如何实现的。

然而,Visual Studio调试器拒绝进入该方法。我可以在这个调用上设置断点:

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

然后打开调试(Debug) > 窗口(Windows) > 反汇编(Disassembly),进入Equals调用并单步执行,直到它到达 call x86 指令。但是,当我尝试对call使用"单步调试"功能(我知道从反编译器(Reflector)中,这是nativeCompareOrdinal调用),它不会像我期望的那样跳转到nativeCompareOrdinal内部的第一条指令,而是跳过该方法,直接进入Equals中下一个x86指令。
由于混合模式调试不支持x64应用程序,因此我正在以x86方式构建。我在 "工具(Tools) > 选项(Options) > 调试(Debugging)" 中取消了 "我的代码(Just My Code)" ,在项目属性(Project Properties) > 调试(Debug)选项卡中勾选了"启用非托管代码调试",但它仍然跳过了call。我还尝试启动进程,然后附加调试器,并显式附加托管和本机调试器,但仍无法进入该InternalCall方法。
如何使Visual Studio调试器可以进入非托管方法?
1个回答

5

是的,这很棘手。你看到的CALL指令的偏移是虚假的。此外,当当前焦点在托管函数上时,它不允许你导航到非托管代码地址。

首先启用非托管代码调试并在调用上设置断点。运行代码,当断点命中时使用调试+窗口+反汇编:

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

调试器试图显示绝对地址,但由于使用虚假的增量地址而获取错误。因此首先要恢复真实的相对值:0x6E53D5D0 - 0x2A = 0x6E53D5A6。

接下来需要找到真正的代码地址。单击"调试(Debug)" + "窗口(Windows)" + "寄存器(Registers)",查看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使用的内部名称。


我最终并没有找到一个方法,而是找到了一个thunk -- 有一个CALL调用了一个JMP再到一个POP EAX(将CALL的返回值从堆栈中弹出)。我猜这是thunk的一个合理起点。然而,当我在CALL操作码上设置断点时,调试器永远停留在那里,占用CPU但从未中断;当我删除该断点并在JMP和POP上设置断点时,它直接跳过它们,让应用程序运行完成而从未中断。所以我可以通过这种方式查看x86操作码,但显然无法进行调试。比什么都不好,但仍然有些令人失望。 - Joe White

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