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) 示例

  • 将目标文件打包成静态库

  • 列出静态库包含的目标文件

  • 先移除静态库中某些目标文件,再添加某些目标文件,而后重排目标文件顺序

相关文章
相关标签/搜索