林廣棟 黃光紅 耿銳
摘要:DWARF格式是一種常見的調(diào)試信息格式,它以節(jié)點作為存儲調(diào)試信息的基本單元。BWDSP系列芯片的調(diào)試系統(tǒng)使用一種自主可控的算法分析C語言的DWARF調(diào)試信息。該方法首先讀取.debug_abbrev節(jié)區(qū),獲得節(jié)點的縮略信息。然后讀取.debug_info節(jié)區(qū),獲取調(diào)試信息節(jié)點屬性的取值,并把這些調(diào)試信息存儲為內(nèi)部數(shù)據(jù)結(jié)構(gòu)。該算法已經(jīng)在BWDSP系列芯片的調(diào)試系統(tǒng)中得到成功的使用,實踐驗證了其可行性與正確性。
關(guān)鍵詞:DWARF;調(diào)試信息;調(diào)試系統(tǒng);BWDSP
中圖分類號:TP368.1 文獻標(biāo)識碼:A 文章編號:1009-3044(2014)25-5825-09
A Method to Analyze DWARF Format C Language Debugging Information
LIN Guang-dong, HUANG Guang-hong, GENG Rui
(NO. 38th Research Institute of China Electronic Technology Group Corporation, Hefei 230088, China)
Abstract: DWARF format is a widely used debugging information format. It uses entries as basic element to store debugging information. The debugger system of BWDSP develops an innovative algorithm to analyze DWARF debugging information of C language. Firstly, the algorithm read .debug_abbrev section to retrieve abbreviation information of entry. Then the algorithm analyzes .debug_info section to get value of attribute of entries, and finally stores the extracted information in local data structures. The algorithm has been applied successfully in debugger system of BWDSP and has been proved to be applicable and corrective.
Key words: DWARF; debugging information; debugger system; BWDSP
BWDSP系列芯片是中國電子科技集團公司第38所自主研發(fā)的一系列高性能通用DSP,包括單核、雙核等多個型號,受國家十二五“核高基”科技專項支持。BWDSP系列芯片擁有自主開發(fā)的調(diào)試系統(tǒng),該調(diào)試系統(tǒng)使用自主開發(fā)的算法分析調(diào)試信息。該調(diào)試系統(tǒng)支持的調(diào)試信息格式包括DWARF、STABS等等,
DWARF是一種常用的調(diào)試信息格式,它包括DWARF1、DWARF2、DWARF3三個版本。其中DWARF2格式是使用最廣泛,定義最標(biāo)準(zhǔn)的DWARF格式。BWDSP芯片調(diào)試系統(tǒng)支持對DWARF2版本調(diào)試信息的分析。
DWARF格式中,行號調(diào)試信息在.debug_line節(jié)區(qū)存儲,而高級語言的源文件、函數(shù)、變量、類型等調(diào)試信息在.debug_info節(jié)區(qū)中存儲。該文主要介紹對高級語言調(diào)試信息的分析,提出的算法主要針對C語言調(diào)試信息。
.debug_info節(jié)區(qū)中,調(diào)試信息以節(jié)點的形式存在。節(jié)點可以存儲一個源文件的調(diào)試信息、一個變量的調(diào)試信息、一個函數(shù)的調(diào)試信息等等。節(jié)點之間存在兄弟或父子的關(guān)系,一個源文件的調(diào)試信息節(jié)點形成一個調(diào)試信息樹。若被調(diào)試文件由多個源文件聯(lián)合編譯生成,則.debug_info節(jié)區(qū)會包含多個源文件的調(diào)試信息,這些源文件的調(diào)試信息樹構(gòu)成一個調(diào)試信息森林?jǐn)?shù)據(jù)結(jié)構(gòu)。
.debug_info節(jié)區(qū)中的節(jié)點有不同的類型和格式,但大多數(shù)節(jié)點的類型和格式是相同的,為了節(jié)省存儲空間,DWARF在.debug_abbrev節(jié)區(qū)中定義了所有節(jié)點的類型和格式。.debug_info節(jié)區(qū)中存儲節(jié)點調(diào)試信息時,只需要引用.debug_abbrev節(jié)區(qū)中存儲的節(jié)點類型格式等信息即可,然后只存儲節(jié)點的取值即可。所以,分析.debug_abbrev節(jié)區(qū)是分析.debug_info節(jié)區(qū)的先決條件。
本文將詳細(xì)介紹DWARF格式C語言調(diào)試信息的格式以及在BWDSP系列芯片中應(yīng)用的一種自主可控的解析方法和存儲數(shù)據(jù)結(jié)構(gòu)。
1 DWARF格式C語言調(diào)試信息簡介
DWARF格式高級語言調(diào)試信息以樹的形式存在,所有調(diào)試信息都可以用樹中的節(jié)點表示。一個節(jié)點用一個整數(shù)來表示其類型。DWARF2中預(yù)定義的節(jié)點類型有如表1所示。
每個節(jié)點表示一種調(diào)試信息。例如,DW_TAG_compile_unit表示源文件調(diào)試信息,DW_TAG_variable表示變量,DW_TAG_subprogram表示函數(shù),DW_TAG_typedef表示自定義類型等等。每個節(jié)點有若干組屬性,這些屬性描述該節(jié)點的特點。例如,常見的DW_TAG_compile_unit節(jié)點的屬性及屬性的格式如圖1所示。該節(jié)點描述源文件的調(diào)試信息,每個源文件都有一個該類型的節(jié)點描述。
圖1 DW_TAG_compile_unit類型節(jié)點的屬性及屬性的格式
每個預(yù)定義的TAG具有的屬性并不是固定的。例如,常見的節(jié)點DW_TAG_compile_unit具有上述幾種屬性,但對不同的源文件DW_TAG_compile_unit節(jié)點也可以具有其他屬性。所以,在表示一個節(jié)點的調(diào)試信息時,必須把它具有什么屬性也表示出來。DWARF2中預(yù)定義的屬性值如表2所示。
表2 DWARF格式常見屬性類型及屬性格式
[屬性名\&屬性含義\&屬性常用格式\&DW_AT_name\&節(jié)點描述的調(diào)試信息的名字,如文件名、函數(shù)名、變量名、類型名等等。\&DW_FORM_string\&DW_AT_high_pc\&一段程序的開始地址\&DW_FORM_addr\&DW_AT_low_pc\&一段程序的結(jié)束地址\&DW_FORM_addr\&DW_AT_encoing\&基類型的編碼形式\&DW_FORM_data1\&DW_AT_decl_file\&節(jié)點描述的調(diào)試信息定義的文件號\&DW_FORM_data1\&DW_AT_decl_line\&節(jié)點描述的調(diào)試信息定義的行號\&DW_FORM_data1
DW_FORM_data2
DW_FORM_data4\&DW_AT_type\&類型索引\&DW_FORM_ref4\&DW_AT_sibling\&兄弟節(jié)點的位置\&DW_FORM_ref4\&DW_AT_byte_size\&調(diào)試信息以字節(jié)為單位的大小,如類型的大小、變量的大小等等\&DW_FORM_data1\&DW_AT_bit_size\&調(diào)試信息以位為單位的大小,如結(jié)構(gòu)體元素的大?。?DW_FORM_data1\&DW_AT_bit_offset\&調(diào)試信息以位為單位的位置,如結(jié)構(gòu)體元素的位置\&DW_FORM_data1\&DW_AT_comp_dir\&源文件的編譯目錄,這里指編譯器編譯源文件時的工作目錄\&DW_FORM_string\&]
節(jié)點的每種屬性都要有一個確定的取值,該屬性的取值可以有多種格式,有4字節(jié)整數(shù)、2字節(jié)整數(shù)、字符串、LEB128數(shù)、節(jié)區(qū)內(nèi)的偏移值等等。LEB128數(shù)是DWARF定義的一種壓縮存儲數(shù)據(jù)方式,它可以描述任意范圍的數(shù)據(jù),但又可以占用較少的空間。DWARF2中屬性的常見格式如表3所示。
表3 DWARF格式中屬性常用格式及其內(nèi)部存儲結(jié)構(gòu)
[格式名\&格式含義\&存儲該格式的數(shù)據(jù)結(jié)構(gòu)類型\&DW_FORM_addr\&32位絕對地址\&unsigned int\&DW_FORM_block1\&首先是一個1字節(jié)的長度信息,其后則是長度在0-255字節(jié)之間的二進制信息塊,該信息塊的長度由第一個字節(jié)指定\&shared_ptr
unsigned int len;\&DW_FORM_block2\&首先是一個2字節(jié)的長度信息,其后則是長度在0-65535字節(jié)之間的二進制信息塊,該信息塊的長度由前2個字節(jié)指定\&shared_ptr
unsigned int len;\&DW_FORM_block4\&首先是一個4字節(jié)的長度信息,其后則是連續(xù)的二進制信息塊,該信息塊的長度由前4個字節(jié)指定\&shared_ptr
unsigned int len;\&DW_FORM_data1\&1字節(jié)大小的無符號立即數(shù)\&unsigned char\&DW_FORM_data2\&2字節(jié)大小的無符號立即數(shù)\&unsigned short\&DW_FORM_data4\&4字節(jié)大小的無符號立即數(shù)\&unsigned int\&DW_FORM_string\&以\0字符結(jié)束的字符串\&string\&DW_FORM_ref4\&4字節(jié)大小的立即數(shù),表示相對于該源文件調(diào)試信息節(jié)點的偏移\&unsigned int\&DW_FORM_sdata\&有符號型LEB128數(shù)\∫\&DW_FORM_udata\&無符號型LEB128數(shù)\&unsigned int\&]
除屬性外,節(jié)點還有一個默認(rèn)的屬性:是否有子節(jié)點。一般,調(diào)試信息按其從屬關(guān)系確定子節(jié)點。例如源文件調(diào)試信息DW_TAG_compile_unit節(jié)點的子節(jié)點是該源文件中的函數(shù)調(diào)試信息DW_TAG_subprogram節(jié)點。DW_TAG_subprogram節(jié)點的子節(jié)點包括函數(shù)參數(shù)、在該函數(shù)中定義的局部變量等等。DW_TAG_compile_unit節(jié)點的子節(jié)點還包括在該源文件中定義的全局變量節(jié)點、類型節(jié)點等等。不同類型節(jié)點之間的從屬關(guān)系見表4。
表4 DWARF格式中常見的父子節(jié)點類型
[節(jié)點類型\&可能的子節(jié)點類型\&DW_TAG_compile_unit\&DW_TAG_subprogram
DW_TAG_base_type
DW_TAG_pointer_type
DW_TAG_array_type
DW_TAG_structure_type
DW_TAG_typedef
DW_TAG_variable\&DW_TAG_structure_type\&DW_TAG_member\&DW_TAG_subprogram\&DW_TAG_formal_parameter
DW_TAG_variable
DW_TAG_pointer_type
DW_TAG_array_type
DW_TAG_structure_type
DW_TAG_typedef
DW_TAG_lexical_block\&DW_TAG_array_type\&DW_TAG_subrange_type\&]
一般,一個被調(diào)試文件的調(diào)試信息中有很多節(jié)點,這些節(jié)點的類型大多數(shù)是相同的。例如,若一個可執(zhí)行文件由若干個源文件編譯而成,其調(diào)試信息中必然有若干個DW_TAG_compile_unit節(jié)點,這些節(jié)點的屬性類型、屬性的格式一般是相同的。又比如,一般一個源文件中會有若干個函數(shù),則該源文件調(diào)試信息的DW_TAG_compile_unit節(jié)點必然有很多子節(jié)點是DW_TAG_subprogram節(jié)點,這些子節(jié)點的屬性類型、屬性格式也相同。如果把這些相同類型節(jié)點的屬性類型及其格式都顯式地表示出來,會造成很多存儲空間的浪費。因此,DWARF2規(guī)定,所有節(jié)點的類型、屬性類型、屬性格式、是否有子節(jié)點等信息都定義在.debug_abbrev節(jié)區(qū)內(nèi)。而.debug_info節(jié)區(qū)內(nèi)只記錄各節(jié)點的屬性的具體取值。.debug_abbrev節(jié)區(qū)中定義的節(jié)點類型、屬性類型等信息稱為節(jié)點的縮略信息。例如,對上述DW_TAG_compile_unit節(jié)點,該節(jié)點的類型、屬性的類型、屬性的格式等信息存儲在.debug_abbrev節(jié)區(qū)內(nèi)。屬于該類型的節(jié)點可以有若干個,它們各屬性的具體取值存儲在.debug_info節(jié)區(qū)中。圖1是一個DW_TAG_compile_unit類型節(jié)點及其屬性類型、屬性格式在.debug_abbrev節(jié)區(qū)中的定義,而圖2則是該類型節(jié)點在.debug_info節(jié)區(qū)中的具體取值。
圖2 DW_TAG_compile_unit在.debug_info節(jié)區(qū)中的具體取值
2 DWARF格式調(diào)試信息分析方法
2.1 .debug_abbrev節(jié)區(qū)分析方法
根據(jù)DWARF格式調(diào)試信息的特點,讀取DWARF調(diào)試信息的第一步是讀取.debug_abbrev節(jié)區(qū)內(nèi)各節(jié)點的縮略信息。首先定義讀取.debug_abbrev節(jié)區(qū)需要的數(shù)據(jù)結(jié)構(gòu)。下面是存儲屬性類型及屬性格式的數(shù)據(jù)結(jié)構(gòu)。
typedef struct _AttAbbrev
{
unsigned int at;//屬性類型
unsigned int form;//屬性格式
}AttAbbrev;
AttAbbrev結(jié)構(gòu)體可以存儲一個屬性的類型及格式信息。在此基礎(chǔ)上,定義存儲節(jié)點縮略信息的數(shù)據(jù)結(jié)構(gòu)。
typedef struct _TagAbbrev
{
unsigned int abbCode;//節(jié)點縮略信息編號
unsigned int tag;//節(jié)點類型
unsigned char haschildren;//該節(jié)點是否有子節(jié)點
vector
}TagAbbrev;
TagAbbrev結(jié)構(gòu)體可以存儲節(jié)點的類型、屬性類型、屬性的格式、有無子節(jié)點等信息。
typedef struct _CompilationUnitTags
{
unsigned int offset;//一個源文件的所有節(jié)點的縮略信息在.debug_abbrev節(jié)區(qū)中的偏移
vector
}CompUnitTags;
CompUnitTags結(jié)構(gòu)體可以存儲一個源文件的所有節(jié)點縮略信息。
在.debug_abbrev節(jié)區(qū)中,節(jié)點類型、屬性類型、屬性格式類型都以無符號LEB128數(shù)的格式存儲。它們存儲順序依次為:節(jié)點的縮略信息編號、節(jié)點的類型、若干組屬性類型和屬性格式類型定義。若一組屬性類型和屬性格式類型都為0,標(biāo)志著一個節(jié)點的屬性信息結(jié)束。每個源文件的節(jié)點縮略信息連續(xù)存儲,節(jié)點縮略信息編號從1開始,連續(xù)遞增。不同的源文件也可以共享節(jié)點縮略信息。若一個節(jié)點的縮略信息編號為0,標(biāo)志著一個源文件的節(jié)點縮略信息的結(jié)束。.debug_abbrev節(jié)區(qū)中的信息結(jié)構(gòu)如圖3示意。
分析.debug_abbrev節(jié)區(qū)的算法流程圖如圖4所示。
2.2 .debug_info節(jié)區(qū)分析方法
一個節(jié)點的屬性及其值用如下數(shù)據(jù)結(jié)構(gòu)表示。
typedef struct _EntryAtt
{
unsigned int att;//屬性標(biāo)識,標(biāo)志著屬性的類型,如DW_AT_name,DW_AT_type等等
unsigned int form;//屬性格式,標(biāo)志著屬性的形式,如DW_FORM_data1等等
//以下為屬性內(nèi)容,這幾種形式的屬性內(nèi)容只能取其一。
string str;//字符串形式的屬性
shared_ptr
unsigned int len;//數(shù)據(jù)塊屬性長度。block和len必須同時賦值。
unsigned long long u8;//存儲64位無符號數(shù)
long long i8;//存儲64位有符號數(shù)
unsigned int u4;//存儲32位無符號數(shù)
unsigned short u2;//存儲16位無符號數(shù)
unsigned char u1;//存儲8位無符號數(shù)
}EntryAtt;
其中shared_ptr
EntryAtt數(shù)據(jù)結(jié)構(gòu)可以存儲一個屬性的類型、格式及取值。由于該結(jié)構(gòu)體需要存儲各種格式的屬性值,所以它必須支持存儲所有DWARF2可能屬性格式。各種屬性格式的存儲數(shù)據(jù)結(jié)構(gòu)見表3。但由于一個屬性的格式只有一種,所以該結(jié)構(gòu)體中存儲屬性值的元素只能使用一個。
存儲一個節(jié)點的調(diào)試信息的數(shù)據(jù)結(jié)構(gòu)如下:
typedef struct _Entry
{unsigned int uCpIdx;//該節(jié)點調(diào)試信息所在源文件的編號索引
unsigned int uEtIdx;//該節(jié)點調(diào)試信息在該源文件所有節(jié)點中的編號索引
unsigned int tag;//標(biāo)志節(jié)點的類型,如DW_TAG_variable等等
unsigned int offset;//該節(jié)點調(diào)試信息在.debug_info節(jié)區(qū)中的偏移
unsigned int depth;//該節(jié)點在DWARF格式調(diào)試信息樹中的深度
unsigned int abbrevCode;//節(jié)點縮略信息號
bool done;//標(biāo)志一個節(jié)點是否已經(jīng)被分析過
bool hasChildren;//是否有孩子
vector
}Entry;
Entry數(shù)據(jù)結(jié)構(gòu)中,除DWARF格式中定義的一些節(jié)點調(diào)試信息外,還有一些幫助分析調(diào)試信息的元素,如uCpIdx、uEtIdx、depth、done等等。其中uCpIdx記錄該節(jié)點所屬源文件調(diào)試信息的索引號,uEtIdx是該節(jié)點在其所在源文件中的索引號,depth表示該節(jié)點在DWARF調(diào)試信息樹中的深度,done標(biāo)記該節(jié)點是否已經(jīng)被分析完成,offset標(biāo)志該節(jié)點在.debug_info節(jié)區(qū)中的偏移。
一個源文件的所有節(jié)點調(diào)試信息記錄在一個數(shù)據(jù)結(jié)構(gòu)中:
typedef struct _CompUnit
{unsigned int offset;//一個源文件的調(diào)試信息在.debug_info節(jié)區(qū)的開始位置
unsigned int endoffset;//一個源文件的調(diào)試信息在.debug_info節(jié)區(qū)的結(jié)束位置
unsigned int abbOffset;//一個源文件的節(jié)點縮略信息在.debug_abbrev節(jié)區(qū)中的位置
unsigned int len;// 一個源文件的調(diào)試信息在.debug_info節(jié)區(qū)的長度
unsigned short version; //調(diào)試信息版本
unsigned char potSize;//程序中地址的大小,32位計算機中為4,64位計算機中為8
vector
}CompUnit;
同樣,源文件的調(diào)試信息數(shù)據(jù)結(jié)構(gòu)CompUnit中,除DWARF格式規(guī)定的信息外,還有一些為方便解析調(diào)試信息而加入的額外信息,如offset、endoffset。Offset表示一個源文件的節(jié)點調(diào)試信息在.debug_info節(jié)區(qū)中的開始位置,endoffset表示一個源文件的節(jié)點調(diào)試信息在.debug_info節(jié)區(qū)中的結(jié)束位置。
.debug_info節(jié)區(qū)中,源文件的節(jié)點調(diào)試信息是連續(xù)存放的。例如,若一個可執(zhí)行文件由a.c和b.c編譯而成,則a.c的調(diào)試信息在.debug_info中存放為連續(xù)的二進制塊,b.c的調(diào)試信息也存儲為連續(xù)的二進制塊,兩者不會交叉。
每個源文件的調(diào)試信息以一個源文件頭開始。該源文件頭不是節(jié)點,其格式與節(jié)點的格式不同。源文件頭按順序包含如下信息:
1) 一個4字節(jié)的長度信息len,該長度表示.debug_info節(jié)區(qū)中為該源文件產(chǎn)生的調(diào)試信息的長度,這里的長度不包含存放該長度信息的4字節(jié);
2) 一個2字節(jié)的版本信息,該版本信息該源文件調(diào)試信息的DWARF格式版本號;
3) 一個4字節(jié)的偏移,該偏移指該源文件調(diào)試信息的節(jié)點縮略信息在.debug_abbrev節(jié)區(qū)中的位置;
4) 一個1字節(jié)的地址大小。例如,對于32位計算機,該值為4,對于64位計算機,該值為8。
源文件調(diào)試信息頭僅僅給出與該源文件與DWARF存儲格式有關(guān)的一些信息。對每一個源文件,都會有一個DW_TAG_compile_unit類型節(jié)點,該節(jié)點會給出關(guān)于該節(jié)點更詳細(xì)的調(diào)試信息。一般,DW_TAG_compile_unit節(jié)點是該源文件調(diào)試信息的第一個節(jié)點,并且是該源文件DWARF調(diào)試信息節(jié)點樹的根節(jié)點。
源文件調(diào)試信息頭之后就是一系列的DWARF調(diào)試信息節(jié)點值。每個節(jié)點以一個LEB128數(shù)開始,這個數(shù)記錄該節(jié)點的縮略信息編號。調(diào)試信息分析程序可以從.debug_abbrev節(jié)區(qū)中根據(jù)該縮略信息編號找到該節(jié)點類型、屬性類型、屬性格式等信息。該LEB128縮略信息編號之后就是該節(jié)點各屬性的取值。若該節(jié)點縮略信息中記錄該節(jié)點有子節(jié)點,則.debug_info節(jié)區(qū)中此后的節(jié)點為該節(jié)點的子節(jié)點,這些節(jié)點在調(diào)試信息樹中的深度比本節(jié)點深一層。若遇到一個節(jié)點的縮略信息編號為0,表示子節(jié)點結(jié)束,此后的節(jié)點在調(diào)試信息樹的上一層。.debug_info節(jié)區(qū)的內(nèi)容格式如圖5示意。
.debug_info節(jié)區(qū)分析算法流程如圖6所示。
3 實驗結(jié)果
DWDSP系列芯片調(diào)試系統(tǒng)的DWARF調(diào)試信息分析算法在分析調(diào)試信息的過程中,不斷通過向日志文件中寫入調(diào)試信息來記錄每一步的數(shù)據(jù)。通過這種方法可以驗證解析過程中每一步的正確性,方便追蹤錯誤調(diào)試信息的來源。當(dāng)所有調(diào)試信息都得到解析并處理完成后,輸出獲得的最終調(diào)試信息,與源代碼進行驗證,可以驗證調(diào)試信息分析算法的正確性。如下為一個示例性的源文件。
int fun(int a)
{return a*a;
}
int main()
{typedef struct _stu
{int a;
char * b;
}stu;
char c='z';
stu s;
s.a=10;
s.b=&c;
return fun(s.a);
}
經(jīng)過調(diào)試信息解析算法分析處理后,得到的最終調(diào)試信息如圖5所示??梢姡馕龅玫降淖罱K調(diào)試信息與源代碼一致,算法處理過程正確。該文提出的解析算法經(jīng)過大量源代碼測試,已經(jīng)證實該算法是正確有效的算法。
4 結(jié)束語
本文介紹了高級語言的DWARF2格式調(diào)試信息。該文還著重介紹了BWDSP系列芯片調(diào)試系統(tǒng)使用的DWARF格式調(diào)試信息分析算法。該算法首先分析.debug_abbrev節(jié)區(qū),讀取調(diào)試信息節(jié)點的縮略信息。然后該算法分析.debug_info節(jié)區(qū),讀取各節(jié)點的值。該文還介紹了該算法使用的主要數(shù)據(jù)結(jié)構(gòu)。該算法在BWDSP芯片的調(diào)試系統(tǒng)中已經(jīng)得到應(yīng)用,經(jīng)過實際使用,該算法的正確性已經(jīng)得到確保。該算法還通過輸出日志的方式驗證分析過程每一步的正確性。通過日志文件最后一步輸出的調(diào)試信息,可以驗證該算法的正確性。
參考文獻:
[1] Unix International Programming Languages SIG. DWARF Debugging Information Format. UNIX International.
[2] Julia Menapace, Jim Kingdon, David MacKenzie. The “stabs” debug format. Free Software Foundation.
[3] 黃光紅,劉冠南.可配置多核處理器的調(diào)試器模塊化分層設(shè)計[J].單片機與嵌入式系統(tǒng)應(yīng)用,2014,14(7):13-15.
[4] 余鋒林,劉小明,朱艷,鮑華.BWDSP100集成開發(fā)環(huán)境設(shè)計與實現(xiàn)[J].中國集成電路,2012,21(6):25-29.