趁着五一假期,又重讀了《高級C++編譯技術》這本書,着重看了裏面查看庫文件和可執行文件詳細信息的工具集,好比
庫文件有哪些符號
、依賴哪些動態庫
、如何修改靜態庫等
。說實話,已經不是第一次看這部份內容了,但每次看完老是似懂非懂,內容太多也老是記不住。此次看的過程當中,也本身寫些demo實操了一遍,同時把整個過程記錄下來方便之後回顧。ios
objdump -f /path/to/program
看ELF文件頭也能夠。連接器命令行指定的連接庫
和間接依賴的動態庫
)連接器會將直接依賴項的列表(在構建過程當中,連接器命令行指定的那些動態庫)寫進二進制文件的ELF格式字段中。在分析加載依賴項時,ldd首先會掃描二進制文件並找到這些連接庫信息,並遞歸查找間接依賴項、去除重複項。安全
objdump -p /path/to/program | grep NEEDED
readelf -d /path/to/grogram | grep NEEDED
nm <二進制文件路徑>
。若是二進制包含C++代碼,默認打印通過名稱修飾以後的符號名(經過-C
選項打印沒有通過修飾的符號名);nm -D <二進制文件路徑>
;nm -C <二進制文件路徑>
;nm -u <二進制文件路徑>
;nm輸出的常見符號類型:bash
符號類型 | 含義 |
---|---|
B | 該符號的值出如今非初始化數據段(bss)中,全局未初始化變量 |
C | 該符號爲common。common symbol是未初始話數據段。該符號沒有包含於一個普通section中。只有在連接過程當中才進行分配。符號的值表示該符號須要的字節數。例如在一個c文件中,定義int test,而且該符號在別的地方會被引用,則該符號類型即爲C。不然其類型爲B |
D | 該符號的值位於初始化數據段(data)中,全局初始化變量 |
R | 該符號位於只讀數據段(rodata)中,const變量 |
T | 該符號位於代碼段(text)中,一般是函數 |
U | 該符號在當前文件中是未定義的,即該符號的定義在別的文件中。在定義它的文件中,其符號類型爲C,在使用它的文件中,其類型爲U |
V | 該符號是一個weak object |
如下是兩個小代碼:函數
#include "add.h"
int g_var_in_add = 10; // 全局初始化,位於data節
int add(int left, int right) {
return left + right;
}
複製代碼
#include "sub.h"
int g_var_in_sub = 20; // 全局初始化,位於data節
int g_var2_in_sub; // 全局未初始化,位於.bss節
const int g_var3_in_sub = 30; // 只讀,位於rodata節
int sub(int left, int right) {
return left - right;
}
複製代碼
共享庫的導出符號
、對外可見的符號
)objdump -f <二進制文件路徑>
;objdump -t <二進制文件路徑>
,與nm <二進制文件路徑>
輸出徹底相同;objdump -T <二進制文件路徑>
,與nm -D <二進制文件路徑>
輸出徹底相同;objdump -p <二進制文件路徑>
;objdump -R <二進制文件路徑>
;objdump -C <二進制文件路徑>
,與nm -C <二進制文件路徑>
輸出徹底相同objdump -x -j <.節名> <二進制文件路徑>
;objdump -d -M <intel|att> -S <二進制文件路徑>
。(源碼對照選項-S
,只有在構建時加了-g
選項時才能使用,包括從源代碼->.so->binary都須要加-g);objdump -f <二進制文件路徑>
objdump -x -j <.節名> <二進制文件路徑>
objdump -d -M <intel|att> -S <二進制文件路徑>
。(源碼對照選項-S
,只有在構建時加了-g
選項時才能使用,包括從源代碼->.so->binary都須要加-g)#include <iostream>
#include "add.h"
#include "sub.h"
int g_data_in_test = 1111;
int g_data_1_in_test;
int main() {
int add_val = add(1, 2);
int sub_val = sub(10, 3);
std::cout << "add_val:" << add_val << std::endl;
std::cout << "sub_val:" << sub_val << std::endl;
return 0;
}
複製代碼
與objdump幾乎相同。readelf提供的功能,objdump幾乎都有。不一樣之處在於:工具
- readelf只支持ELF二進制格式(.o .a .so binary),objdump支持大約50中不一樣二進制格式;
- readelf不依賴二進制文件描述庫,GNU的全部目標文件解析工具都依賴這個庫,所以readelf能夠獨立提供ELF格式信息。
readelf -h <二進制文件路徑>
;readelf -S <二進制文件路徑>
;readelf --symbols <二進制文件路徑>
,與nm <二進制文件路徑>
、objdump -t <二進制文件路徑>
輸出徹底相同;readelf --dyn-syms <二進制文件路徑>
,與nm -D <二進制文件路徑>
、objdump -T <二進制文件路徑>
輸出徹底相同;readelf -d <二進制文件路徑>
,與objdump -p <二進制文件路徑>
功能相同;readelf -r <二進制文件路徑>
,與objdump -R <二進制文件路徑>
功能相同;readelf -x <節名> <二進制文件路徑>
,與objdump -x -j <節名> <二進制文件路徑>
功能相同;readelf --debug-dump<選項> <二進制文件路徑>
。--debug-dump支持的選項有:--debug-dump[=rawline,=decodedline,=info,=abbrev,=pubnames,=aranges,=macro,=frames,
=frames-interp,=str,=loc,=Ranges,=pubtypes,
=gdb_index,=trace_info,=trace_abbrev,=trace_aranges,
=addr,=cu_index,=links,=follow-links]
複製代碼
readelf -h <二進制文件路徑>
readelf --debug-dump<選項> <二進制文件路徑>
chrpath -l <二進制文件位置>
;chrpath -r <new_rpath> <二進制文件路徑>
;chrpath -d <二進制文件路徑>
;chrpath -c <二進制文件路徑>
;注意:ui
ar crv 靜態庫名 <目標文件列表>
;ar -t 靜態庫名
;ar -d 靜態庫名 <待刪除的目標文件名>
;ar -r 靜態庫名 <待添加的目標文件名>
;ar -m -b <基準目標文件名 A> 靜態庫名 <要重排的目標文件名 B>
將會把B放到A的前面;