gnu binutils

gnu binutils

  GNU binutils是一組二進制工具集。包括:addr2line ar gprof nm objcopy objdump ranlib size strings strip. 本文歸納他們的常用法。
  ar
  ar用於建立、修改、提取檔案文件(archive)。archive是一個包含多個被包含文 件的單一文件(也稱之爲庫文件),其結構保證了可以從中檢索並得到原始的被包含文件(稱之爲archive中的member)。member的原始文件內 容、模式(權限)、時間戳、所有着和組等屬性都被保存在 archive中。member被提取後,他們的屬性被恢復到初始狀態。
  ar主要用於創建C庫文件
  創建靜態庫
  (1) 生成目標文件:
  $ gcc -Wall -c file1.c file2.c file3.c
  不用指定生成.o文件名(默認生成file1.o, file2.o, file3.o)。
  (2) 從.o目標文件創建靜態連接庫:
  $ ar rv libNAME.a file1.o file2.o file3.o
  ar生成了libNAME.a庫,並列出庫中的文件。
  r : 將flie1.o, file2,o, file3.o插入archive,如故原先archive中已經存在某文件,則先將該文件刪除。
  v : 顯示ar操作的附加信息
  創建動態庫(利用gcc,未用ar)
  (1) 生成目標文件
  $ gcc -Wall -c -fpic file1.c file2.c file3.c
  -fpic: 指定生成的.o目標文件可被重定址. pic是position idependent code的縮寫: 位置無關代碼.
  (2)生成動態庫文件
  $ gcc -shared -o libNAME.so file1.o file2.o file3.o
  一般地, 連接器使用main()函數作爲程序入口. 但在動態共享庫中沒有這樣的入口. 所以就要指定-shared選項來避免編譯器顯示出錯信息.
  實際上, 上述的兩條命令可以合併爲下面這條:
  $ gcc -Wall -shared -fpic -o libNAME.so file1.c file2.c file3.c
  此後,將main函數所在的程序與libNAME.so連接
  至此,與動態庫連接的函數編譯成了一個可執行文件。貌似成功了,但還差最後一步。如果直接運行該程序,會給出這樣的錯誤信息:
  error while loading shared libraries: libhello.so:
  cannot open shared object file: No such file or directory
  這是因爲與動態庫連接的程序在運行時,首先將該動態庫加載到內存中,而gcc默認加載動態庫文 件所在目錄爲/usr/local/lib, /usr/lib。剛纔的程序雖然能編譯成功,但如果我們自己建立的動態庫沒有位於默認目錄中,則執行時會應爲無法找到它而失敗。
  解決辦法:改變加載路徑對應的環境變量,然後再執行。
  export LD_LIBRARY_PATH=動態庫所在目錄:$LD_LIBRARY_PATH
  查看archive內容
  $ ar tv archiveNAME
  t : 顯示archive中member的內容,若不指定member,則列出所有。
  v : 與t結合使用時,顯示member的詳細信息。
  要想進了解ar的詳細選項,參考ar的on-line manual
  nm
  nm用來列出目標文件中的符號,可以幫助程序員定位和分析執行程序和目標文件中的符號信息和它的屬性。
  如果沒有目標文件作爲參數傳遞給nm, nm假定目標文件爲a.out.
  這裏用一個簡單的示例程序來介紹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");
  }
  運行下列命令:
  $ gcc -Wall -c main.c hello.c bye.c
  gcc生成main.o, hello.o, bye.o三個目標文件(這裏沒有聲明函數原型,加了-Wall,gcc會給出警告)
  $ nm main.o hello.o bye.o
  結果顯示如下:
  main.o:
  U bye
  U hello
  00000000 T main
  hello.o:
  00000000 T hello
  U puts
  bye.o:
  00000000 T bye
  U puts
  結合這些輸出結果,以及程序代碼,可以知道:
  對於main.o, bye和hello未被定義, main被定義了
  對於hello.o, hello被定義了, puts未被定義
  對於bye.o, bye被定義了,puts未被定義
  幾個值得注意的問題:
  (1)"目標文件"指.o文件, 庫文件, 最終的可執行文件
  .o : 編譯後的目標文件,即含有最終編譯出的機器碼,但它裏面所引用的其他文件中函數的內存位置尚未定義.
  (2)如果用nm查看可執行文件, 輸出會比較多, 仔細研究輸出, 可以對nm用法有更清醒的認識.
  (3)在上述hello.c, bye.c中, 調用的是printf(), 而nm輸出中顯示調用的是puts(), 說明最終程序實際調用的puts(), 如果令hello.c或bye.c中的printf()使用格式化輸出,則nm顯示調用printf(). ( 如: printf("%d", 1); )
  關於nm的參數選項,參考on-line manual
  objcopy
  objcopy可以將一種格式的目標文件轉化爲另外一種格式的目標文件. 它使用GNU BFD庫進行讀/寫目標文件.使用BFD, objcopy就能將原格式的目標文件轉化爲不同格式的目標文件.
  以我們在nm中使用的hello.o目標文件和hello可執行爲例:
  $ file hello.o hello
  file命令用來判別文件類型, 輸出如下:
  hello.o: ELF 32-bit LSB relocatable, Intel 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
  現在運行objcopy來改變hello的文件類型: 原先它是ELF格式的可執行程序, 現將它轉換爲srec格式. srec格式文件是Motolora S-Record格式的文件, 主要用來在主機和目標機之間傳輸數據.
  $ objcopy -O srec hello hello_srec
  $ file hello.o hello
  file命令結果: hello_srec: Motorola S-Record; binary data in text format
  注意objcopy的格式, "-O"指定輸出文件類型; 輸入文件名和輸出文件名位於命令末尾. 關於objcopy命令的詳細選項, 參考on-line manual
  objdump
  objdump用來顯示目標文件的信息. 可以通過選項控制顯示那些特定信息. objdump一個最大的用處恐怕就是將C代碼反彙編了. 在嵌入式軟件開發過程中, 也可以用它查看執行文件或庫文件的信息.
  下面我們用上文提到的hello可執行文件和hello_srec可執行文件爲例, 介紹objdump的簡單用法:
  $ objdump -f hello hello_srec
  輸出如下:
  hello: file format elf32-i386
  architecture: 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 : 顯示目標文件的頭文件概要信息.
  生成反彙編代碼:
  $ objdump -d hello.o
  顯示如下:
  hello.o: file format elf32-i386
  Disassembly of section .text:
  00000000 <hello>:
  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 <hello+0xf>
  13: 83 c4 10 add $0x10,%esp
  16: c9 leave
  17: c3 ret
  -d : 顯示目標文件中機器指令使用的彙編語言. 只反彙編那些應該含有指令機器碼的節(顯示.text段); 如果用-D, 則反彙編所有節的內容.
  關於objcopy命令的詳細選項, 參考on-line manual
  readelf
  readelf用來顯示ELF格式目標文件的信息.可通過參數選項來控制顯示哪些特定信息.(注意: readelf不支持顯示archive文檔, 也不支持64位的ELF文件).
  下面利用先前的hello可執行文件演示readelf的簡單用法:
  $ 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: Intel 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格式目標文件, 且選項中至少要指定一個(除V, H外)的選項!
  gprof
  gprof被用來測量程序的性能. 它記錄每個函數被調用的次數以及相應的執行時間. 這樣就能鎖定程序執行時花費時間最多的部分, 對程序的優化就可集中於對它們的優化.
  用一個簡單的數值計算程序來掩飾gprof的用法:
  collatz.c:
  #include <stdio.h>
  /* 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編譯成目標文件collatz.o, gcc通過 -pg選項來打開gprof支持:
  $ gcc -Wall -c -pg collatz.c
  $ gcc -Wall -pg -o collatz collatz.o
  注意:兩條命令都要加 "-pg"選項。前一條命令生成collatz.o目標文件。後一條命令生成可執行文件,該可執行文件中包含了記錄函數執行時間的指令。
  生成collatz可執行文件後,現執行它,結果與一般程序的執行無疑。但此時在PWD目錄生成一個名爲"gmon.out"的文件,gprof通過它來分析程序的執行。
  如果不現執行程序,而直接用gprof來分析它,會提示“gmon.out: No such file or directory”。
  gprof用法:
  $ gprof ./collatz
 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章