0

Out of curiosity I want to see the assembly code for the implementation of some internal methods. In this example I am trying to start with the Interlocked.Increment method.

By setting a break point on my source code and then showing the Disassembly window I get the following display...

    29:             Interlocked.Increment(ref xx);
00007FFDBC110279  lea         rcx,[rbp+1FCh]  
00007FFDBC110280  call        00007FFE1AD90020  
00007FFDBC110285  mov         dword ptr [rbp+68h],eax  
00007FFDBC110288  nop 

The implementation seems to be at location 00007FFE1AD90020. But how can I see the disassembly at this address? Entering it into the Address field of the Disassembly window gives the following error...

The specified address cannot be displayed. End of expression expected.

Even if I enter the address of the break point position (00007FFE1AD90020) which we know is a valid address because here we are break pointed on it, I get the same error.

Any ideas how I can get to the disassembly for the target method?

NOTE: I am using Windows 8.1 Pro, Visual Studio 2013 Update 2 on a 64 bit machine. If that makes any difference.

Phil Wright
  • 22,580
  • 14
  • 83
  • 137

2 Answers2

2

You have a very good starting point. You are using the x64 debugger and it is the new debugging engine that's first available in VS2012. So you get to see the actual code addresses, not the fake ones that start numbering at 0. In other words, the call address as displayed by the debugger is accurate, you don't have to do through the painful address calculation necessary on older debuggers.

Two more pretty non-intuitive things you need to do:

  • As-is, the debugger is operating in managed mode and will refuse to display addresses that it considers to contain native code. You first need to enable unmanaged debugging, Project + Properties, Debugging, tick the "Enable native code debugging" option. Then you have to force the debugger to make the mode switch from the managed to the unmanaged debugging engine. Use Debug + Windows + Call Stack and double-click an unmanaged stack frame to force that mode switch. The one that displays ntdll.dll!RtlUserThreadStart is a good one.

  • You copied the address from the displayed value into the Address box. Not good enough, you need to tell the debugger that you meant the hexadecimal value. That requires putting 0x in front of it. In other words, the correct address to type is 0x00007FFE1AD90020

Now the debugger will decompile the code you're looking for. On my machine (different address than yours) it looks like this:

00007FFDE1C863B0  mov         eax,1  
00007FFDE1C863B5  lock xadd   dword ptr [rcx],eax  
00007FFDE1C863B9  inc         eax  
00007FFDE1C863BB  ret  

One more excruciating detail, you are looking at the machine code that was generated with the jitter optimizer turned off. That doesn't tell you that much, the optimizer can greatly modify the code. Really rather important that you take a look at code that will actually run on your user's machine. Makes a big difference here as well, the CALL is optimized away and the jitter generates an inline version of the method.

Switch to the Release build and change a setting: Tools + Options, Debugging, General, untick the "Suppress JIT optimization" option. You'll now see you no longer have to jump through the above listed hoops and Interlocked.Increment() method call turns into

00007FFD82F03AC4  mov         eax,1  
00007FFD82F03AC9  lock xadd   dword ptr [rsp+20h],eax  
Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
0

You can use .NET Reflector to see .NET functions' source code in any(?) .NET language, but not the assmebly code, because assembly code is generated at runtime (JIT) and only visible if you debug your program with some debugger.(If you debug within Visual Studio, its debugger will not allow you to step into .NET internal calls, because those methods has the attribute: DebuggerStepThroughAttribute)

In your case, InterlockedIncrement is not a .NET internal call. InterlockedIncrement is a Windows API function in kernel32.dll (and its source code probably cannot visible because that function possibly contains a syscall/sysenter instruction, except you can run the rdmsr assembly command to get the syscall/sysenter address)

ooffchee
  • 1
  • 1
  • 1