《高级C/C++编译技术》03 - 重复符号处理、版本控制、工具集
作者:互联网
重复符号处理
当因符号重复而报错时,提示信息可能是重命名符号名称而非原始符号名称。
在 C 语言中使用 static 声明的函数仅本源代码文件可见,不会被认为是重复符号。
在动态库链接过程中处理重复符号
在动态链接时可以一定程度上接收重复符号,且不会立即报告链接错误。当多个动态库中含有相同符号时,一个解决方案是挑选一个出现的符号并将所有对符号的引用直接指向那一个特定符号。
比如动态库 lib1.so lib2.so lib3.so 都能实现 fun() 方法,而链接器让所有 fun 函数都指向其中一个库,结果三个动态库只初始化了一个。应避免这种情况
处理重复符号问题的一般策略
最好的策略就是强化符号和特定模块的从属关系,比如c++中命名空间。
使用动态加载动态库时通常不会产生这个问题,问题出现在对动态库的静态链接。
链接器解析动态库重复符号的模糊算法准则
搜索重复符号名的最佳候选符号时,链接器根据以下情况做出决定:
- 重复符号位置:链接器未出现在进程内存映射中不同部分的符号赋予不同重要等级
- 链接时指定的动态库链接顺序:同等优先级的代码中更早传递给动态库的符号有更高优先级
代码优先级划分规则:位置
优先级从高到低
- 客户二进制文件符号
- 动态库可见符号
存储在动态库动态节中的动态库导出符号 - 不参与链接
通常不考虑静态符号,无论属于客户二进制还是静态链接的动态库
案例分析
客户二进制文件符号与动态库ABI函数冲突
即优先级1和优先级2发生冲突,链接器会选择客户二进制文件符号。
静态库和动态库ABI冲突
gcc 使用静态库符号
VS 静态库在前时使用静态库符号,动态库在前时报错。
不同动态库ABI符号冲突
同属优先级2的符号发生冲突,由链接顺序决定。
动态库ABI和另一个动态库局部符号冲突
优先级2与优先级3的符号发生冲突,链接器选择动态库ABI符号。
两个未导出的动态库符号冲突
同属优先级3的符号发生冲突,它们不会出现在链接器符号列表中,也不会引发冲突
注意:静态库中的单例
假设用于日志的单例类在静态库中,多个动态库使用了它。单例类不是ABI接口的一部分,只是动态库内部功能的一部分不会被导出。由于动态库局部符号不会参与最终链接,所以会得到多个共存的单例日志类。
解决方法:
- 放松符号导出限制,允许动态库导出单例类符号。这样该符号就会变为ABI符号
- 将单例类放入动态库中(推荐)
可靠且有效的方法
每个库,包括静态库和动态库,应该有自己专用的命名空间。
版本控制
Linux
基于 soname 版本控制
库文件名:lib<libname>.so.<M>.<m>.<p>
soname:lib<libname>.so.<M>
常用动态库升级方式
在小版本升级时无需重新编译客户端程序,只使用软链接和soname。将软连接和实际动态库放在同一目录下,将软链接指向动态库文件。软连接文件名与动态库soname相同,客户端二进制文件很少包含完整版本信息的动态库,而是与soname文件链接。
在开发客户端二进制程序时使用“-l:<filename>”向链接器传递动态库文件名。 gcc -shared <inputs> -l:libxyz.so.1 -o <clientBinary>
或 gcc -shared <linker input> -Wl,-soname,<soname> -o <file>
使用 ldconfig -n <file>
可显示所有依赖的动态库文件
基于符号的版本控制
链接器将包含符号版本控制信息的节(.gnu.version等)写入 ELF 时,通过一条最简单的命令将基于文件文件的版本控制脚本传递给链接器。它能使动态库文件可以携带多个不同版本的相同符号。不同客户二进制文件可能需要不同版本动态库文件,那么只需要加载同一份二进制文件,并链接特定版本号即可。
省略
Windows
在VS中图形化操作,省略。
Linux 工具集
binutils
GNU Binutils是一系列二进制工具的集合。主要包括:
- ld —— GNU链接器
- as—— GNU汇编器
但也包括以下二进制工具:
- addr2line:从目标文件的虚拟地址获取文件的行号或符号。
- ar:可以对静态库做创建、修改和提取的操作。
- c++filt:反编译(反混淆,demangle)C++符号的工具。
- dlltool:创建创建Windows动态库。
- gold:另一种新的、更快的仅支持ELF的链接器。
- gprof:性能分析(profiling)工具程序。
- nlmconv:可以转换成NetWare Loadable Module(NLM)目标文件格式。
- nm:显示目标文件内的符号信息。
- objcopy:复制和转译目标文件。
- objdump:显示目标文件的相关信息,亦可反汇编。
- ranlib:产生静态库的索引。(和
nm -s
功能类似) - readelf: 显示ELF文件的内容。
- size:列出目标文件或库文件的section大小。
- strings:列出文件中可打印的字符串信息。
- strip:从目标文件中移除符号信息。
- windmc:Windows消息资源编译器。
- windres:Windows资源文件编译器。
查看工具
file 查看文件类型详细信息
size 获取ELF节的字节长度信息
详细信息分析工具
ldd 显示静态加载的依赖项
类似操作:objdump -p /path/program | grep NEEDED readelf -d /path/program | grep NEEDED
nm 列出二进制文件的符号列表
objdump 二进制分析工具,不止elf
readelf elf格式分析工具,不依赖 Binary_File_Descriptor_library 库,而GNU的所有目标文件解析工具都依赖这个库
部署阶段工具
chrpath 修改rpath
patchelf 修改runpath
strip 清除动态加载过程中不需要的库文件符号
ldconfig 指定装载器运行时的库搜索路径
运行时分析工具
strace 追踪系统调用和接收的信号
addr2line 将运行时地址转换成地址对应的源代码文件信息和行号
gdb
静态库工具
ar
标签:03,版本控制,符号,二进制,工具集,静态,文件,动态,链接 来源: https://www.cnblogs.com/zhh567/p/16684435.html