我想调试一个[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调试器进入非托管方法?
答案 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使用的内部名称。