本文记录了解决 --hash-style
兼容性问题的过程。
问题
1 | dlopen failed: empty/missing DT_HASH in "libxxx.so" (built with --hash-style=gnu?) |
最近,稳定性监控平台,被这一行错误日志霸榜,刚看到时也一脸懵逼,下面我们来逐步分析。
名词解释
首先需要查阅一下相关文档,了解一下其中的”新朋友
”。
DT_HASH
1
ELF 中的一个 Sections,保存了一个用于查找符号的散列表,用于支持符号表的访问,能够提高符号搜索速度。
--hash-style=style
(以下解释摘自man ld
)1
Set the type of linker's hash table(s). style can be either "sysv" for classic ELF ".hash" section, "gnu" for new style GNU ".gnu.hash" section or "both" for both the classic ELF ".hash" and new style GNU ".gnu.hash" hash tables. The default is "sysv".
实验
通过查阅 --hash-style=style
参数,发现 style
支持三种配置:sysv
、gnu
和both
,废话不多说,先试一把。
gcc -Wl,--hash-style=sysv
1
2$ readelf -S libxxx.so | grep "hash"
[ 4] .hash HASH 0000000000003120 00003120gcc -Wl,--hash-style=gnu
1
2$ readelf -S libxxx.so | grep "hash"
[ 4] .gnu.hash GNU_HASH 0000000000003120 00003120gcc -Wl,--hash-style=both
1
2
3$ readelf -S libxxx.so | grep "hash"
[ 4] .gnu.hash GNU_HASH 0000000000003120 00003120
[ 5] .hash HASH 00000000000035f8 000035f8
-Wl
用于编译器向链接器传递参数。
如上,发现使用不同的配置,Sections Name
不同。
.gnu.hash
段,提供了与hash
段相同的功能;但是与hash
相比,增加了某些限制(附加规则),导致了不兼容,带来了50%
的动态链接性能提升,具体参见 https://blogs.oracle.com/solaris/gnu-hash-elf-sections-v2。
分析
结合实验结果,我先翻译一下问题中的那一行错误日志:
1 | 动态库加载失败,libxxx.so 中 DT_HASH 为空或者丢失,是不是用了 --hash-style=gnu 编译? |
结合上表,若 --hash-style=gnu
,那么 Section Name
就是 .gnu.hash
了,当然找不到.hash
了。
再查阅一下 Makefile
配置,发现并没有相关配置,怀疑是编译器的默认配置:
1 | $ gcc -dumpspecs | grep "hash" |
-dumpspecs
参数可以打印编译器的内置规范。
果不其然,默认的 --hash-style
配置为了 gnu
。
结论
输出的动态库的 --hash-style
为 gnu
,而目标系统并不能正确读取 --hash-style
为 gnu
的动态库,导致了如上的错误。
解决方案
配置 --hash-style
为 both
:
1 | LDFLAGS += -Wl,--hash-style=both |