【軟體開發底層知識修煉】七 Binutils輔助工具之- ar工具與nm工具
阿新 • • 發佈:2018-12-12
上一篇文章學習addr2line與strip工具。點選連結檢視上一篇文章:點選檢視
本篇文章學習兩個工具:ar與nm工具。
1、ar工具
ar工具很簡單,用於將目標檔案打包生成庫或者將目標檔案從庫中解壓出來
ar crs libname.a x.o y.o
將x.o y.o打包生成libname.a庫ar x libname.a
將libname.a庫解壓
2、nm工具
nm工具用於列出目標檔案的符號的相關資訊:地址,屬性,名字等
使用nm工具,可以輸出三部分內容:地址,段,識別符號
如下圖的例子;
其中識別符號位於的段資訊,需要說明一下。上述的T代表程式碼段。還有很多其他段識別符號如下圖:
它們具體的信可以參考《程式設計師的自我修養》,參考這本書後,可以更加詳細的說明各個段標識代表什麼意思。如下表:
A | 該符號的值是絕對的,在以後的連結過程中,不允許進行改變。這樣的符號值,常常出現在中斷向量表中,例如用符號來表示各個中斷向量函式在中斷向量表中的位置。 |
---|---|
B | 該符號的值出現在非初始化資料段(.bss)中。例如,在一個檔案中定義全域性static int test。則該符號test的型別為b,位於bss section中。其值表示該符號在bss段中的偏移。一般而言,bss段分配於RAM中 。 |
C | 該符號為common。common symbol是未初始話資料段。該符號沒有包含於一個普通section中。只有在連結過程中才進行分配。符號的值表示該符號需要的位元組數。例如在一個c檔案中,定義int test,並且該符號在別的地方會被引用,則該符號型別即為C。否則其型別為B。 |
D | 該符號位於初始化資料段中。一般來說,分配到.data section中。例如定義全域性int baud_table[5] = {9600, 19200, 38400, 57600, 115200},則會分配於初始化資料段中。 |
G | 該符號也位於初始化資料段中。主要用於small object提高訪問small data object的一種方式。 |
I | 該符號是對另一個符號的間接引用。 |
N | 該符號是一個debugging符號。 |
R | 該符號位於只讀資料段。例如定義全域性const int test[] = {123, 123};則test就是一個只讀資料區的符號。注意在cygwin下如果使用gcc直接編譯成MZ格式時,原始檔中的test對應_test,並且其符號型別為D,即初始化資料段中。但是如果使用m6812-elf-gcc這樣的交叉編譯工具,原始檔中的test對應目標檔案的test,即沒有新增下劃線,並且其符號型別為R。一般而言,位於rodata section。值得注意的是,如果在一個函式中定義const char *test = “abc”, const char test_int = 3。使用nm都不會得到符號資訊,但是字串“abc”分配於只讀儲存器中,test在rodata section中,大小為4。 |
S | 符號位於非初始化資料段,用於small object。 |
T | 該符號位於程式碼段text section。 |
U | 該符號在當前檔案中是未定義的,即該符號的定義在別的檔案中。例如,當前檔案呼叫另一個檔案中定義的函式,在這個被呼叫的函式在當前就是未定義的;但是在定義它的檔案中型別是T。但是對於全域性變數來說,在定義它的檔案中,其符號型別為C,在使用它的檔案中,其型別為U。 |
V | 該符號是一個weak object。 |
W | The symbol is a weak symbol that has not been specifically tagged as a weak object symbol. |
- | 該符號是a.out格式檔案中的stabs symbol。 |
? | 該符號型別沒有定義。 |
2.1 程式碼案例分析
還是以上一篇文章的程式碼為例(程式碼是執行時錯誤的,但是這不影響我們的實驗);
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;
}
func.c
#include <stdio.h>
int* g_pointer;
void func()
{
*g_pointer = (int)"D.T.Software";
return;
}
對以上兩個程式分別進行如下編譯:
- gcc -g -c func.c -o func.o
- gcc -g -c test.c -o test.o
然後使用nm工具分別檢視func.o與test.o
通過以上結果,可以看出
在func.o中:
- func符號位於程式碼段,這顯而易見
- g_pointer符號是一個未定義儲存段的符號。參考上表即可知道它的意思
在test.o中
- func未定義,因為它本身是在func.c中定義的
- g_global位於.bss段
- g_pointer未定義
- g_test位於資料段
- main位於程式碼段
- printf未定義,因為它是引自標準庫中的程式碼
3、總結
學會使用ar工具與nm工具。瞭解nm輸出對應的符號的說明資訊,參考本文表格。
本文參考狄泰軟體學院相關課程
想學習的可以加狄泰軟體學院群,
群聊號碼:199546072
學習探討加個人(可以免費幫忙下載CSDN資源):
qq:1126137994
微信:liu1126137994