出于好奇,我想看一些内部方法实现的汇编代码。在这个例子中,我试图从Interlocked.Increment方法开始。
通过在我的源代码上设置断点然后显示Disassembly窗口,我得到以下显示...
29: Interlocked.Increment(ref xx);
00007FFDBC110279 lea rcx,[rbp+1FCh]
00007FFDBC110280 call 00007FFE1AD90020
00007FFDBC110285 mov dword ptr [rbp+68h],eax
00007FFDBC110288 nop
实施似乎位于00007FFE1AD90020。但是我怎么能在这个地址看到反汇编呢?将其输入到“反汇编”窗口的“地址”字段中会出现以下错误...
The specified address cannot be displayed. End of expression expected.
即使我输入断点位置的地址(00007FFE1AD90020),我们知道这是一个有效的地址,因为我们在这里断点,我得到同样的错误。
我有什么想法可以进入目标方法的反汇编吗?
注意:我在64位计算机上使用Windows 8.1 Pro,Visual Studio 2013 Update 2。如果这有任何区别。
答案 0 :(得分:2)
你有一个非常好的起点。您正在使用x64调试器,它是VS2012中首次提供的新调试引擎。所以你可以看到实际的代码地址,而不是那些从0开始编号的伪代码。换句话说,调试器显示的调用地址是准确的,你不必通过痛苦的地址计算来完成在较旧的调试器上。
您需要做的另外两件非常直观的事情:
按原样,调试器在托管模式下运行,并拒绝显示它认为包含本机代码的地址。首先需要启用非托管调试,Project + Properties,Debugging,勾选"启用本机代码调试"选项。然后,您必须强制调试器使模式从托管调试引擎切换到非托管调试引擎。使用Debug + Windows + Call Stack并双击非托管堆栈帧以强制进行该模式切换。显示ntdll.dll!RtlUserThreadStart
的那个是好的。
您将地址从显示的值复制到地址框中。不够好,您需要告诉调试器您的意思是十六进制值。这需要在其前面放置0x
。换句话说,要输入的正确地址是0x00007FFE1AD90020
现在,调试器将反编译您正在寻找的代码。在我的机器上(与你的地址不同),它看起来像这样:
00007FFDE1C863B0 mov eax,1
00007FFDE1C863B5 lock xadd dword ptr [rcx],eax
00007FFDE1C863B9 inc eax
00007FFDE1C863BB ret
另一个令人难以忍受的细节,您正在查看关闭抖动优化器时生成的机器代码。这并没有告诉你那么多,优化器可以大大修改代码。非常重要的是,您要查看实际运行在用户计算机上的代码。这里也有很大的不同,CALL被优化掉了,抖动产生了该方法的内联版本。
切换到发布版本并更改设置:工具+选项,调试,常规,取消勾选"抑制JIT优化"选项。你现在看到你不再需要跳过上面列出的箍和Interlocked.Increment()方法调用变成
00007FFD82F03AC4 mov eax,1
00007FFD82F03AC9 lock xadd dword ptr [rsp+20h],eax
答案 1 :(得分:0)
您可以使用.NET Reflector在任何(?).NET语言中查看.NET函数的源代码,但不能使用可编程代码,因为汇编代码是在运行时生成的(JIT),只有在调试时才可见你的程序有一些调试器。(如果你在Visual Studio中调试,它的调试器将不允许你进入.NET内部调用,因为这些方法具有属性:DebuggerStepThroughAttribute)
在您的情况下,InterlockedIncrement不是.NET内部调用。 InterlockedIncrement是kernel32.dll中的Windows API函数(其源代码可能无法显示,因为该函数可能包含syscall / sysenter指令,但您可以运行rdmsr assembly命令来获取syscall / sysenter地址)