解析基于ELF的嵌入式軟件源碼級(jí)交叉調(diào)試技術(shù)
開發(fā)任何一個(gè)軟件都不可避免地存在各種錯(cuò)誤,要修正錯(cuò)誤必須找出其錯(cuò)誤原因。通常程序員利用調(diào)試器來跟蹤程序執(zhí)行情況,快速有效地定位錯(cuò)誤產(chǎn)生的位置從而找到引起錯(cuò)誤的原因,并改正錯(cuò)誤。
本文引用地址:http://www.ex-cimer.com/article/148626.htm調(diào)試器為用戶提供的主要功能包括:在目標(biāo)程序中設(shè)置、刪除斷點(diǎn);以單步執(zhí)行或連續(xù)執(zhí)行等方式控制目標(biāo)程序運(yùn)行;瀏覽程序中的變量或表達(dá)式的值;查看、修改目標(biāo)機(jī)寄存器的內(nèi)容;查看、修改目標(biāo)機(jī)內(nèi)存的內(nèi)容。源碼級(jí)調(diào)試器是面向高級(jí)語(yǔ)言的符號(hào)調(diào)試工具,它基于源代碼的語(yǔ)句和符號(hào)跟蹤觀察目標(biāo)程序,同時(shí)提供基于匯編級(jí)的程序跟蹤功能以滿足用戶底層的調(diào)試需要。通用計(jì)算機(jī)軟件一般在同一臺(tái)機(jī)器上進(jìn)行編輯、編譯、調(diào)試;而嵌入式軟件的目標(biāo)系統(tǒng)多為特殊的專用系統(tǒng),通常采用宿主機(jī)/目標(biāo)機(jī)開發(fā)環(huán)境,借助通用計(jì)算機(jī)作為編輯源文件的宿主機(jī),利用交叉編譯器在宿主機(jī)上編譯生成目標(biāo)機(jī)的可執(zhí)行代碼,調(diào)試時(shí)通過通訊介質(zhì)(串線或網(wǎng)絡(luò))將目標(biāo)代碼下載到目標(biāo)系統(tǒng)上運(yùn)行,利用交叉調(diào)試器進(jìn)行跟蹤調(diào)試。
一、源碼級(jí)交叉調(diào)試器的實(shí)現(xiàn)途徑
程序運(yùn)行過程中目標(biāo)程序的指令代碼和數(shù)據(jù)都映射到目標(biāo)機(jī)上相應(yīng)的內(nèi)存內(nèi)容,為了實(shí)現(xiàn)源碼級(jí)調(diào)試,利用目標(biāo)文件中在程序編譯鏈接時(shí)生成的調(diào)試信息來實(shí)現(xiàn)目標(biāo)程序與源程序之間的映射,從而在源碼級(jí)實(shí)現(xiàn)對(duì)程序執(zhí)行情況的控制和觀察。其關(guān)鍵在于找到調(diào)試控制點(diǎn)和數(shù)據(jù)在源程序與目標(biāo)程序之間的映射關(guān)系。
任何數(shù)據(jù)都有名和值兩個(gè)側(cè)面,數(shù)據(jù)名與數(shù)據(jù)值之間的映射關(guān)系為:根據(jù)數(shù)據(jù)名得到存放該數(shù)據(jù)值的內(nèi)存地址,再?gòu)哪繕?biāo)機(jī)的內(nèi)存地址取出其內(nèi)容即為數(shù)據(jù)值:
調(diào)試中的程序控制點(diǎn)通常為源程序中的函數(shù)、語(yǔ)句行等,它們對(duì)應(yīng)于裝載到目標(biāo)內(nèi)存中的相應(yīng)目標(biāo)代碼,要實(shí)現(xiàn)程序的運(yùn)行控制關(guān)鍵在于得到源代碼與目標(biāo)代碼之間的映射關(guān)系:由源碼定位信息得到相應(yīng)的目標(biāo)碼信息;由目標(biāo)碼地址得到相應(yīng)的源碼定位信息。源碼定位信息為源文件名+行號(hào)或函數(shù)名;目標(biāo)碼信息為目標(biāo)指令在目標(biāo)機(jī)內(nèi)存中的起始和終止地址。
嵌入式軟件以宿主機(jī)/目標(biāo)機(jī)模式開發(fā),其交叉調(diào)試器分為宿主機(jī)部分和目標(biāo)機(jī)部分,兩者以統(tǒng)一的通訊協(xié)議進(jìn)行通信,宿主機(jī)向目標(biāo)機(jī)發(fā)送命令,目標(biāo)機(jī)接收、執(zhí)行命令并將結(jié)果返回宿主機(jī),從而實(shí)現(xiàn)兩機(jī)之間的交互控制。免費(fèi)軟件基金會(huì)FSF提供的調(diào)試工具gdb具有一套比較成熟的通訊協(xié)議----remote通訊協(xié)議,該協(xié)議作為開放軟件被廣為采用,在此我們選擇了rmote協(xié)議作為交叉調(diào)試器的遠(yuǎn)程通訊協(xié)議。
二、ELF格式目標(biāo)文件
目標(biāo)文件是實(shí)現(xiàn)源碼級(jí)調(diào)試的基礎(chǔ),需要詳細(xì)分析文件的格式及內(nèi)容以從中獲取有用的調(diào)試信息。在設(shè)計(jì)調(diào)試器時(shí)采用可執(zhí)行連接格式DDELF格式目標(biāo)文件作為開發(fā)基礎(chǔ),ELF(Executable and Linking Format)是UNIX系統(tǒng)實(shí)驗(yàn)室(USL)作為應(yīng)用程序二進(jìn)制接口(Application Binary Interface(ABI))而開發(fā)和發(fā)布的,已被軟件業(yè)廣泛采用,在Linux系統(tǒng)中ELF格式是其默認(rèn)的目標(biāo)文件格式,許多嵌入式軟件都采用ELF格式作為目標(biāo)文件格式。
ELF目標(biāo)文件主要有三種類型:可重定位文件,可執(zhí)行文件,共享的目標(biāo)文件,我們以可執(zhí)行文件為分析對(duì)象。
ELF頭固定在文件的起始位置,其它各部分的位置由ELF頭及其它相關(guān)信息獲得。
1、ELF頭
ELF頭是整個(gè)文件的入口,具有固定的長(zhǎng)度,52個(gè)字節(jié),包含14個(gè)值。包括ELF文件標(biāo)識(shí),程序頭表和節(jié)頭表的位置、長(zhǎng)度,文件中段的數(shù)目和節(jié)的數(shù)目等信息。
2、程序頭表與段
程序頭表中有多個(gè)表項(xiàng),每個(gè)表項(xiàng)是一個(gè)程序段的信息,固定長(zhǎng)度為32個(gè)字節(jié),包含8個(gè)值,包括段在文件中的位置,段在內(nèi)存中的起始虛擬地址,段的長(zhǎng)度及其它屬性等。調(diào)試器根據(jù)程序頭表中的信息來確定需要下載到目標(biāo)機(jī)上的目標(biāo)文件內(nèi)容(指令與數(shù)據(jù))及其在目標(biāo)機(jī)中的內(nèi)存地址。
3、節(jié)頭表與節(jié)
節(jié)頭表中也有多個(gè)表項(xiàng),每個(gè)表項(xiàng)是一個(gè)節(jié)的信息,固定長(zhǎng)度為40個(gè)字節(jié),包含10個(gè)值,包括節(jié)名、節(jié)的類型、該節(jié)在文件中的位置、該節(jié)在內(nèi)存中的起始地址(如果該節(jié)出現(xiàn)在內(nèi)存映象中)、節(jié)的長(zhǎng)度等信息。某些節(jié)是程序段的組成部分,如包含程序二進(jìn)制指令代碼的正文節(jié).text和數(shù)據(jù)節(jié).rodata,.hash等,某些節(jié)不作為段的組成部分,只提供其它的額外信息。為源碼調(diào)試服務(wù)的有 .debug,.line,.symtab,.debug_ pubname,.debug_range等節(jié),其中.debug, .line節(jié)包含了源碼調(diào)試信息的基本內(nèi)容。
.debug節(jié)中有多種類型的記錄,可分為幾大類:
(1)、編譯模塊信息:包含組成該文件的各個(gè)模塊的源文件名,路徑,及該模塊的代碼地址范圍等。
(2)、子程序信息:包含程序名,程序類型,起始終止地址,程序返回結(jié)果存放地址等。
(3)、變量信息:包含變量名、變量類型、變量存放地址信息等,變量有多種類型,簡(jiǎn)單變量、結(jié)構(gòu)變量等類型的變量其信息內(nèi)容各有不同。
將.debug節(jié)中各項(xiàng)內(nèi)容的結(jié)構(gòu)關(guān)系抽象為家族關(guān)系。以節(jié)的起始為根,首先是一個(gè)編譯單元的信息,它給出下一個(gè)編譯單元(兄弟關(guān)系)在文件中的相對(duì)位置。緊跟著編譯單元的是該編譯單元中的子程序與公共變量信息(父子關(guān)系),同樣的,編譯單元中頭一個(gè)函數(shù)記錄或變量記錄將給出它的兄弟的位置信息。緊隨該函數(shù)記錄的是該函數(shù)內(nèi)部的子程序與局部變量信息。相鄰層次成員是父子關(guān)系,同一層次上的成員是兄弟關(guān)系,如圖4所示:
.line節(jié)中包含目標(biāo)代碼地址與源代碼行號(hào)之間的對(duì)應(yīng)關(guān)系。對(duì)每個(gè)編譯單元給出其行記錄信息的長(zhǎng)度和目標(biāo)碼的起始基地址,以及該編譯單元中所有的行記錄,每條記錄以固定的格式表示:“該行目標(biāo)碼相對(duì)于基地址的偏移,列號(hào)(保留,暫未使用),行號(hào)”。
綜合上述程序段和節(jié)的內(nèi)容,即可確定源碼與目標(biāo)碼的映射關(guān)系。如給定一個(gè)文件名及行號(hào),確定其目標(biāo)代碼的信息。首先根據(jù)文件名確定其在.debug節(jié)中的編譯模塊信息,從中可得該文件模塊的起始終止地址;再由其起始地址找到該編譯模塊的行記錄信息在.line節(jié)中的位置,根據(jù)行號(hào)找到行記錄,得到該行目標(biāo)碼的地址范圍;由這些地址信息,可直接從目標(biāo)機(jī)內(nèi)存中取得目標(biāo)代碼,也可結(jié)合程序段信息從目標(biāo)文件的程序段中取得該行所對(duì)應(yīng)的目標(biāo)代碼指令內(nèi)容。調(diào)試器利用地址與指令信息就可以查看、修改、執(zhí)行相應(yīng)目標(biāo)代碼,供用戶進(jìn)行調(diào)試。
linux操作系統(tǒng)文章專題:linux操作系統(tǒng)詳解(linux不再難懂)
評(píng)論