Also, no other method can terminate the process (Task Manager, Process Explorer, Kill utility…). Attempting to kill the process from these utilities will, however, schedule the terminating process. Thus, the debugger would receive EXIT_PROCESS_DEBUG_EVENT as the next event!
When a process is being debugged, the debugger always receives the exception before the debuggee gets it. You must have seen "First-chance exception at 0x00412882 in SomeModule:…" while debugging your Visual C++ module. This is referred to as First Chance Exception.
處理器一般提供以下功能供上層軟體除錯:
Linux 提供 ptrace,Windows 提供 Win32 Debug API。
針對匯編語言,除錯訊息應該要能帶入以下資訊。
Before any other stabs occur, there must be a stab specifying the source file. This information is contained in a symbol of stab type N_SO; the string field contains the name of the file. The value of the symbol is the start address of the portion of the text section corresponding to that file.
An N_SLINE symbol represents the start of a source line. The desc field contains the line number and the value contains the code address for the start of that source line.
The N_LBRAC and N_RBRAC stabs that describe the block scope of a procedure are located after the N_FUN stab that represents the procedure itself.
$ cat hello.c main() { printf("Hello world"); } $ gcc -gstabs -S hello.c .file "hello.c" .stabs "hello.c",100,0,2,.Ltext0 .text .Ltext0: .stabs "gcc2_compiled.",60,0,0,0 .stabs "int:t(0,1)=r(0,1);-2147483648;2147483647;",128,0,0,0 $ objdump --stabs hello a.out: file format elf32-i386 Contents of .stab section: Symnum n_type n_othr n_desc n_value n_strx String -1 HdrSym 0 118 00000f24 1 0 SO 0 2 0804841d 1 hello.c 1 OPT 0 0 00000000 9 gcc2_compiled. 2 LSYM 0 0 00000000 24 int:t(0,1)=r(0,1);-2147483648;2147483647;
$ cat hello.c int main() { int i = 0; return 0; } $ gcc -g hello.c # 觀察 DWAFT 資訊。 $ objdump --dwarf=info a.out a.out: file format elf32-i386 Contents of the .debug_info section: Compilation Unit @ offset 0x0: Length: 0x4f (32-bit) Version: 4 Abbrev Offset: 0x0 Pointer Size: 4 <0><b>: Abbrev Number: 1 (DW_TAG_compile_unit) <c> DW_AT_producer : (indirect string, offset: 0x22): GNU C 4.8.2 -mtune=generic -march=i686 -g -fstack-protector <10> DW_AT_language : 1 (ANSI C) <11> DW_AT_name : (indirect string, offset: 0x0): hello.c <15> DW_AT_comp_dir : (indirect string, offset: 0x8): /home/chenwj/project/misc <19> DW_AT_low_pc : 0x80483ed <1d> DW_AT_high_pc : 0x14 <21> DW_AT_stmt_list : 0x0 <1><25>: Abbrev Number: 2 (DW_TAG_subprogram) <26> DW_AT_external : 1 <26> DW_AT_name : (indirect string, offset: 0x5e): main <2a> DW_AT_decl_file : 1 <2b> DW_AT_decl_line : 1 <2c> DW_AT_type : <0x4b> <30> DW_AT_low_pc : 0x80483ed <34> DW_AT_high_pc : 0x14 <38> DW_AT_frame_base : 1 byte block: 9c (DW_OP_call_frame_cfa) <3a> DW_AT_GNU_all_call_sites: 1 <3a> DW_AT_sibling : <0x4b> <2><3e>: Abbrev Number: 3 (DW_TAG_variable) <3f> DW_AT_name : i <41> DW_AT_decl_file : 1 <42> DW_AT_decl_line : 2 <43> DW_AT_type : <0x4b> <47> DW_AT_location : 2 byte block: 91 74 (DW_OP_fbreg: -12) <2><4a>: Abbrev Number: 0 <1><4b>: Abbrev Number: 4 (DW_TAG_base_type) <4c> DW_AT_byte_size : 4 <4d> DW_AT_encoding : 5 (signed) <4e> DW_AT_name : int <1><52>: Abbrev Number: 0
LOC CFA R0 R1 ... RN L0 L1 // 執行完 L0 之後的狀態 ... LN
底下是 DWARF Debugging Information Format 的 Appendix 5。
high -------------- <---- old R7 (CFA) | R1 | -------------- 8 | old R6 | -------------- 4 | R4 | -------------- <---- new R7/R6 low
;; start prologue foo sub R7, R7, <fsize> ; Allocate frame foo+4 store R1, R7, (<fsize>-4) ; Save the return address foo+8 store R6, R7, (<fsize>-8) ; Save R6 foo+12 add R6, R7, 0 ; R6 is now the Frame ptr foo+16 store R4, R6, (<fsize>-12) ; Save a preserve reg. ;; This subroutine does not change R5 ... ;; Start epilogue (R7 has been returned to entry value) foo+64 load R4, R6, (<fsize>-12) ; Restore R4 foo+68 load R6, R7, (<fsize>-8) ; Restore R6 foo+72 load R1, R7, (<fsize>-4) ; Restore return address foo+76 add R7, R7, <fsize> ; Deallocate frame foo+80 jump R ; Return foo+84
Loc CFA R0 R1 R2 R3 R4 R5 R6 R7 R8 foo [R7]+0 s u u u s s s s r1 # 尚未執行第一條指令。caller 調用 foo 時,R8 (PC) 被存在 R1。 foo+4 [R7]+fsize s u u u s s s s r1 # 執行完 foo 指令。CFA 為當前 R7 + fsize。 foo+8 [R7]+fsize s u u u s s s s c4 # 執行完 foo+4 指令。R1 存在 CFA - 4。 foo+12 [R7]+fsize s u u u s s c8 s c4 # 執行完 foo+8 指令。R6 存在 CFA - 8。 foo+16 [R6]+fsize s u u u s s c8 s c4 # 執行完 foo+12 指令。當前棧底由 R7 改為 R6。CFA 為當前 R6 + fsize。 foo+20 [R6]+fsize s u u u c12 s c8 s c4 # 執行完 foo+16 指令。R4 存在 CFA - 12。 foo+64 [R6]+fsize s u u u c12 s c8 s c4 # 此時棧頂 R7 已回復到 R6 所在位置。 foo+68 [R6]+fsize s u u u s s c8 s c4 # 執行完 foo+64 指令。R4 回復至調用前的狀態。 foo+72 [R7]+fsize s u u u s s s s c4 # 執行完 foo+68 指令。R6 回復至 old R6。當前棧底由 R6 改為 R7。 foo+76 [R7]+fsize s u u u s s s s r1 # 執行完 foo+72 指令。R1 回復至調用前的狀態。 foo+80 [R7]+0 s u u u s s s s r1 # 執行完 foo+74 指令。CFA 為當前 R7。
cie+13 DW_CFA_def_cfa (7, 0) ; CFA = [R7]+0 cie+16 DW_CFA_same_value (0) ; R0 not modified (=0) cie+18 DW_CFA_undefined (1) ; R1 scratch cie+20 DW_CFA_undefined (2) ; R2 scratch cie+22 DW_CFA_undefined (3) ; R3 scratch cie+24 DW_CFA_same_value (4) ; R4 preserve cie+26 DW_CFA_same_value (5) ; R5 preserve cie+28 DW_CFA_same_value (6) ; R6 preserve cie+30 DW_CFA_same_value (7) ; R7 preserve cie+32 DW_CFA_register (8, 1) ; R8 is in R1
fde+16 DW_CFA_advance_loc(1) ; foo+4 fde+17 DW_CFA_def_cfa_offset(<fsize>/4) ; CFA = [R7] + <fsize>/4 (4) fde+19 DW_CFA_advance_loc(1) ; foo+8 fde+20 DW_CFA_offset(8,1) ; R8 = CFA - 1 (4) fde+22 DW_CFA_advance_loc(1) ; foo+12 fde+23 DW_CFA_offset(6,2) ; R6 = CFA - 2 (4) fde+25 DW_CFA_advance_loc(1) ; foo+16 fde+26 DW_CFA_def_cfa_register(6) ; CFA = [R6] + <fsize>/4 (4) fde+28 DW_CFA_advance_loc(1) ; foo+20 fde+29 DW_CFA_offset(4,3) ; R4 = CFA - 3 (4) fde+31 DW_CFA_advance_loc(11) ; foo+64 fde+32 DW_CFA_restore(4) ; R4 回復至調用前的狀態。 fde+33 DW_CFA_advance_loc(1) ; foo+68 fde+34 DW_CFA_restore(6) ; R6 回復至調用前的狀態。 fde+35 DW_CFA_def_cfa_register(7) ; R6 回復至 old R6。當前棧底由 R6 改為 R7。 fde+37 DW_CFA_advance_loc(1) ; foo+68 fde+38 DW_CFA_restore(8) ; R8 回復至調用前的狀態。 fde+39 DW_CFA_advance_loc(1) ; foo+72 fde+40 DW_CFA_def_cfa_offset(0) ; CFA = [R7]+0
.debug_frame
和 .eh_frame
,後者用於支持 C++ 的例外處理。.cfi_def_cfa register, offset
.cfi_def_cfa_register register
.cfi_def_cfa_offset offset
.cfi_offset register, offset
$ gcc -g test.c -S -o test.s main: .LFB0: .file 1 "test.c" .loc 1 1 0 .cfi_startproc pushq %rbp .cfi_def_cfa_offset 16 ; (a) .cfi_offset 6, -16 ; (b) movq %rsp, %rbp .cfi_def_cfa_register 6 ; (c) .loc 1 1 0 movl $0, %eax popq %rbp .cfi_def_cfa 7, 8 ; (d) ret .cfi_endproc .LFE0: .size main, .-main $ gcc -g test.c $ objdump -d --start-address=0x5fa --stop-address=0x605 a.out 00000000000005fa <main>: 5fa: 55 push %rbp 5fb: 48 89 e5 mov %rsp,%rbp 5fe: b8 00 00 00 00 mov $0x0,%eax 603: 5d pop %rbp 604: c3 retq $ objdump --dwarf=frames a.out 00000030 0000000000000014 00000000 CIE Version: 1 Augmentation: "zR" Code alignment factor: 1 Data alignment factor: -8 Return address column: 16 Augmentation data: 1b DW_CFA_def_cfa: r7 (rsp) ofs 8 DW_CFA_offset: r16 (rip) at cfa-8 DW_CFA_nop DW_CFA_nop 00000088 000000000000001c 0000005c FDE cie=00000030 pc=00000000000005fa..0000000000000605 DW_CFA_advance_loc: 1 to 00000000000005fb DW_CFA_def_cfa_offset: 16 (a) DW_CFA_offset: r6 (rbp) at cfa-16 (b) DW_CFA_advance_loc: 3 to 00000000000005fe DW_CFA_def_cfa_register: r6 (rbp) (c) DW_CFA_advance_loc: 6 to 0000000000000604 DW_CFA_def_cfa: r7 (rsp) ofs 8 (d) DW_CFA_nop DW_CFA_nop DW_CFA_nop
以下描述除錯器內部資料結構。
the main strength of hardware breakpoints is that you can use them to halt on non-execution accesses to memory locations.