1. 程式人生 > >dwarf除錯資訊格式入門

dwarf除錯資訊格式入門

https://www.cnblogs.com/zqingnn/archive/2011/01/05/1926384.html

一個程式的完成過程一般是編碼、編譯、執行的過程,當然這是一個理想的過程,所有的開發幾乎都不可能是一帆風順的,總會有些意想不到的錯誤,這時便需要除錯,良好的偵錯程式應該每一個程式設計師的必備。

那麼偵錯程式使用的除錯資訊是從哪裡來的呢?答案簡單的很,是從編譯後的檔案中來的(注意這裡編譯的時候要使用特定的編譯選項,如VC使用debug模式,GCC使用”-g”)。在編譯的時候,編譯器會從原始檔中收集大量的資訊,例如變數名、變數型別、變數所在行號、函式名、函式引數、函式的地址範圍、行號和地址的對應關係等等,然後按照一種特定的格式寫入到編譯後的檔案中。除錯的時候,偵錯程式便從檔案中讀取並解析這些資訊,以產生人們可讀性比較強的資訊。簡單的說,除錯資訊就是在機器碼和對應的原始碼之間建立一座橋樑,大大方便和提高了除錯程式的能力。

除錯資訊一般都是按照什麼樣的格式存放的呢?主要有下面幾種:stabs,COFF,PE-COFF,OMF,IEEE-695和DWARF。其中DWARF在Linux中被普遍使用,我們主要分析它。

DWARF的全稱是"Debugging With Attributed Record Formats",遵從GNU FDL授權。現在已經有dwarf1,dwarf2,dwarf3三個版本。

Dwarf最初被貝爾實驗室設計用來供Unix System V的sdb偵錯程式使用,並且在1989年被Unix國際化部門的PLSIG (Programming Languages Special Interest Group)標準化成為dwarf1.0。但是dwarf1有著很多明顯的缺點,於是PLSIG繼續開發,改正了缺點,並加入了對C++等語言的支援,並在1990年正式公佈了dwarf2的標準草案。但是稍後由於一些原因,PLSIG被解散,dwarf的開發陷入到多個並不合作的組織中間,造成dwarf2的一些實現細節要取決於特定的編譯器。這種情況一直持續到1999年,開發工作受到了來自實現對HP/Inter IA-64架構提供較好支援的推動,成立了dwarf委員會,dwarf的原作者擔任負責人,

開始了dwarf3的開發,並於2006年1月份推出dwarf3.0,同時為了解決分歧,dwarf委員會加入了自由標準組織,在自由標準組織與來自Linux基金會的OSDL(Open Source Development Labs)合併後,dwarf重返獨立狀態並建立了自己的網站:dwarfstd.org

這三個版本中,dwarf2對dwarf1的改變很大,dwarf3大多是對dwarf2的擴充。

現在dwarf已經是一種獨立的標準,可以支援C、C++、JAVA、Fortran等語言。

在瞭解了dwarf的歷史之後,來看一下如何檢視dwarf所包含的除錯資訊內容,並在下一篇文章中介紹這些內容的具體意思。檢視內容的工具常用的有四種:

1. readelf

GNU提供的二進位制工具,功能很多,並不限於讀dwarf資訊

2. gdb

這個就不用多說了吧,^_^

3. drawfdump

是一個被打包在libdwarf內的程式

4. libdwarf

是一個封裝好的C庫API,用來讀取dwarf資訊

在這裡我們主要使用readelf工具。

先寫一個簡單的C程式,如下:

1:  
2: int add(int, int);
3:  
4: int main()
5: ...{
6: int i, j;
7:  
8: for(i = 0; i < 100; i += 5, j = i * 5)
9: add(i, j);
10:  
11: return 0;
12: }
13:  
14: int add(int a, int b)
15: ...{
16: return a + b;
17: }

然後使用gcc –g hello.c –o hello編譯。生成hello檔案。

Hello檔案是elf格式的,elf一般由多個節(section)組成,不熟悉的可以看前面兩篇關於elf檔案格式的文章。除錯資訊被包含在某幾個節中,如果是用dwarf2格式編譯的,這些節的名字一般是以.debug開頭,如.debug_info,.debug_line,.debug_frame等,如果是用dwarf1格式編譯的,這些節的名字一般是.debug,.line等。現在的編譯器預設大多數是dwarf2格式編譯,當然可以通過gcc的編譯選項改變。

現在來看hello檔案都包含了哪些除錯資訊。

首先來看都包含了哪些除錯節,使用readelf –S hello命令,產生如下輸出(已刪一些無關內容):

[Nr] Name Type Addr Off Size 
 
[ 0] NULL 00000000 000000 000000 
 
[ 1] .text PROGBITS 00008000 008000 0006c4 
 
[ 2] .ARM.exidx ARM_EXIDX 000086c4 0086c4 000008 
 
[ 3] .data PROGBITS 000086d0 0086d0 000520 
 
[ 4] .bss NOBITS 00008bf0 008bf0 000020 
 
[ 5] .debug_aranges PROGBITS 00000000 008bf0 000020 
 
[ 6] .debug_pubnames PROGBITS 00000000 008c10 000023 
 
[ 7] .debug_info PROGBITS 00000000 008c33 0000cc 
 
[ 8] .debug_abbrev PROGBITS 00000000 008cff 00006b 
 
[ 9] .debug_line PROGBITS 00000000 008d6a 00003e 
 
[10] .debug_frame PROGBITS 00000000 008da8 000188 
 
[11] .debug_loc PROGBITS 00000000 008f30 000054 
 
[12] .ARM.attributes ARM_ATTRIBUTES 00000000 008f84 000010 
 
[13] .comment PROGBITS 00000000 008f94 000032 
 
[14] .shstrtab STRTAB 00000000 008fc6 0000ad 
 
[15] .symtab SYMTAB 00000000 00931c 000780 
 
[16] .strtab STRTAB 00000000 009a9c 000416
 

可見一共包含了17個節,其中7個除錯資訊的節。

在來看一下各個除錯資訊節包含的內容,使用readelf –w* hello命令,*是除錯節名的第一個字母,如-wi就是檢視.debug_info節的內容,-wl就是檢視.debug_line節的內容。

對於一個除錯檔案,.debug_info和.debug_line節是必須有的,其他的不見得。同時也可以自己寫連結指令碼實現對所有節(不侷限於除錯節)的控制,如指定每個節的基址等。

.debug_info基本包含了一個原始檔內部的大部分資訊,如函式、引數、變數、型別等等,我們看一下它的輸出:

The section .debug_info contains:
 
Compilation Unit @ offset 0x0:
 
Length: 200
 
Version: 2
 
Abbrev Offset: 0
 
Pointer Size: 4
 
<0><b>: Abbrev Number: 1 (DW_TAG_compile_unit)
 
DW_AT_stmt_list : 0 
 
DW_AT_high_pc : 0x8248 
 
DW_AT_low_pc : 0x81ac 
 
DW_AT_producer : GNU C 4.1.1 
 
DW_AT_language : 1 (ANSI C)
 
DW_AT_name : hello.c 
 
DW_AT_comp_dir : C:\Program Files\CodeSourcery\Sourcery G++\bin 
 
<1><5c>: Abbrev Number: 2 (DW_TAG_subprogram)
 
DW_AT_sibling : <92> 
 
DW_AT_external : 1 
 
DW_AT_name : main 
 
DW_AT_decl_file : 1 
 
DW_AT_decl_line : 5 
 
DW_AT_type : <92> 
 
DW_AT_low_pc : 0x81ac 
 
DW_AT_high_pc : 0x8214 
 
DW_AT_frame_base : 0 (location list)
 
<2><79>: Abbrev Number: 3 (DW_TAG_variable)
 
DW_AT_name : i 
 
DW_AT_decl_file : 1 
 
DW_AT_decl_line : 6 
 
DW_AT_type : <92> 
 
DW_AT_location : 2 byte block: 91 68 (DW_OP_fbreg: -24)
 
<2><85>: Abbrev Number: 3 (DW_TAG_variable)
 
DW_AT_name : j 
 
DW_AT_decl_file : 1 
 
DW_AT_decl_line : 6 
 
DW_AT_type : <92> 
 
DW_AT_location : 2 byte block: 91 6c (DW_OP_fbreg: -20)
 
<1><92>: Abbrev Number: 4 (DW_TAG_base_type)
 
DW_AT_name : int 
 
DW_AT_byte_size : 4 
 
DW_AT_encoding : 5 (signed)
 
<1><99>: Abbrev Number: 5 (DW_TAG_subprogram)
 
DW_AT_external : 1 
 
DW_AT_name : add 
 
DW_AT_decl_file : 1 
 
DW_AT_decl_line : 15 
 
DW_AT_prototyped : 1 
 
DW_AT_type : <92> 
 
DW_AT_low_pc : 0x8214 
 
DW_AT_high_pc : 0x8248 
 
DW_AT_frame_base : 0x2a (location list)
 
<2><b2>: Abbrev Number: 6 (DW_TAG_formal_parameter)
 
DW_AT_name : a 
 
DW_AT_decl_file : 1 
 
DW_AT_decl_line : 14 
 
DW_AT_type : <92> 
 
DW_AT_location : 2 byte block: 91 6c (DW_OP_fbreg: -20)
 
<2><be>: Abbrev Number: 6 (DW_TAG_formal_parameter)
 
DW_AT_name : b 
 
DW_AT_decl_file : 1 
 
DW_AT_decl_line : 14 
 
DW_AT_type : <92> 
 
DW_AT_location : 2 byte block: 91 68 (DW_OP_fbreg: -24)

 

.debug_line包含了所有地址和原始檔行的對應資訊,內容如下:

 

Dump of debug contents of section .debug_line:
 
Length: 58
 
DWARF Version: 2
 
Prologue Length: 30
 
Minimum Instruction Length: 2
 
Initial value of 'is_stmt': 1
 
Line Base: -5
 
Line Range: 14
 
Opcode Base: 13
 
Opcodes:
 
Opcode 1 has 0 args
 
Opcode 2 has 1 args
 
Opcode 3 has 1 args
 
Opcode 4 has 1 args
 
Opcode 5 has 1 args
 
Opcode 6 has 0 args
 
Opcode 7 has 0 args
 
Opcode 8 has 0 args
 
Opcode 9 has 1 args
 
Opcode 10 has 0 args
 
Opcode 11 has 0 args
 
Opcode 12 has 1 args
 
The Directory Table is empty.
 
The File Name Table:
 
Entry Dir Time Size Name
 
1 0 0 0 hello.c
 
Line Number Statements:
 
Extended opcode 2: set Address to 0x81ac
 
Special opcode 9: advance Address by 0 to 0x81ac and Line by 4 to 5
 
Special opcode 120: advance Address by 16 to 0x81bc and Line by 3 to 8
 
Special opcode 90: advance Address by 12 to 0x81c8 and Line by 1 to 9
 
Special opcode 88: advance Address by 12 to 0x81d4 and Line by -1 to 8
 
Advance PC by constant 34 to 0x81f6
 
Special opcode 78: advance Address by 10 to 0x8200 and Line by 3 to 11
 
Special opcode 34: advance Address by 4 to 0x8204 and Line by 1 to 12
 
Special opcode 120: advance Address by 16 to 0x8214 and Line by 3 to 15
 
Special opcode 174: advance Address by 24 to 0x822c and Line by 1 to 16
 
Special opcode 90: advance Address by 12 to 0x8238 and Line by 1 to 17
 
Advance PC by 16 to 0x8248
 
Extended opcode 1: End of Sequence