GCC編譯器原理(一)04------GCC 工具:nlmconv、nm、objcopy、objdump和 ranlib


1.3.13 nlmconv

nlmconv 將可重定位的對象文件(Infile)轉換為 NetWare 可加載模塊(outfile),並可選擇讀取頭文件信息獲取 NLM 頭信息。

選項,描述

-I bfdname

--input-target=bfdname

指定源文件得格式為 bfdname

-O bfdname

--output-target=bfdname

使用對象格式bfdname編寫輸出文件。

nlmconv 根據輸入格式推斷輸出格式,例如對於 i386 輸入文件,輸出格式為 nlm32-i386。

-T headerfile

--header-file=headerfile

從 NLM 頭信息中讀取頭文件。

-V

--version

顯示 nlmconv 的版本號並退出。

-h

--help

顯示 nlmconv 的幫助信息並退出。

1.3.14 nm:列出目標文件中的符號

nm用來列出目標文件中的符號,可以幫助程序員定位和分析執行程序和目標文件中的符號信息和它的屬性。利用命令行選項,可以根據符號的地址、尺寸或名字組織這些符號,而且可以按照很多方式格式化該輸出結果。符號也可以被demangled,產生的結果和源代碼中的一樣。

如果沒有目標文件作為參數傳遞給nm,nm 假定目標文件為 a.out。

來個例子:

bye.c

1 #include <stdio.h>
2 #include <stdlib.h>
3 
4 void bye(void)
5 {
6     printf("good bye!\n");
7 }

hello.c

1 #include <stdio.h>
2 #include <stdlib.h>
3 
4 void hello(void)
5 {
6     printf("hello!\n");
7 }

main.c

1 #include <stdio.h>
2 #include <stdlib.h>
3 
4 int main(int argc, char *argv[])
5 {
6     hello();
7     bye();
8     return 0;
9 }

 

執行命令: 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, bye和hello未被定義, main被定義了
    • 對於hello.o,hello被定義了,puts未被定義
    • 對於bye.o, bye被定義了,puts未被定義
  • 幾個值得注意的問題:
    • "目標文件" 指 .o文件, 庫文件, 最終的可執行文件
      • .o : 編譯后的目標文件,即含有最終編譯出的機器碼,但它里面所引用的其他文件中函數的內存位置尚未定義.
    • 如果用 nm 查看可執行文件,輸出會比較多,仔細研究輸出,可以對 nm 用法有更清醒的認識。
    • 在上述 hello.c,bye.c 中,調用的是 printf(),而 nm 輸出中顯示調用的是 puts(),說明最終程序實際調用的 puts(),如果令 hello.c 或 bye.c 中的 printf() 使用格式化輸出,則 nm 顯示調用 printf()。( 如: printf("%d", 1); )

選項

描述

-A

同選項 --print-file-name

-a

同選項 --debug-syms

-B

同選項 --format=bsd。這是默認設置

-C [type]

同選項 --demangle

-D

同選項 –dynamic

--debug-syms

顯示調試器使用的符號。通常不會顯示這些符號

--demangle[=type]

demangles 符號,使它變回源代碼中找到的用戶級的名字。如果指定類型,會為如下類型之一:autognulucidarmhpedggnu-v3javagnat compaq

--dynamic

對於動態目標,例如共享庫,該選項可以顯示動態符號而不是普通符號

--extern-only

顯示定義為外部的符號

-f fmt

同選項 --format

--format=fmt

使用指定的輸出格式顯示符號。可選的格式包括 bsd、sysv 和 posix,其中 bsd 為默認格式

-g

同選項 --extern-only

-h

顯示選項列表,然后退出

--help

顯示選項列表,然后退出

-l

同選項 --line-numbers

--line-numbers

使用文件中保存的調試信息來確定文件名和每個符號的行號

-n

同選項 --numeric-sort

--no-sort

指出不要將符號排序

--numeric-sort

按照符號地址的數值排序

-o

同選項 --print-file-name

-p

同選項 --no-sort

-P

同選項 --format=posix

--portability

同選項 --format=posix

--print-armap

在列舉靜態庫成員符號時,該選項包含了模塊的索引信息及其他信息,正是該模塊含有所列符號

--print-file-name

用源文件的名字標記每個符號,而不是只在文件頭命名源文件

-r

同選項 --reverse-sort

--radix=base

指出打印符號值的數字的進制。可選的為 d 的十進制、o 的八進制、或 x 的十六進制

--reverse-sort

反序排列,不論字母還是數字均可

-s

同選項 --print-armap

--size-sort

按照尺寸進行符號排序。計算尺寸取的是下一個符號的最高地址和本符號的地址的差。輸出列出的是尺寸而不是通常的地址

-t base

同選項 --radix

--target=bfdname

bfdname 是目標文件格式的名字,它不是當前機器的名字。為得到已知格式名字列表,鍵入命令 objdump –i

-u

同選項--undefined-only

--undefined-only

只顯示文件引用但未定義的符號

-V

同選項 --version

--version

顯示版本信息,然后退出

 

1.3.15 objcopy

將目標文件的一部分或者全部內容拷貝到另外一個目標文件中,或者實現目標文件的格式轉換。

objcopy 工具使用 BFD 庫讀寫目標文件,它可以將一個目標文件的內容拷貝到另外一個目標文件當中。objcopy 通過它的選項來控制其不同的動作,它可以將目標文件拷貝成和原來的文件不一樣的格式。需要注意的是 objcopy 能夠在兩種格式之間拷貝一個完全鏈接的文件,在兩種格式之間拷貝一個可重定位的目標文件可能不會正常地工作。

objcopy 在做轉換的時候會創建臨時文件,然后將這些臨時文件刪除。objcopy 使用 BFD 來做它所有的轉換工作;它訪問BFD中描述的所有格式,可以不必指定就識別大多數的格式。

通過指定輸出目標為 srec(例如 -O srec),objcopy 可以用來生成 S-record 文件。

通過指定輸入目標為而進制文件(例如 -O binary),objcopy 可以生成原始格式的二進制文件。當 objcopy 生成一個原始格式的二進制文件的時候,它會生成輸入的目標文件的基本內存拷貝,然后所有的標號和可重定位信息都會被去掉。內存拷貝開始於最低段的加載地址,拷貝到輸出文件。

當生成一個 S-record 或者原始的二進制文件的時候,可以使用-S這個很有用的選項選項來移除一些包含調試信息的節。有時 -R 可以用來移除一些二進制文件不需要的節。

objcopy [選項]... 輸入文件 [輸出文件]

選項

描述

input-file outfile

參數 input-file 和 outfile 分別表示輸入目標文件(源目標文件)和輸出目標文件(目的目標文件)。如果在命令行中沒有明確地指定 outfile,那么 Objcopy 將創建一個臨時文件來存放目標結果,然后使用 input-file 的名字來重命名這個臨時文件(這時候,原來的 input-file 將被覆蓋)。

-I bfdname

--input-target=bfdname 明確告訴 Objcopy ,源文件的格式是什么,bfdname 是 BFD 庫中描述的標准格式名。這樣做要比讓 Objcopy 自己去分析源文件的格式,然后去和 BFD 中描述的各種格式比較,通過而得知源文件的目標格式名的方法要高效得多。

-O bfdname

--output-target= bfdname 使用指定的格式來寫輸出文件(即目標文件),bfdname 是 BFD 庫中描述的標准格式名。

-F bfdname

--target= bfdname 明確告訴 Objcopy ,源文件的格式是什么,同時也使用這個格式來寫輸出文件(即目標文件),也就是說將源目標文件中的內容拷貝到目的目標文件的過程中,只進行拷貝不做格式轉換,源目標文件是什么格式,目的目標文件就是什么格式。

-R sectionname

--remove-section= sectionname 從輸出文件中刪掉所有名為 sectionname 的段。這個選項可以多次使用。注意:不恰當地使用這個選項可能會導致輸出文件不可用。

-S

--strip-all (strip 剝去、剝)不從源文件中拷貝重定位信息和符號信息到輸出文件(目的文件)中去。

-g

--strip-debug 不從源文件中拷貝調試符號到輸出文件(目的文件)中去。

--strip-undeeded

剝去所有在重定位處理時所不需要的符號。

-K symbolname

--keep-symbol= symbolname 僅從源文件中拷貝名為 symbolname 的符號。這個選項可以多次使用。

-N symbolname

--strip-symbol= symbolname 不從源文件中拷貝名為 symbolname 的符號。這個選項可以多次使用。它可以和其他的 strip 選項聯合起來使用(除了 -K symbolname | --keep-symbol= symbolname 外)。

-L symbolname

--localize-symbol= symbolname 使名為 symbolname 的符號在文件內局部化,以便該符號在該文件外部是不可見的。這個選項可以多次使用。

-W symbolname

-weaken-symbol= symbolname 弱化名為 symbolname 的符號。這個選項可以多次使用。

-x

--discard-all (discard 丟棄、拋棄) 不從源文件中拷貝非全局符號。

-X

--discard-locals 不從源文件中拷貝又編譯器生成的局部符號(這些符號通常是L或 . 開頭的)。

-b byte

--byte= byte 如果 --interleave 使能,則開始保持字節在一個范圍內。字節范圍為 0 到 字節總長 -1,字節總長由 --interleave 選項設定

-i interleave

--interleave= interleave (interleave 隔行、交叉) 只復制每個寬度字節的范圍

-p

--preserve-dates (preserve 保存、保持) 設置輸出文件的訪問和修改日期和輸入文件相同。

--debugging

如果可能的話,轉換調試信息。因為只有特定的調試格式被支持,以及這個轉換過程要耗費一定的時間,所以這個選項不是默認的。

--gap-fill= val

使用內容 val 填充段與段之間的空隙。通過增加段的大小,在地址較低的一段附加空間中填充內容 val 來完成這一選項的功能。

--pad-to= address

填充輸出文件到虛擬地址 address。通過增加輸出文件中最后一個段的大小,在輸出文件中最后一段的末尾和 address 之間的這段附加空間中,用 --gap-fill= val 選項中指定的內容 val 來填充(默認內容是0,即沒有使用 --gap-fill= val 選項的情況下)。

--set-start= val

設置新文件(應該是輸出文件吧?)的起始地址為 val。不是所有的目標文件格式都支持設置起始地址。

--change-start = incr

--adjust-start= incr

通過增加值 incr 來改變起始地址。不是所有的目標文件格式都支持設置起始地址。

--change-addresses incr

--adjust-vma incr

通過加上一個值incr,改變所有段的 VMA(Virtual Memory Address 運行時地址)和 LMA(Load Memory Address 裝載地址),以及起始地址。某些目標文件格式不允許隨便更改段的地址。

--change-section-address section{= + -} val

--adjust-section-vma section{= + -} val

設置或者改變名為 section 的段的 VMA(Virtual Memory Address 運行時地址)和 LMA(Load Memory Address 裝載地址)。如果這個選項中使用的是 "=",那么名為 section 的段的 VMA(Virtual Memory Address 運行時地址)和 LMA(Load Memory Address 裝載地址)將被設置成 val;如果這個選項中使用的是 "-" 或者 "+" ,那么上述兩個地址將被設置或者改變成這兩個地址的當前值減去或加上 val 后的值。如果在輸入文件中名為 section 的段不存在,那么 Objcopy 將發出一個警告,除非 --no-change-warnings 選項被使用。

這里的段地址設置和改變都是輸出文件中的段相對於輸入文件中的段而言的。例如:

(1)--change-section-address .text = 10000

這里是指將輸入文件(即源文件)中名為 .text 的段拷貝到輸出文件中后,輸出文件中的 .text 段的 VMA(Virtual Memory Address 運行時地址)和 LMA(Load Memory Address 裝載地址)將都被設置成 10000。

(2)--change-section-address .text + 100

這里是指將輸入文件(即源文件)中名為 .text 的段拷貝到輸出文件中后,輸出文件中的 .text 段的 VMA(Virtual Memory Address 運行時地址)和 LMA(Load Memory Address 裝載地址)將都被設置成以前輸入文件中 .text 段的地址(當前地址)加上 100 后的值。

--change-section-lma section{= + -} val

僅設置或者改變名為 section 的段的 LMA(Load Memory Address 裝載地址)。一個段的 LMA 是程序被加載時,該段將被加載到的一段內存空間的首地址。通常 LMA 和 VMA(Virtual Memory Address 運行時地址)是相同的,但是在某些系統中,特別是在那些程序放在 ROM 的系統中,LMA 和 VMA 是不相同的。如果這個選項中使用的是 "=",那么名為 section 的段的 LMA(Load Memory Address裝載地址)將被設置成 val;如果這個選項中使用的是 "-" 或者 "+",那么LMA將被設置或者改變成這兩個地址的當前值減去或加上 val 后的值。如果在輸入文件中名為 section 的段不存在,那么 Objcopy 將發出一個警告,除非 --no-change-warnings 選項被使用。

--change-section-vma section{= + -} val

僅設置或者改變名為 section 的段的 VMA(Load Memory Address 裝載地址)。一個段的 VMA 是程序運行時,該段的定位地址。

通常 VMA 和 LMA(Virtual Memory Address 運行時地址)是相同的,但是在某些系統中,特別是在那些程序放在ROM的系統中,LMA 和 VMA 是不相同的。如果這個選項中使用的是 "=",那么名為 section 的段的 LMA(Load Memory Address 裝載地址)將被設置成 val;如果這個選項中使用的是 "-" 或者 "+",那么 LMA 將被設置或者改變成這兩個地址的當前值減去或加上 val 后的值。

如果在輸入文件中名為 section 的段不存在,那么 Objcopy 將發出一個警告,除非 --no-change-warnings 選項被使用。

--change-warnings

--adjust-warnings

如果命令行中使用了 --change-section-address section{= + -} val 或者 --adjust-section-vma section{= + -} val ,又或者 --change-section-lma section{= + -} val ,又或者 --change-section-vma section{= + -} val ,並且輸入文件中名為 section 的段不存在,則 Objcopy 發出警告。這是默認的選項。

--no-chagne-warnings

--no-adjust-warnings

如果命令行中使用了 --change-section-address section{= + -} val 或者 --adjust-section-vma section{= + -} val ,又或者 --change-section-lma section{= + -} val ,又或者 --change-section-vma section{= + -} val ,即使輸入文件中名為 section 的段不存在, Objcopy 也不會發出警告。

--set-section-flags section=flags

為 section 的段設置一個標識。這個 flags 變量的可以取逗號分隔的多個標識名字符串(這些標識名字符串是能夠被 Objcopy 程序所識別的),合法的標識名有 alloc,load,readonly,code,data 和 rom。

--add-section sectionname=filename

進行目標文件拷貝的過程中,在輸出文件中增加一個名為 sectionname 的新段。這個新增加的段的內容從文件 filename 得到。這個新增加的段的大小就是這個文件 filename 的大小。只要輸出文件的格式允許該文件的段可以有任意的段名(段名不是標准的,固定的),這個選項才能使用。

1.3.16 objdump

objdump 命令是用查看目標文件或者可執行的目標文件的構成的 gcc 工具。最常見得用法就是對目標文件進行反匯編,進行代碼追蹤。

選項如下:

選項

詳細選項

描述

-a

--archive-headers

顯示檔案庫的成員信息,類似 ls -l 將 lib*.a 的信息列出。

-b bfdname

--target=bfdname

指定目標碼格式。這不是必須的,objdump 能自動識別許多格式,比如: objdump -b oasys -m vax -h fu.o 顯示 fu.o 的頭部摘要信息,明確指出該文件是 Vax 系統下用 Oasys 編譯器生成的目標文件。objdump -i 將給出這里可以指定的目標碼格式列表。

-C

--demangle

將底層的符號名解碼成用戶級名字,除了去掉所開頭的下划線之外,還使得C++函數名以可理解的方式顯示出來。

-g

--debugging

顯示調試信息。企圖解析保存在文件中的調試信息並以 C 語言的語法顯示出來。僅僅支持某些類型的調試信息。有些其他的格式被 readelf -w 支持。

-e

--debugging-tags

類似 -g 選項,但是生成的信息是和 ctags 工具相兼容的格式。

-d

--disassemble

從 objfile 中反匯編那些特定指令機器碼的 section。

-D

--disassemble-all

與 -d 類似,但反匯編所有 section.

--prefix-addresses

 

反匯編的時候,顯示每一行的完整地址。這是一種比較老的反匯編格式。

-EB

   

-EL

--endian={big|little}

指定目標文件的小端。這個項將影響反匯編出來的指令。在反匯編的文件沒描述小端信息的時候用。例如 S-records.

-f

--file-headers

顯示 objfile 中每個文件的整體頭部摘要信息。

-h

--section-headers --headers

顯示目標文件各個section的頭部摘要信息。

-H

--help

簡短的幫助信息。

-i

--info

顯示對於 -b 或者 -m 選項可用的架構和目標格式列表。

-j name

--section=name

僅僅顯示指定名稱為 name 的 section 的信息

-l

--line-numbers

用文件名和行號標注相應的目標代碼,僅僅和 -d、-D 或者 -r 一起使用使用 -ld 和使用 -d 的區別不是很大,在源碼級調試的時候有用,要求編譯時使用了 -g 之類的調試編譯選項。

-m machine

--architecture=machine

指定反匯編目標文件時使用的架構,當待反匯編文件本身沒描述架構信息的時候(比如 S-records),這個選項很有用。可以用 -i 選項列出這里能夠指定的架構.

-r

--reloc

顯示文件的重定位入口。如果和 -d 或者 -D 一起使用,重定位部分以反匯編后的格式顯示出來。

-R

--dynamic-reloc

顯示文件的動態重定位入口,僅僅對於動態目標文件意義,比如某些共享庫。

-s

--full-contents

顯示指定 section 的完整內容。默認所有的非空 section 都會被顯示。

-S

--source

盡可能反匯編出源代碼,尤其當編譯的時候指定了 -g 這種調試參數時,效果比較明顯。隱含了 -d 參數。

--show-raw-insn

 

反匯編的時候,顯示每條匯編指令對應的機器碼,如不指定 --prefix-addresses,這將是缺省選項。

--no-show-raw-insn

 

反匯編時,不顯示匯編指令的機器碼,如不指定 --prefix-addresses,這將是缺省選項。

--start-address=address

 

從指定地址開始顯示數據,該選項影響 -d、-r 和 -s 選項的輸出。

--stop-address=address

 

顯示數據直到指定地址為止,該項影響 -d、-r 和 -s 選項的輸出。

-t

--syms

顯示文件的符號表入口。類似於 nm -s 提供的信息

-T

--dynamic-syms

顯示文件的動態符號表入口,僅僅對動態目標文件意義,比如某些共享庫。它顯示的信息類似於 nm -D | --dynamic 顯示的信息。

-V

--version

版本信息

--all-headers

-x

顯示所可用的頭信息,包括符號表、重定位入口。-x 等價於 -a -f -h -r -t 同時指定。

-z

--disassemble-zeroes

一般反匯編輸出將省略大塊的零,該選項使得這些零塊也被反匯編。

@file

 

可以將選項集中到一個文件中,然后使用這個 @file 選項載入

1.3.17 ranlib:更新靜態庫

靜態庫文件需要使用 " ar " 來創建和維護。當給靜態庫增建一個成員時(加入一個 .o 文件到靜態庫中)," ar " 可直接將需要增加的 .o 文件簡單的追加到靜態庫的末尾。

之后當我們使用這個庫進行鏈接生成可執行文件時,鏈接程序 " ld " 可能提示錯誤,這可能是:主程序使用了之前加入到庫中的 .o 文件中定義的一個函數或者全局變量,但連接程序無法找到這個函數或者變量。

這個問題的原因是:之前我們將編譯完成的 .o 文件直接加入到了庫的末尾,卻並沒有更新庫的有效符號表。連接程序進行連接時,在靜態庫的符號索引表中無法定位剛才加入的 .o 文件中定義的函數或者變量。這就需要在完成庫成員追加以后讓加入的所有 .o 文件中定義的函數(變量)有效,完成這個工作需要使用另外一個工具 " ranlib " 來對靜態庫的符號索引表進行更新。

我們所使用到的靜態庫(文檔文件)中,存在這樣一個特殊的成員,它的名字是 " __.SYMDEF " 。它包含了靜態庫中所有成員所定義的有效符號(函數名、變量名)。因此,當為庫增加了一個成員時,相應的就需要更新成員 " __.SYMDEF " ,否則所增加的成員中定義的所有的符號將無法被連接程序定位。

完成更新的命令是:ranlib ARCHIVEFILE

通常在 Makefile 中我們可以這樣來實現。

libfoo.a: libfoo.a(x.o) libfoo.a(y.o) ...

ranlib libfoo.a

它所實現的是在更新靜態庫成員" x.o "和" y.o "之后,對靜態庫的成員" __.SYMDEF "進行更新(更新庫的符號索引表)。

如果我們使用 GNU ar 工具來維護、管理靜態庫,我們就不需要考慮這一步。 GNU ar 本身已經提供了在更新庫的同時更新符號索引表的功能(這是默認行為,也可以通過命令行選項控制 ar 的具體行為。可參考 GNU ar 工具的 man 手冊)。

1.3.18 readelf

1.3.19 size

1.3.20 strings

1.3.21 strip

1.3.22 windres


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM