天天看點

binutils工具集,軟體開發利器

如果使用gcc編譯器,那麼在開發中就離不開使用與之配套的工具集(tool chain),即binutils。工具集中的部分工具除了被gcc在背景使用為我們建立程式檔案之外,其他則有助于友善開發和調試.

在binutils工具集中,以下工具是我們在做嵌入式軟體開發時需要掌握的。

·as

是彙編編譯器,用于将彙編代碼轉換為目标檔案。

·addr2line

用于得到程式指令位址所對應的函數,以及函數所在的源檔案名和行号。(指令位址翻譯器)

·ar

用于建立和修改檔案檔案,以及從檔案檔案中抽取檔案。靜态庫(.a檔案)就是一種檔案檔案,需要用它生成和管理。(靜态庫生成器)

·ld

是連結器

·nm

用于列出程式檔案中的符号及符号在記憶體中(開始)位址。符号包含C程式中的函數名和變量名。(符号顯示器)

·objcopy

可以用來從程式檔案中拷貝出我們所指定的段。在将引導加載器燒至閃存中時,有時需要通過從程式中抽取段的方式生成燒寫檔案,這時objcopy工具就能派上用場。(段剪輯器)

·objdump

能顯示程式檔案的相關資訊和對程式檔案進行反彙編。(資訊檢視器)

·ranlib

用于生成一個檔案檔案的内容索引,以加快對檔案檔案的查找速度。将該工具運用與靜态庫能提高庫參與連結的效率。(庫索引生成器)

·size

用于了解程式檔案中各段的大小。(段大小檢視器)

·string

用于檢視程式檔案内的可顯示字元串。(字元串窺視器)

·strip

用于剝去程式檔案的調試資訊,以減少程式檔案所占用的存儲空間。這個工具對于存儲空間有限的嵌入式系統尤為重要。(程式檔案瘦身器)

在嵌入式開發環境中,編譯器的名稱往往不是gcc,而是像arm-rtems-gcc這樣的名稱。對于這種命名形式的編譯器,讀者可以找到arm-rtems-addr2line,arm-rtems-objdump等相應名稱的工具,這是GNU工具集的命名慣例。

addr2line,指令位址   翻譯器

要使用addr2line,在編譯器必須帶上-g加加上調試資訊。

addr2line 位址 -f -e a.out将顯示位址所指位置在哪個檔案中哪個函數中,該函數在該函數的第幾行。比如用nm檢視段位置foo函數緊接在main函數前面,那麼參數給的位址在之間的話,得到結果就是:foo,檔案,foo在檔案中的行号

用在C++上可能得到的函數名與原來的函數名不一樣,亂碼。這展現了C++的語言特性。在GNU工具集存在“mangling”這樣一個稱呼,而在window中稱為“decorating”,都是指對C++中的函數名進行名字分裂。名字分裂是因為在C++源程式中允許多個函數重名的(重載)。為了做到這點,C++編譯器的處理方法是對每個函數,将根據其輸入參數采用一定的編碼方式,形成不同的C函數名,這一過程就是名字分裂過程。可以加上--demangle=gnu-v3 将輸出未分裂的番薯名。

ar,靜态庫生成器

如果要将多個.o檔案生成一個庫檔案,則存在兩種類型。一種是靜态庫,在Linux世界裡其字尾是.a;另一種是動态庫,其字尾是.so

對于嵌入式系統,大多數情況下都是整個軟體就是一個可執行程式且不支援動态加載的方式,即以靜态庫為主。

binutils中的ar被用來管理靜态庫。先看看一個靜态庫中到底有什麼。拿庫檔案/lib/libc.a為例。對其采用ar的x參數進行解壓操作:

ar -x libc.a

ls

acltotext.o    fsetpos.o   initgroups.o    recomp.o   tmpfile.o   chown.o  fstat.o    lchown.o  ftello.o  libc.a  lseek.o  ..............等

對于解壓後的.o檔案并不陌生。每個.o檔案差不多都能找到其檔案名所對應的C庫函數。采用GNU工具集進行開發時,一個靜态庫其實就是将.o檔案打包而生成的檔案檔案。

如何用ar生成靜态庫:ar crs libmy.a  foo.o   bar.o  将foo.o和bar.o放入靜态庫libmy.a中。其中c參數表示建立一個檔案檔案,而r參數訓示将檔案增加到所建立的庫檔案中,s參數是為了生成庫索引以提高庫被連結時的效率。

采用t參數可以檢視一個靜态庫有什麼内容。ar t libmy.a 結果:foo.o bar.o

使用d參數可以删除庫中的目标檔案,ar d libmy.a foo.o                ar  t  libmy.a 結果:bar.o

nm,符号顯示器

總體來說nm用于列出程式檔案中的符号,執行個體:

nm  -n  main.exe

00401100    T    foo

0040111c    T    main

004011b8    T    printf

nm所列出的每一行由三部分組成。第一列是指程式運作時符号在記憶體中的位址,它表示函數或變量的開始位址;第二列是指相應的符号存放在哪一個段;最後一列則是符号的名稱。

第二列資訊非常有用,下面列出常見字母的含義:

————————————————————————————————

A :表示符号所對應的值是絕對的且在以後的連接配接過程中也不會改變 

B或b:表示符号位于未初始化的資料段(.bss)中                                           

C:表示沒有被初始化的公共符号                                                               

D或d:表示符号位于初始化的資料段(.data)中                                      

N:表示符号是調試用的                                                                            

p:表示符号位于一個棧回溯段中                                                               

R或r :表示符号位于隻讀資料段(.radata)中                                          

T或t :表示符号位于代碼段(.text)中                                                        

U :表示符号沒有被定義                                                                             

————————————————————————————————

·靜态變量和全局變量在哪個段與其是否初始化有關,未初始化在.bss中,否則在.data中

·函數無論是靜态還是非靜态的,總是被配置設定在.text段中。字母“t”的大小寫表示了符号是否是靜态函數,小寫表示靜态。

·函數内的局部變量由于是在棧上的,是以在nm中看不到他們。

objdump,資訊檢視器

有時需要知道所生成檔案的段資訊以分析問題,或則檢視C語言所對應的彙編的代碼,此時objdump幫了大忙。

-h選項,用來檢視程式檔案中的段資訊,從得出的結果中可以看到每個段的大小,以及當程式運作時各段在記憶體中的開始位址。File off資訊表示每個段在程式檔案中的存儲位置。對于引導加載器來說,當加載程式時,就是通過File off資訊從檔案中讀出相應段的内容,然後将這一内容寫到所指定的VMA(記憶體)處。“Algn”訓示了每一段的邊界對齊位元組數是多少。另外還可以看到每個段的屬性,比如READONLY,ALLOC等。也可以看到以.debug _開頭的,這些段是調試時需要使用的,其中存儲了程式中每一個符号的調試資訊。這些調試資訊采用一定的編碼格式,最常用的格式是DWARF(Debugging With Attributed Record Formats),DWARF規範可以從網站上http://www.dwarfstd.org/找到。使用-W選項可以檢視的DWARF資訊。正是因為其中存在每個函數的起始位址資訊,是以用addr2line工具才能反向找到指令位址所對應的函數名,檔案和檔案行号。總而言之,所有在調試時能檢視的符号資訊都采用DWARF格式放在調試段中,包括局部變量的棧幀中為重。當通過調試器檢視一個變量的值時,調試器首先通過DWARF格式中的資訊找到變量在記憶體中的位址,然後将記憶體中内容根據變量的類型顯示給我們看。

-d 選項可以顯示彙編代碼

 -S  -d  選項在反彙編時同時顯示C/C++源程式(不能對編譯程式時使用優化選項,否則因為程式被優化是的無法與源程式一一對應)

-f 選項可以顯示程式檔案頭資訊(其中的start address訓示執行時的入口位址,即第一條指令在記憶體中的位址)

-s -j 能檢視一個段的具體内容,如objdump   -s  -j    .data   main

objcopy,段剪輯器

-j  參數,用于指定拷貝程式中的哪一段

-R 相反,用于删除一個段

如:objcopy   - j   .text  main     main.text表示将main中的.text拷貝到main.text檔案中,要拷貝多個段,可以多次-j,如objcopy   -j  .text   -j    .data   -j   .bss   main     main2

生成一個不含.text段的檔案objcopy   -R  .text   main   notextmain

ranlib,庫索引生成器

ar的s參數有同樣的功能。就是在檔案檔案中生成索引。當檔案檔案生成索引後,對其内檔案的存取速度将更快。如果檔案檔案是靜态庫,那麼生成索引檔案後的庫連結速度更快。     (ranlib    libmy.a)

可以使用nm加上-s參數檢視檔案檔案中的索引資訊

size,段大小觀察器

用objdump檢視時,出了.text    .data  和.bsss三個段外,還有.rdata和.idata兩個段。在size資訊中,.rdata段被歸到.text段中,而.idata段被歸到.data中。如果使用-A參數,size将給出更詳細的資訊。

strings,字元串窺視器

工作原理:針對輸入資料查找以換行符号或NUL結尾的四個(或以上)可列印的序列,再将結果列印到标準輸出。

strings mian

從結果中可以看到程式中定義的字元串,還有很多函數名。

因為這些資訊存放在.data(或.rdata段)中,這些資訊會向他人洩露程式的“痕迹”。

是以如果在程式中要定義密碼等加密資訊,則最好不要用字元串形式,或則定義也不要直接用它作為密碼,而采用一定的算法加工,防止其他人猜到它。

strings對于調試也是有用的。比如将版本資訊放入程式中,當釋出軟體時,不知道版本号,可以用strings獲得版本資訊。

另外strings工具與具體的處理器無關,也就是說可以在x86處理器上用strings檢視其他處理器上運作的程式檔案中的字元資訊。

strip,瘦身器

用于剝去程式檔案中的調試資訊,減少程式檔案的大小。objcopy 加--strip-debug參數也可以實作這點。strip所就有的功能,objcopy也都有。

繼續閱讀