Reference
I wanted to introduce how to use SOS to debug CLR programs in windbg according to the command classification in the SOS help file, but it was not intuitive enough. Simply changed to based on my analysis of the actual cases of CLR, step by step introduced the function, although the structure is a bit confusing, it is more intuitive and easy to use.
The previous two Articles respectively introduced the basic concepts of windbg debugging configuration and thread. This article will analyze the process of JIT compiling object method and gradually introduce how to use windbg to debug CLR programs.
Use windbg to explore the CLR world [1]-Installation and environment Configuration
Explore the CLR world with windbg [2]-Thread
First, write a simple demo. CS program and compile it as demo.exe. Use the configured windbg to open it:
Reference:
Using system;
Namespace flier
{
Class entrypoint
{
Public void M1 ()
{
System. Console. Write ("entrypoint. M1 ()");
}
Public void m2 ()
{
System. Console. Write ("entrypoint. m2 ()");
}
Public static void main ()
{
Entrypoint Ep = new entrypoint ();
Ep. M1 ();
Ep. m2 ();
}
}
}
Windbg will interrupt execution after loading demo.exe. In this case, you can use. load SOS command to load SOS. DLL command extension, and use. run the LD demo command to load the debug symbol file of demo.exe and run the LM command to verify whether the file is successfully loaded.
Then, use LD Kernel32 to load the debugging symbol file of Kernel32 and use BP Kernel32! The loadlibraryexw "du poi (esp + 4)" command adds a breakpoint to the function entry for loading the DLL. The next step is a G command until mscorwks. dll is loaded. This mscorwks. dll is similar to the JVM. dll Virtual Machine implementation code in JVM. Most of the functions we need to know are in it. For more information, see my previous article "Analysis of CLR program loading principles on the. NET platform".
After mscorwks. dll is loaded, use the LD mscorwks command to load its debugging symbol library. Then we can start our exploration work.
The currently used windbg command is as follows:
Reference:
. Load SOS // load the SOS debugging extension module, which can be verified using the. Chain command.
LD demo // load demo.exe to debug the symbol library, which can be verified by LM command
LD Kernel32 // load Kernel32.exe to debug the Symbol Library
BP Kernel32! Loadlibraryexw "du poi (esp + 4)" // sets the breakpoint to monitor when mscorwks. dll is loaded.
G // execute until mscorwks. dll is loaded
BD 0 // clear the previously set breakpoint and start processing mscorwks. dll
LD mscorwks // load mscorwks. DLL to debug the Symbol Library
In chapter 6 of ". Net Article 1st: Public Language Runtime Library", Don box introduces the internal implementation process of method calls. Before JIT, call mscorwks. dll is saved in the method table! Prestubworker is called. the JIT compilation and calling of the target il code will not be performed until the first time it is used. Therefore, we can set a breakpoint (BP mscorwks! Prestubworker) to see how the system calls this function.
Reference:
0: 000> BP mscorwks! Prestubworker
0: 000> G
Modload: 70ad0000 70bb6000 E: \ windows \ winsxs \ x86_Microsoft.Windows.Common-Controls_6595b64144ccf1df_6.0.100.0_x-ww_8417450B \ comctl32.dll
Modload: 79780000 79980000 E: \ windows \ microsoft.net \ framework \ v1.1.4322 \ mscorlib. dll
Modload: 79980000 79ca6000 E: \ windows \ Assembly \ nativeimages1_v1.1.4322 \ mscorlib \ 1.0.5000.0 _ b77a5c561934e089_ed6bc96c \ mscorlib. dll
Modload: 79510000 79523000 E: \ windows \ Microsoft. NET \ framework \ v1.1.4322 \ mscorsn. dll
Breakpoint 1 hit
Eax = 0012f7c0 EBX = 0014860ecx = 04aa112c edX = 00000004 ESI = 0012f784 EDI = 0012f9a8
EIP = 791d6a4a ESP = 0012f764 EBP = 0012f79c iopl = 0 NV up ei pl Zr Na Po NC
Cs = 001b Ss = 0023 DS = 0023 es = 0023 FS = 0038 GS = 0000 EFL = 00000246
Mscorwks! Prestubworker:
791d6a4a 55 push EBP
When a breakpoint is activated, the function is called. Let's first use K to check the context environment when the function is called.
Reference:
0: 000> K
Childebp retaddr
0012f760 0014930e mscorwks! Prestubworker
Warning: frame IP not in any known module. Following frames may be wrong.
0012f79c 791da434 0x14930e
0012f8b4 791dd2ec mscorwks! Methoddesc: calldescr + 0x1b6
0012f96c 79240405 mscorwks! Methoddesc: Call + 0xc5
0012fa18 79240520 mscorwks! Appdomain: initializedomaincontext + 0x10f
0012fa7c 7923d744 mscorwks! Systemdomain: initializedefaultdomain + 0x11c
0012fd60 791c6e73 mscorwks! Systemdomain: executemainmethod + 0x120
0012ffa0 791c6ef3 mscorwks! Executeexe + 0x1c0
0012ffb0 7880a53e mscorwks! _ Corexemain + 0x59
0012ffc0 77e1f38c mscoree! _ Corexemain + 0x30 [F: \ dd \ NDP \ CLR \ SRC \ DLLs \ Shim. cpp @ 5426]
0012fff0 00000000 Kernel32! Baseprocessstart + 0x23
Here we can see from mscoree! _ Corexemain: the step that is executed along the way. The warning indicates that the stack frame is not in any known module. This is normal, because the stack frame actually points to the code dynamically generated by JIT. Mscorwks we are monitoring! The prestubworker function is only used as the entry to the function stub in the method table. When the system starts, it also calls JIT to compile and execute the code.
Next we will use SOS! The clrstack command looks at the CLR call stack, which is shown as follows:
Reference:
0: 000>! Clrstack
Succeeded
Loaded son of strike data table version 5 from "E: \ windows \ Microsoft. NET \ framework \ v1.1.4322 \ mscorwks. dll"
Thread 0
ESP EIP
0012f784 791d6a4a [frame: prestubmethodframe] [Default] [hasthis] void system. appdomain. setupdomain (valueclass system. loaderoptimization, String, string)
0012f9a8 791d6a4a [frame: gcframe]
0012fad0 791d6a4a [frame: debuggerclassinitmarkframe]
0012fa94 791d6a4a [frame: gcframe]
For more details, you can use the-P,-l, or-r parameters to display the parameters, local variables, and registers. Of course, the first two must be supported by the debugging symbol library.
So all the way g ;! Execute clrstack until the flier. entrypoint. M1 function needs to be processed:
Reference:
0: 000>! Clrstack
Thread 0
ESP EIP
0012f68c 791d6a4a [frame: prestubmethodframe] [Default] [hasthis] void flier. entrypoint. M1 ()
0012f69c 06d90080 [Default] void flier. entrypoint. Main ()
0012f9b0 791da717 [frame: gcframe]
0012fa94 791da717 [frame: gcframe]
Use it now! The dumpstackobjects command can view all objects used in the current thread stack.
Reference:
0: 000>! Dumpstackobjects
ESP/REG Object Name
ECX 04aa1a90 flier. entrypoint
0012f678 04aa1a90 flier. entrypoint
0012f67c 04aa1a90 flier. entrypoint
0012f680 04aa1a90 flier. entrypoint
Here, the flier. entrypoint object address 0x04aa1a90 is the location of the object to be analyzed in the memory.
The windbg Command Used in this phase is as follows:
Reference:
BP mscorwks! Prestubworker // sets the code breakpoint
G // continue to run to the breakpoint
K // view native stack calls for function calls
! Clrstack // view the CLR stack call for a function call
! Dumpstackobjects // view all objects used in the thread Stack
Once you know the address, you can use it! Run the dumpobj command to view the object details.
Reference:
0: 000>! Dumpobj 04aa1a90
Name: flier. entrypoint
Methodtable 0x009750a8
Eeclass 0x06c632e8
Size 12 (0xc) bytes
Mdtoken: 02000002 (D: \ temp \ demo.exe)
The information includes the object type name and the eeclass of the type information, the object size and Token, and the method table (methodtable) it is our goal to analyze method calls. We can use it! The dumpclass command further checks the object type information:
Reference:
0: 000>! Dumpclass 0x6c632e8
Class Name: flier. entrypoint
Mdtoken: 02000002 ()
Parent class: 79b7c3c8
Classloader: 00153850
Method table: 009750a8
Vtable slots: 4
Total method slots: 8
Class attributes: 100000:
Flags: 1000003
Numinstancefields: 0
Numstaticfields: 0
Threadstaticoffset: 0
Threadstaticssize: 0
Contextstaticoffset: 0
Contextstaticssize: 0
It can be found that there are many conformances between its information and object information. As Don box said, an object reference points to an eeclass instance of the type, and the method table is of the type, and its objects share the same. We can use it! Run the dumpmt command to further view the information of the method table. The-MD parameter indicates that you need to view the description of each method (methoddesc ):
Reference:
0: 000>! Dumpmt-MD 0x09750a8
Eeclass: 06c632e8
Module: 0014e090
Name: flier. entrypoint
Mdtoken: 02000002 (D: \ temp \ demo.exe)
Methodtable flags: 80000
Number of ifaces in ifacemap: 0
Interface map: 009750f4
Slots in vtable: 8
--------------------------------------
Methoddesc table
Entry methoddesc JIT name
79b7c4eb 79b7c4f0 none [Default] [hasthis] string system. Object. tostring ()
79b7c473 79b7c478 none [Default] [hasthis] Boolean system. Object. Equals (object)
79b7c48b 79b7c490 none [Default] [hasthis] I4 system. Object. gethashcode ()
79b7c52b 79b7c530 none [Default] [hasthis] void system. Object. Finalize ()
0097506b 00975070 none [Default] [hasthis] void flier. entrypoint. M1 ()
0097507b 00975080 none [Default] [hasthis] void flier. entrypoint. m2 ()
0097508b 00975090 none [Default] void flier. entrypoint. Main ()
0097509b 009750a0 none [Default] [hasthis] void flier. entrypoint .. ctor ()
We can see that there are eight table items in the method table, and the first four have been bound to the static functions pre-compiled with ngen.
Reference:
0: 000> U 79b7c4eb
Mscorlib_79980000 + 0x1fc4eb:
79b7c4eb e8909cfeff call mscorlib_79980000 + 0x1e6180 (79b66180)
79b7c4f0 0000 add [eax], Al
79b7c4f2 0080d86206c0 add [eax + 0xc00662d8], Al
79b7c4f8 06 push es
79b7c4f9 00fc add ah, BH
79b7c4fb e8809cfeff call mscorlib_79980000 + 0x1e6180 (79b66180)
79b7c500 07 pop es
79b7c501 0010 add [eax], DL
The last four virtual methods can be overwritten in the method table, which is also why vtable slots = 4 and total method slots = 8 when viewing the type information.
For each item in the method table, you can use it! View the detailed description of the dumpmd Command, as shown in figure
Reference:
0: 000>! Dumpmd 0x00975070
Method Name: [Default] [hasthis] void flier. entrypoint. M1 ()
Methodtable 9750a8
Module: 14e090
Mdtoken: 06000001 (D: \ temp \ demo.exe)
Flags: 0
Il RVA: 00002050
Il RVA indicates that the Il code of this method is relative to the virtual address (il rva). That is to say, this method has not been JIT and still exists in the form of IL code. For methods that have completed JIT, the virtual address (method VA) of the function body code after JIT is displayed ):
Reference:
0: 000>! Dumpmd 0x009750a0
Method Name: [Default] [hasthis] void flier. entrypoint .. ctor ()
Methodtable 9750a8
Module: 14e090
Mdtoken: 06000004 (D: \ temp \ demo.exe)
Flags: 0
Method VA: 06d900a8
The windbg Command Used in this phase is as follows:
Reference:
! Dumpobj 04aa1a90 // view the object details
! Dumpclass 0x6c632e8 // view the Type Details
! Dumpmt-MD 0x09750a8 // view detailed information of the method table
! Dumpmd 0x00975070 // view detailed information about the method description of a method table item
U 0x79b7c4eb // disassemble the instruction of the specified address
Let's disassemble it! Several methods listed by the dumpmt command will find that, as Don box said, the JIT code has been directed to a JMP command and directly jumps to the compiled method body, such:
Reference:
0: 000> U 0097509b
0097509b e908b04106 JMP 06d900a8
A function without JIT directs to a call command, calls a Prolog Code, and indirectly calls mscorwks! The prestubworker function completes the actual JIT work, for example:
Reference:
0: 000> U 0x0097506b
0097506b e878427dff call 001492e8
0: 000> U 0x0097507b
0097507b e868427dff call 001492e8
This Prolog Code is very simple and is responsible for constructing mscorwks! Call Stack required by prestubworker
Reference:
0: 000> U 0x001492e8
001492e8 52 push edX
001492e9 68f0301b79 push 0x791b30f0
001492ee 55 push EBP
001492ef 53 push EBX
001492f0 56 push ESI
001492f1 57 push EDI
001492f2 8d742410 Lea ESI, [esp + 0x10]
001492f6 51 push ECx
001492f7 52 push edX
001492f8 648b1d2c0e0000 mov EBX, FS: [00000e2c]
001492ff 8b7b08 mov EDI, [EBX + 0x8]
00149302 897e04 mov [ESI + 0x4], EDI
00149305 897308 mov [EBX + 0x8], ESI
00149308 56 push ESI
00149309 e83cd70879 call mscorwks! Prestubworker (791d6a4a)
0014930e 897b08 mov [EBX + 0x8], EDI
00149311 894604 mov [ESI + 0x4], eax
00149314 5A pop edX
00149315 59 pop ECx
00149316 5f pop EDI
00149317 5E pop ESI
00149318 5B pop EBX
00149319 5d pop EBP
0014931a 83c404 add ESP, 0x4
0014931d 8f0424 pop [esp]
00149320 C3 RET
The Prolog Code is dynamically generated by functions like generateprestub in the rotor (Vm \ i386 \ cgenx86.cpp: 1829) to encapsulate the call to the prestubworker function. The prestubworker function calls JIT to complete the real function compilation, and changes the entry of the method table to the JMP Instruction of the compiled function body. For detailed procedures, see the introduction of Don box in Chapter 6 of. Net Article 1st: Public Language Runtime Library. In the future, I will have the opportunity to write an article to analyze the JIT workflow in detail.
Handle flier in JIT. entrypoint. when M1, run the G command and analyze the M1 function entry. As described above, the call command for calling the JIT process is changed to the JMP command for directly calling the native function body. : D
This section describes how to use windbg to track and debug CLR programs, and how to analyze stack, object, and class information, I hope you can start your journey to explore the internal world of CLR.
Jason Zander, in an article on his blog, SOS debugging with the CLR (Part 1), also details some methods for debugging CLR programs using windbg and SOS. It is worth reading.
Related Articles: |
|
Using windbg to explore the calling mechanism of the CLR world [4] MethodUsing windbg to explore the CLR world [3] JIT process of tracking methodsExploring the CLR world with windbg [2] threadsUse windbg to explore the CLR world [1]-Installation and environment Configuration |