溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶(hù)服務(wù)條款》

開(kāi)發(fā)中的輔助工具(六)

發(fā)布時(shí)間:2020-05-31 02:43:35 來(lái)源:網(wǎng)絡(luò) 閱讀:706 作者:上帝之子521 欄目:系統(tǒng)運(yùn)維

        今天我們來(lái)看一看開(kāi)發(fā)中的輔助工具,那么什么是開(kāi)發(fā)環(huán)境呢?在我們的印象中,開(kāi)發(fā)環(huán)境就指的是編寫(xiě)代碼的環(huán)境。其實(shí)不然,開(kāi)發(fā)環(huán)境包括三大部分:構(gòu)建環(huán)境、調(diào)試環(huán)境以及測(cè)試環(huán)境。構(gòu)建環(huán)境便指得是代碼編寫(xiě)、程序編譯以及版本控制等;調(diào)試環(huán)境則指的是用于定位問(wèn)題的輔助工具集;測(cè)試環(huán)境指的是用于驗(yàn)證目標(biāo)程序是否滿(mǎn)足用戶(hù)的顯性需求和隱形需求。顯性需求指的是客戶(hù)的要求,而隱形需求則指的是一些用戶(hù)沒(méi)有要求到的但是必須具備的要求。比如一個(gè)應(yīng)用程序在 win7 系統(tǒng)上可以運(yùn)行起來(lái),在 win10 系統(tǒng)上也要能運(yùn)行起來(lái)。

        在嵌入式的開(kāi)發(fā)中,我們?cè)谡麄€(gè)項(xiàng)目中的代碼編寫(xiě)及目標(biāo)構(gòu)建上一般只花費(fèi) 20% 的時(shí)間,剩下的 80% 的時(shí)間適用于測(cè)試、調(diào)試以及 bug 修復(fù)的。那么我們?cè)撊绾翁岣唛_(kāi)發(fā)效率呢?工欲善其事必先利其器,我們可以借助于一些工具,從而提高開(kāi)發(fā)的效率。GNU 為 GCC 編譯器提供了配套的輔助工具集(Binutils),網(wǎng)址是 http://www.gnu.org/software/binutils/ ;其中的一些工具介紹如下

工具名     
功能簡(jiǎn)介                                    
add2line 
將代碼地址轉(zhuǎn)換為對(duì)應(yīng)的程序引導(dǎo)
strip        
提出可執(zhí)行程序中的調(diào)試信息      
ar            
將目標(biāo)文件打包成為靜態(tài)庫(kù)         
nm          
列出目標(biāo)文件中的符號(hào)及對(duì)應(yīng)地址
objdump
查看程序段信息及反匯編             
size         
查看目標(biāo)文件中的段大小             
strings    
查看目標(biāo)文件中的字符串             

        下來(lái)我們來(lái)一一的介紹下這幾個(gè)工具。

        1、addr2line : 將制定復(fù)制轉(zhuǎn)換為對(duì)應(yīng)的文件名和行號(hào),常用于分析和定位內(nèi)存訪(fǎng)問(wèn)錯(cuò)誤的問(wèn)題。來(lái)個(gè)示例代碼進(jìn)行分析說(shuō)明


func.c 源碼

#include <stdio.h>

int* g_pointer;

int main()
{
    *g_pointer = (int)"D.T.Software";
    
    return 0;
}


test.c 源碼

#include <stdio.h>

int g_global = 0;
int g_test = 1;

extern int* g_pointer;
extern void func();

int main(int argc, char *argv[])
{
    printf("&g_global = %p\n", &g_global);
    printf("&g_test = %p\n", &g_test);
    printf("&g_pointer = %p\n", &g_pointer);
    printf("g_pointer = %p\n", g_pointer);
    printf("&func = %p\n", &func);
    printf("&main = %p\n", &main);
    
    func();
    
    return 0;
}

        我們先來(lái)分析下這份代碼。我們?cè)谇懊嬷苯佣x int 類(lèi)型的指針,但是并沒(méi)有將其指為 NULL。那么此時(shí) g_pointer 已經(jīng)指向了內(nèi)存的 0 地址處,在 main 函數(shù)中操作 0 地址處,這肯定會(huì)引起段錯(cuò)誤。我們來(lái)看看編譯運(yùn)行結(jié)果

開(kāi)發(fā)中的輔助工具(六)

        我們看到 g_pointer 是個(gè)空指針,后面就直接發(fā)生段錯(cuò)誤,如果這是個(gè)很大的項(xiàng)目,幾十萬(wàn)行的代碼,相信我們定位問(wèn)題就很困難了。那么此時(shí)我們?cè)撊绾味ㄎ粏?wèn)題呢?addr2line 工具便出場(chǎng)了。使用的步驟如下:1、開(kāi)啟 core dump 選項(xiàng):ulimit -c unlimited;2、運(yùn)行程序,并生產(chǎn)崩潰時(shí)的 core 文件,執(zhí)行導(dǎo)致程序崩潰的測(cè)試用例;3、讀取 core 文件,獲取 IP 寄存器的值:dmesg core;4、使用 add2line 定位代碼行:addr2line 地址 -f -e test.out。如下

開(kāi)發(fā)中的輔助工具(六)

        我們看到此時(shí)已經(jīng)生成了 core 文件,我們來(lái) dmesg core 看看 IP 寄存器的值是什么

開(kāi)發(fā)中的輔助工具(六)

        我們看到 IP 寄存器的值是 0x080484b8,然后利用 addr2line 工具就可以直接定位到問(wèn)題的所在了。

        2、strip:用于剔除程序文件中的調(diào)試信息,減少目標(biāo)程序的大小。一般在程序發(fā)布前都需要將調(diào)試信息剔除,過(guò)多的調(diào)試信息可能影響程序的執(zhí)行效率。我們來(lái)看看它的用法,strip test.out

開(kāi)發(fā)中的輔助工具(六)

        我們看到在經(jīng)過(guò)剔除之后,它的文件大小幾乎減少了一半。使用它的注意事項(xiàng):1、幾乎所有的調(diào)試工具都依賴(lài)于目標(biāo)文件中的調(diào)試信息,調(diào)試信息的運(yùn)用能夠快速定位問(wèn)題;2、使用 gcc 編譯程序時(shí)使用 -g 選項(xiàng)生成調(diào)試信息,發(fā)布程序時(shí)再考慮是否使用 strip 剔除調(diào)試信息。那么這時(shí)如果想要利用 core 文件定位問(wèn)題就不可以了,因?yàn)?core 文件只能定位出調(diào)試版本的信息

開(kāi)發(fā)中的輔助工具(六)

        我們看到它是定位不出問(wèn)題的所在的。

        3、ar : 打包目標(biāo)文件,ar crs libname.a x.o y.o;解壓目標(biāo)文件,ar x libname.a。下來(lái)看看是如何使用的

開(kāi)發(fā)中的輔助工具(六)

        我們利用打包和解壓命令從而就可以直接給別人發(fā)第三方庫(kù)文件。如果我們只有第三方的庫(kù)文件而想使用其中的一個(gè) .o 文件,那么就可以使用解壓命令來(lái)進(jìn)行使用其中的一個(gè)文件。

        4、nm:用于列出目標(biāo)文件中的標(biāo)識(shí)符(變量名、函數(shù)名),輸出結(jié)果由三部分組成{地址、段以及標(biāo)識(shí)符}。示例如下

開(kāi)發(fā)中的輔助工具(六)

        段標(biāo)識(shí)說(shuō)明如下

開(kāi)發(fā)中的輔助工具(六)

        我們來(lái)看看 func.o 和 test.o 文件中的標(biāo)識(shí)符

開(kāi)發(fā)中的輔助工具(六)

        我們看到在 func.o 文件中 func 是位于代碼段的,它的偏移量為0。在這沒(méi)有經(jīng)過(guò)鏈接,所以顯示的都是相對(duì)地址。g_pointer 屬于未定義的標(biāo)識(shí)符,相對(duì)偏移量為 4。下來(lái)我們看看鏈接后的地址

開(kāi)發(fā)中的輔助工具(六)

        我們看到經(jīng)過(guò)鏈接后的地址是絕對(duì)地址。

        5、objdump : 反匯編目標(biāo)問(wèn)阿金,查看匯編到源碼的映射。objdump -d func.o 或者 objdump -S func.o。查看目標(biāo)文件中的詳細(xì)段信息,objdump -h test.out。

        objdump -h 的輸出說(shuō)明如下

開(kāi)發(fā)中的輔助工具(六)

        使用輸出信息如下

開(kāi)發(fā)中的輔助工具(六)

        我們?cè)賮?lái)看看鏈接后的 test.out 的詳細(xì)信息。

開(kāi)發(fā)中的輔助工具(六)

        我們看到它的 VMA 和 LMA 是一樣的,也就是說(shuō),虛存地址和加載地址是一樣的。在進(jìn)行運(yùn)行程序的時(shí)候,首先是為可執(zhí)行文件分配虛存,接著通過(guò) file off 獲取到相對(duì)位置,通過(guò)復(fù)制段信息過(guò)去,加載目標(biāo)地址到虛存上。最后是執(zhí)行程序。

        6、size 用來(lái)獲取目標(biāo)文件中的所有段大小,size test.out;strings 用來(lái)獲取目標(biāo)文件中的所有字符串常量。如下

開(kāi)發(fā)中的輔助工具(六)

        那么我們獲取它們的大小和字符串有什么意義呢?在嵌入式的開(kāi)發(fā)中,資源往往是非常受限的,因此我們就必須得嚴(yán)格控制目標(biāo)文件的大小,以防止超出其界限造成不可預(yù)料的錯(cuò)誤;我們?nèi)绻胍@取某些特定的字符串時(shí)就不必去看代碼了,直接用 strings 就可以看到全部的字符串了,以提高開(kāi)發(fā)效率。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI