趁着五一假期,又重读了《高级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的前面;