专注于 JetBrains IDEA 全家桶,永久激活,教程
持续更新 PyCharm,IDEA,WebStorm,PhpStorm,DataGrip,RubyMine,CLion,AppCode 永久激活教程

Linux库文件与可执行文件辅助工具集

趁着五一假期,又重读了《高级C++编译技术》这本书,着重看了里面查看库文件和可执行文件详细信息的工具集,比如库文件有哪些符号依赖哪些动态库如何修改静态库等。说实话,已经不是第一次看这部分内容了,但每次看完总是似懂非懂,内容太多也总是记不住。这次看的过程中,也自己写些demo实操了一遍,同时把整个过程记录下来方便以后回顾。

一、快速查看

1、file

(1) 功能

1、 查看文件类型
2、 查看可执行文件位数。通过objdump -f /path/to/program看ELF文件头也可以。

101_1.png

(2) 示例

101_2.png

2、size

(1) 功能

1、 查看静态库包含哪些目标文件
2、 查看ELF节的字节长度信息

(2) 示例

101_3.png

二、详细信息分析

1、ldd

(1) 功能

1、 显示出二进制文件启动时需要静态加载的动态库的完整列表(包含链接器命令行指定的链接库间接依赖的动态库

链接器会将直接依赖项的列表(在构建过程中,链接器命令行指定的那些动态库)写进二进制文件的ELF格式字段中。在分析加载依赖项时,ldd首先会扫描二进制文件并找到这些链接库信息,并递归查找间接依赖项、去除重复项。

(2) 示例

101_4.png

(3) 优缺点

1、 无法识别运行时通过调用dlopen()函数动态加载的动态库;
2、 一些环境下某些版本的ldd可能会尝试执行程序来获取依赖信息,可能会导致安全问题。对于不受信任的可执行文件,可以采取下面的安全的方法(下面的方法只会查找直接依赖项,不会递归查找间接依赖项):

1.  `objdump -p /path/to/program | grep NEEDED`
2.  `readelf -d /path/to/grogram | grep NEEDED`

101_5.png

2、nm

(1) 功能

1、 列出二进制文件的所有符号列表 nm <二进制文件路径>。如果二进制包含C++代码,默认打印经过名称修饰之后的符号名(通过-C选项打印没有经过修饰的符号名);
2、 只列出动态节中的符号(只针对共享库使用,共享库的导出符号、对外可见的符号nm -D <二进制文件路径>
3、 列出没有经过名称修饰的符号名 nm -C <二进制文件路径>
4、 列出二进制文件中未定义符号(这个二进制本身不包含的、但期望在运行从其他动态库中加载的) nm -u <二进制文件路径>

(2) 示例

nm输出的常见符号类型

符号类型 含义
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;
}

  • 列出二进制文件的所有符号列表

101_6.png

  • 只列出动态节中的符号(只针对共享库使用共享库的导出符号对外可见的符号

101_7.png

  • 列出没有经过名称修饰的符号名

101_8.png

3、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 <二进制文件路径>

101_9.png

  • 查看节中的数据 objdump -x -j <.节名> <二进制文件路径>

101_10.png

  • 反汇编二进制文件,并与源代码对照 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;
}

101_11.png

4、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 <二进制文件路径>

101_12.png

  • 确定二进制文件是否包含调试信息(构建时是否开启了-g选项) readelf --debug-dump<选项> <二进制文件路径>

101_13.png

三、部署阶段

1、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 <二进制文件路径>

注意

1、 只能在原本存在的DT_RPATH字符串长度内修改rpath,新值超过原本字符串长度之后会报错;

(2) 示例

101_14.png

四、静态库工具

1、ar

(1) 功能

1、 可以将目标文件打包成静态库 ar crv 静态库名 <目标文件列表>
2、 可以列出静态库包含的目标文件 ar -t 静态库名
3、 可以移除静态库中某些目标文件 ar -d 静态库名 <待删除的目标文件名>
4、 可以使用较新的版本替代这些目标文件 ar -r 静态库名 <待添加的目标文件名>
5、 重拍静态库中目标文件顺序 ar -m -b <基准目标文件名 A> 静态库名 <要重排的目标文件名 B> 将会把B放到A的前面;

(2) 示例

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

101_15.png

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

101_16.png

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

101_17.png

文章永久链接:https://tech.souyunku.com/43599

未经允许不得转载:搜云库技术团队 » Linux库文件与可执行文件辅助工具集

JetBrains 全家桶,激活、破解、教程

提供 JetBrains 全家桶激活码、注册码、破解补丁下载及详细激活教程,支持 IntelliJ IDEA、PyCharm、WebStorm 等工具的永久激活。无论是破解教程,还是最新激活码,均可免费获得,帮助开发者解决常见激活问题,确保轻松破解并快速使用 JetBrains 软件。获取免费的破解补丁和激活码,快速解决激活难题,全面覆盖 2024/2025 版本!

联系我们联系我们