如何用动态链接补全Alpine中glibc编译库缺失的feenableexcept符号
feenableexcept符号找不到 我有一个基于glibc编译的Linux共享库,希望在Alpine Linux中无需重新编译即可运行。找到的方案是安装gcompat(Alpine的musl到glibc兼容层),执行:
patchelf --add-needed libgcompat.so.0 /var/lib/libdyn_MyLib.so
这样不用重新编译就能让动态加载器在libgcompat.so中查找符号。
这个方案部分生效,ldd输出如下:
ldd /var/lib/libdyn_MyLib.so /lib/ld-musl-x86_64.so.1 (0x7f79007cc000) libgcompat.so.0 => /lib/libgcompat.so.0 (0x7f78ff278000) libicudata.so.50 => /lib/libicudata.so.50 (0x7f78fdca4000) libicui18n.so.50 => /lib/libicui18n.so.50 (0x7f78fd8a6000) libicuuc.so.50 => /lib/libicuuc.so.50 (0x7f78fd52d000) libstdc++.so.6 => /usr/lib/libstdc++.so.6 (0x7f78fd2df000) libm.so.6 => /lib/ld-musl-x86_64.so.1 (0x7f79007cc000) libgomp.so.1 => /usr/lib/libgomp.so.1 (0x7f78fd298000) libgcc_s.so.1 => /usr/lib/libgcc_s.so.1 (0x7f78fd27a000) libpthread.so.0 => /lib/ld-musl-x86_64.so.1 (0x7f79007cc000) libc.so.6 => /lib/ld-musl-x86_64.so.1 (0x7f79007cc000) ld-linux-x86-64.so.2 => /lib/ld-linux-x86-64.so.2 (0x7f78fd274000) libucontext.so.1 => /lib/libucontext.so.1 (0x7f78fd26f000) libobstack.so.1 => /usr/lib/libobstack.so.1 (0x7f78fd26a000) libdl.so.2 => /lib/ld-musl-x86_64.so.1 (0x7f79007cc000) Error relocating libdyn_MyLib.so: feenableexcept: symbol not found
可以看到libgcompat.so已经被加载,但仍提示feenableexcept符号未找到。查资料得知这个函数是glibc扩展,gcompat不支持。于是我自己写了个空实现的共享库:
glibc_extension.h:
#ifndef __ALPINE_GLIBC__ #define __ALPINE_GLIBC__ extern int feenableexcept(int __excepts) throw (); #endif
lib_ext.cpp:
#include "glibc_extension.h" int feenableexcept(int e) throw () { return 0; }
编译成共享库:
gcc -Wall -Werror -shared -o libalpine.so lib_ext.cpp
用nm确认符号表中有feenableexcept。然后用patchelf把这个库也添加到目标共享库中,再次执行ldd,输出里已经有libalpine.so,但还是提示同样的符号找不到错误。
问题出在C++名字修饰和符号类型上:
- 你用C编译补充库时,
feenableexcept会被编译器进行C名字修饰,变成类似_Z14feenableexcepti的符号名,但原共享库查找的是C风格的feenableexcept符号。 - 原共享库引用的
feenableexcept是全局函数符号,需要确保补充库导出的是相同类型的符号。
修复步骤
1. 修改代码,强制使用C风格符号导出
修改头文件和实现文件,用extern "C"包裹函数声明与定义,避免C++名字修饰:
glibc_extension.h:
#ifndef __ALPINE_GLIBC__ #define __ALPINE_GLIBC__ #ifdef __cplusplus extern "C" { #endif int feenableexcept(int __excepts); #ifdef __cplusplus } #endif #endif
lib_ext.cpp:
#include "glibc_extension.h" int feenableexcept(int e) { return 0; }
注:移除throw(),这是C++98的异常规格,在C风格导出中无必要,还可能影响符号导出。
2. 重新编译共享库
加上-fPIC生成位置无关代码,确保共享库能被正确加载:
gcc -Wall -Werror -shared -fPIC -o libalpine.so lib_ext.cpp
3. 确认符号正确导出
用nm命令检查,需显示全局(大写T)的C风格符号:
nm -D libalpine.so | grep feenableexcept
预期输出:
0000000000001060 T feenableexcept
4. 重新用patchelf添加依赖
优先添加libalpine.so,确保动态加载器优先查找它的符号:
patchelf --remove-needed libalpine.so /var/lib/libdyn_MyLib.so # 先移除旧依赖 patchelf --add-needed libalpine.so /var/lib/libdyn_MyLib.so # 重新添加
或者调整依赖顺序:
patchelf --insert-needed libalpine.so /var/lib/libdyn_MyLib.so
5. 验证结果
再次运行ldd或依赖该库的程序,feenableexcept: symbol not found的错误应该会消失。
内容的提问来源于stack exchange,提问作者Alon Lanyado

