GNU binutils是一組二進(jìn)制工具集。包括:addr2line ar gprof nm objcopy objdump ranlib size strings strip. 本文歸納他們的常用法。arar用于建立、修改、提取檔案文件(archive)。archive是一個(gè)包含多個(gè)被包含文件的單一文件(也稱(chēng)之為庫(kù)文件),其結(jié)構(gòu)保證了可以從中檢索并得到原始的被包含文件(稱(chēng)之為archive中的member)。member的原始文件內(nèi)容、模式(權(quán)限)、時(shí)間戳、所有著和組等屬性都被保存在 archive中。member被提取后,他們的屬性被恢復(fù)到初始狀態(tài)。
基本內(nèi)容
ar主要用于創(chuàng)建C庫(kù)文件
創(chuàng)建靜態(tài)庫(kù)
(1) 生成目標(biāo)文件:
$ gcc -Wall -c file1.c file2.c file3.c
不用指定生成.o文件名(默認(rèn)生成file1.o, file2.o, file3.o)。
(2) 從.o目標(biāo)文件創(chuàng)建靜態(tài)連接庫(kù):
$ ar rv libNAME.a file1.o file2.o file3.o
ar生成了libNAME.a庫(kù),并列出庫(kù)中的文件。
r : 將flie1.o, file2,o, file3.o插入archive,如故原先archive中已經(jīng)存在某文件,則先將該文件刪除。
v : 顯示ar操作的附加信息
創(chuàng)建動(dòng)態(tài)庫(kù)(利用gcc,未用ar)
(1) 生成目標(biāo)文件
$ gcc -Wall -c -fpic file1.c file2.c file3.c
-fpic: 指定生成的.o目標(biāo)文件可被重定址. pic是position idependent code的縮寫(xiě): 位置無(wú)關(guān)代碼.
(2)生成動(dòng)態(tài)庫(kù)文件
$ gcc -shared -o libNAME so file1.o file2.o file3.o
一般地, 連接器使用main()函數(shù)作為程序入口. 但在動(dòng)態(tài)共享庫(kù)中沒(méi)有這樣的入口. 所以就要指定-shared選項(xiàng)來(lái)避免編譯器顯示出錯(cuò)信息.
實(shí)際上, 上述的兩條命令可以合并為下面這條:
$ gcc -Wall -shared -fpic -o libNAME so file1.c file2.c file3.c
此后,將main函數(shù)所在的程序與libNAME so連接
至此,與動(dòng)態(tài)庫(kù)連接的函數(shù)編譯成了一個(gè)可執(zhí)行文件。貌似成功了,但還差最后一步。如果直接運(yùn)行該程序,會(huì)給出這樣的錯(cuò)誤信息:
error while loading shared libraries: libhello so:
cannot open shared object file: No such file or directory
這是因?yàn)榕c動(dòng)態(tài)庫(kù)連接的程序在運(yùn)行時(shí),首先將該動(dòng)態(tài)庫(kù)加載到內(nèi)存中,而GCC默認(rèn)加載動(dòng)態(tài)庫(kù)文件所在目錄為/usr/local/lib, /usr/lib。剛才的程序雖然能編譯成功,但如果我們自己建立的動(dòng)態(tài)庫(kù)沒(méi)有位于默認(rèn)目錄中,則執(zhí)行時(shí)會(huì)應(yīng)為無(wú)法找到它而失敗。
解決辦法:改變加載路徑對(duì)應(yīng)的環(huán)境變量,然后再執(zhí)行。
export LD_LIBRARY_PATH=動(dòng)態(tài)庫(kù)所在目錄:$LD_LIBRARY_PATH
查看archive內(nèi)容
$ ar tv archiveNAME
t : 顯示archive中member的內(nèi)容,若不指定member,則列出所有。
v : 與t結(jié)合使用時(shí),顯示member的詳細(xì)信息。
要想進(jìn)了解ar的詳細(xì)選項(xiàng),參考ar的on-line manual
nm
nm用來(lái)列出目標(biāo)文件中的符號(hào),可以幫助程序員定位和分析執(zhí)行程序和目標(biāo)文件中的符號(hào)信息和它的屬性。
如果沒(méi)有目標(biāo)文件作為參數(shù)傳遞給nm, nm假定目標(biāo)文件為a.out.
這里用一個(gè)簡(jiǎn)單的示例程序來(lái)介紹nm的用法:
main.c:
int main(int argc, char *argv[])
{
hello();
bye();
return 0;
}
hello.c:
void hello(void)
{
printf("hello!\n");
}
bye.c:
void bye(void)
{
printf("good bye!\n");
}
運(yùn)行下列命令:
$ gcc -Wall -c main.c hello.c bye.c
GCC生成main.o, hello.o, bye.o三個(gè)目標(biāo)文件(這里沒(méi)有聲明函數(shù)原型,加了-Wall,gcc會(huì)給出警告)
$ nm main.o hello.o bye.o
結(jié)果顯示如下:
main.o:
U bye
U hello
00000000 T main
hello.o:
00000000 T hello
U puts
bye.o:
00000000 T bye
U puts
結(jié)合這些輸出結(jié)果,以及程序代碼,可以知道:
對(duì)于main.o, bye和hello未被定義, main被定義了
對(duì)于hello.o, hello被定義了, puts未被定義
對(duì)于bye.o, bye被定義了,puts未被定義
幾個(gè)值得注意的問(wèn)題:
(1)"目標(biāo)文件"指.o文件, 庫(kù)文件, 最終的可執(zhí)行文件
.o : 編譯后的目標(biāo)文件,即含有最終編譯出的機(jī)器碼,但它里面所引用的其他文件中函數(shù)的內(nèi)存位置尚未定義.
(2)如果用nm查看可執(zhí)行文件, 輸出會(huì)比較多, 仔細(xì)研究輸出, 可以對(duì)nm用法有更清醒的認(rèn)識(shí).
(3)在上述hello.c, bye.c中, 調(diào)用的是printf(), 而nm輸出中顯示調(diào)用的是puts(), 說(shuō)明最終程序?qū)嶋H調(diào)用的puts(), 如果令hello.c或bye.c中的printf()使用格式化輸出,則nm顯示調(diào)用printf(). ( 如: printf("%d", 1); )
關(guān)于nm的參數(shù)選項(xiàng),參考o(jì)n-line manual
objcopy
objcopy可以將一種格式的目標(biāo)文件轉(zhuǎn)化為另外一種格式的目標(biāo)文件. 它使用GNU bfd庫(kù)進(jìn)行讀/寫(xiě)目標(biāo)文件。使用BFD, objcopy就能將原格式的目標(biāo)文件轉(zhuǎn)化為不同格式的目標(biāo)文件.
以我們?cè)趎m中使用的hello.o目標(biāo)文件和hello可執(zhí)行為例:
$ file hello.o hello
file命令用來(lái)判別文件類(lèi)型, 輸出如下:
hello.o: ELF 32-bit LSB relocatable, 英特爾 80386, version 1 (SYSV), not Stripped
hello: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), for GNU/Linux 2.2.0, dynamically linked (uses shared libs), not stripped
現(xiàn)在運(yùn)行objcopy來(lái)改變hello的文件類(lèi)型: 原先它是ELF格式的可執(zhí)行程序, 現(xiàn)將它轉(zhuǎn)換為srec格式. srec格式文件是Motolora S-Record格式的文件, 主要用來(lái)在主機(jī)和目標(biāo)機(jī)之間傳輸數(shù)據(jù)
$ objcopy -O srec hello hello_srec
$ file hello.o hello
file命令結(jié)果: hello_srec: Motorola S-Record; binary data in text format
注意objcopy的格式, "-O"指定輸出文件類(lèi)型; 輸入文件名和輸出文件名位于命令末尾. 關(guān)于objcopy命令的詳細(xì)選項(xiàng), 參考o(jì)n-line manual
Objdump
Objdump用來(lái)顯示目標(biāo)文件的信息. 可以通過(guò)選項(xiàng)控制顯示那些特定信息. objdump一個(gè)最大的用處恐怕就是將C代碼反匯編了. 在嵌入式軟件開(kāi)發(fā)過(guò)程中, 也可以用它查看執(zhí)行文件或庫(kù)文件的信息.
下面我們用上文提到的hello可執(zhí)行文件和hello_srec可執(zhí)行文件為例, 介紹objdump的簡(jiǎn)單用法:
$ objdump -f hello hello_srec
輸出如下:
hello: file format elf32-i386
建筑: i386, flags 0x00000112:
EXEC_P, HAS_SYMS, D_PAGED
start address 0x080482c0
hello_srec: file format srec
architecture: UNKNOWN!, flags 0x00000000:
start address 0x00000000080482c0
-f : 顯示目標(biāo)文件的頭文件概要信息.
生成反匯編代碼:
$ objdump -d hello.o
顯示如下:
hello.o: file format elf32-i386
Disassembly of section .text:
00000000
0: 55 push %EBP
1: 89 e5 mov %esp,%ebp
3: 83 ec 08 sub $0x8,%esp
6: 83 ec 0c sub $0xc,%esp
9: 68 00 00 00 00 push $0x0
e: e8 fc ff ff ff call f
13: 83 c4 10 add $0x10,%esp
16: c9 leave
17: C3 ret
-d : 顯示目標(biāo)文件中機(jī)器指令使用的匯編語(yǔ)言 只反匯編那些應(yīng)該含有指令機(jī)器碼的節(jié)(顯示.text段); 如果用-D, 則反匯編所有節(jié)的內(nèi)容.
關(guān)于objcopy命令的詳細(xì)選項(xiàng), 參考o(jì)n-line manual
readelf
readelf用來(lái)顯示ELF格式目標(biāo)文件的信息。可通過(guò)參數(shù)選項(xiàng)來(lái)控制顯示哪些特定信息.(注意: readelf不支持顯示archive文檔, 也不支持64位的ELF文件).
下面利用先前的hello可執(zhí)行文件演示readelf的簡(jiǎn)單用法:
$ readelf -h hello
ELF Header:
Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
Class: ELF32
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: EXEC (Executable file)
Machine: 英特爾 80386
Version: 0x1
Entry point address: 0x80482c0
Start of program headers: 52 (bytes into file)
Start of section headers: 3848 (bytes into file)
Flags: 0x0
Size of this header: 52 (bytes)
Size of program headers: 32 (bytes)
Number of program headers: 7
Size of section headers: 40 (bytes)
Number of section headers: 34
Section header string table index: 31
注意: readelf只能用于ELF格式目標(biāo)文件, 且選項(xiàng)中至少要指定一個(gè)(除V, H外)的選項(xiàng)!
gprof
gprof被用來(lái)測(cè)量程序的性能. 它記錄每個(gè)函數(shù)被調(diào)用的次數(shù)以及相應(yīng)的執(zhí)行時(shí)間 這樣就能鎖定程序執(zhí)行時(shí)花費(fèi)時(shí)間最多的部分, 對(duì)程序的優(yōu)化就可集中于對(duì)它們的優(yōu)化.
用一個(gè)簡(jiǎn)單的數(shù)值計(jì)算程序來(lái)掩飾gprof的用法:
collatz.c:
#include
/* Computes the length of Collatz sequences */
unsigned int step (unsigned int x)
{
if (x % 2 == 0)
{
return (x / 2);
}
else
{
return (3 * x + 1);
}
}
unsigned int nseq (unsigned int x0)
{
unsigned int i = 1, x;
if (x0 == 1 || x0 == 0)
return i;
x = step (x0);
while (x != 1 && x != 0)
{
x = step (x);
i++;
}
return i;
}
int main (void)
{
unsigned int i, m = 0, im = 0;
for (i = 1; i < 500000; i++)
{
unsigned int k = nseq (i);
if (k > m)
{
m = k;
im = i;
printf ("sequence length = %u for %u\n", m, im);
}
}
return 0;
}
先將collatz.c編譯成目標(biāo)文件collatz.o, gcc通過(guò) -pg選項(xiàng)來(lái)打開(kāi)gprof支持:
$ gcc -Wall -c -pg collatz.c
$ gcc -Wall -pg -o collatz collatz.o
注意:兩條命令都要加 "-pg"選項(xiàng)。前一條命令生成collatz.o目標(biāo)文件。后一條命令生成可執(zhí)行文件,該可執(zhí)行文件中包含了記錄函數(shù)執(zhí)行時(shí)間的指令。
生成collatz可執(zhí)行文件后,現(xiàn)執(zhí)行它,結(jié)果與一般程序的執(zhí)行無(wú)疑。但此時(shí)在PWD目錄生成一個(gè)名為"gmon.out"的文件,gprof通過(guò)它來(lái)分析程序的執(zhí)行。
如果不現(xiàn)執(zhí)行程序,而直接用gprof來(lái)分析它,會(huì)提示“gmon.out: No such file or directory”。
gprof用法:
$ gprof ./collatz
參考資料 >