Linux庫文件與可執行文件輔助工具集

趁着五一假期,又重讀了《高級C++編譯技術》這本書,着重看了裏面查看庫文件和可執行文件詳細信息的工具集,好比庫文件有哪些符號依賴哪些動態庫如何修改靜態庫等。說實話,已經不是第一次看這部份內容了,但每次看完老是似懂非懂,內容太多也老是記不住。此次看的過程當中,也本身寫些demo實操了一遍,同時把整個過程記錄下來方便之後回顧。ios

1、快速查看

一、file

(1) 功能

  1. 查看文件類型
  2. 查看可執行文件位數。經過objdump -f /path/to/program看ELF文件頭也能夠。

(2) 示例

二、size

(1) 功能

  1. 查看靜態庫包含哪些目標文件
  2. 查看ELF節的字節長度信息

(2) 示例

2、詳細信息分析

一、ldd

(1) 功能

  1. 顯示出二進制文件啓動時須要靜態加載的動態庫的完整列表(包含連接器命令行指定的連接庫間接依賴的動態庫

連接器會將直接依賴項的列表(在構建過程當中,連接器命令行指定的那些動態庫)寫進二進制文件的ELF格式字段中。在分析加載依賴項時,ldd首先會掃描二進制文件並找到這些連接庫信息,並遞歸查找間接依賴項、去除重複項。安全

(2) 示例

(3) 優缺點

  1. 沒法識別運行時經過調用dlopen()函數動態加載的動態庫;
  2. 一些環境下某些版本的ldd可能會嘗試執行程序來獲取依賴信息,可能會致使安全問題。對於不受信任的可執行文件,能夠採起下面的安全的方法(下面的方法只會查找直接依賴項,不會遞歸查找間接依賴項):
    1. objdump -p /path/to/program | grep NEEDED
    2. readelf -d /path/to/grogram | grep NEEDED

二、nm

(1) 功能

  1. 列出二進制文件的全部符號列表 nm <二進制文件路徑>。若是二進制包含C++代碼,默認打印通過名稱修飾以後的符號名(經過-C選項打印沒有通過修飾的符號名);
  2. 只列出動態節中的符號(只針對共享庫使用,共享庫的導出符號、對外可見的符號nm -D <二進制文件路徑>
  3. 列出沒有通過名稱修飾的符號名 nm -C <二進制文件路徑>
  4. 列出二進制文件中未定義符號(這個二進制自己不包含的、但指望在運行從其餘動態庫中加載的) nm -u <二進制文件路徑>

(2) 示例

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

如下是兩個小代碼函數

  • add.cpp
#include "add.h"

int g_var_in_add = 10;        // 全局初始化,位於data節


int add(int left, int right) {
    return left + right;
}
複製代碼
  • sub.cpp
#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

(1) 功能

  1. 解析ELF頭部 objdump -f <二進制文件路徑>
  2. 列出全部符號 objdump -t <二進制文件路徑>,與nm <二進制文件路徑>輸出徹底相同;
  3. 只列出動態節符號 objdump -T <二進制文件路徑>,與nm -D <二進制文件路徑>輸出徹底相同;
  4. 查看動態節(DT_RPATH和DT_RUNPATH的設置) objdump -p <二進制文件路徑>
  5. 查看重定位節 objdump -R <二進制文件路徑>
  6. 打印沒有通過名稱修飾的符號名 objdump -C <二進制文件路徑>,與nm -C <二進制文件路徑>輸出徹底相同
  7. 查看節中的數據 objdump -x -j <.節名> <二進制文件路徑>
  8. 反彙編二進制文件,並於源代碼對照 objdump -d -M <intel|att> -S <二進制文件路徑>。(源碼對照選項-S,只有在構建時加了-g選項時才能使用,包括從源代碼->.so->binary都須要加-g);

(2) 示例

  • 解析ELF頭部 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;
}
複製代碼

四、readelf

(1) 功能

與objdump幾乎相同。readelf提供的功能,objdump幾乎都有。不一樣之處在於:工具

  1. readelf只支持ELF二進制格式(.o .a .so binary),objdump支持大約50中不一樣二進制格式;
  2. readelf不依賴二進制文件描述庫,GNU的全部目標文件解析工具都依賴這個庫,所以readelf能夠獨立提供ELF格式信息。
  1. 解析ELF頭 readelf -h <二進制文件路徑>
  2. 列出全部節 readelf -S <二進制文件路徑>
  3. 列出全部符號 readelf --symbols <二進制文件路徑>,與nm <二進制文件路徑>objdump -t <二進制文件路徑>輸出徹底相同;
  4. 只列出動態符號 readelf --dyn-syms <二進制文件路徑>,與nm -D <二進制文件路徑>objdump -T <二進制文件路徑>輸出徹底相同;
  5. 查看動態節(DT_RPATH和DT_RUNPATH的設置) readelf -d <二進制文件路徑>,與objdump -p <二進制文件路徑>功能相同;
  6. 查看重定位節 readelf -r <二進制文件路徑>,與objdump -R <二進制文件路徑>功能相同;
  7. 查看節中的數據 readelf -x <節名> <二進制文件路徑>,與objdump -x -j <節名> <二進制文件路徑>功能相同;
  8. 肯定二進制文件是否包含調試信息(構建時是否開啓了-g選項) 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]
複製代碼

(2) 示例

  • 解析ELF頭 readelf -h <二進制文件路徑>

  • 肯定二進制文件是否包含調試信息(構建時是否開啓了-g選項) readelf --debug-dump<選項> <二進制文件路徑>

3、部署階段

一、chrpath

(1) 功能

  1. 列出ELF二進制文件的rpath(DT_RPATH字段) chrpath -l <二進制文件位置>
  2. 修改ELF二進制文件的rpath chrpath -r <new_rpath> <二進制文件路徑>
  3. 直接刪除ELF二進制文件的rpath chrpath -d <二進制文件路徑>
  4. 能夠將DT_RPATH轉換成DT_RUNPATH chrpath -c <二進制文件路徑>

注意ui

  1. 只能在本來存在的DT_RPATH字符串長度內修改rpath,新值超過本來字符串長度以後會報錯;

(2) 示例

4、靜態庫工具

一、ar

(1) 功能

  1. 能夠將目標文件打包成靜態庫 ar crv 靜態庫名 <目標文件列表>
  2. 能夠列出靜態庫包含的目標文件 ar -t 靜態庫名
  3. 能夠移除靜態庫中某些目標文件 ar -d 靜態庫名 <待刪除的目標文件名>
  4. 可使用較新的版本替代這些目標文件 ar -r 靜態庫名 <待添加的目標文件名>
  5. 重拍靜態庫中目標文件順序 ar -m -b <基準目標文件名 A> 靜態庫名 <要重排的目標文件名 B> 將會把B放到A的前面;

(2) 示例

  • 將目標文件打包成靜態庫

  • 列出靜態庫包含的目標文件

  • 先移除靜態庫中某些目標文件,再添加某些目標文件,而後重排目標文件順序

相關文章
相關標籤/搜索