将两个32位汇编文件链接为32位动态库时遇重定位错误求解
问题描述
我在32位环境下工作,通过基于LLVM后端的自制编译器生成了两个32位.s汇编文件。尝试执行以下命令将它们链接为动态库时出现错误:
clang++ -m32 test1.s test2.s -shared -fPIC -o test.so
报错信息:
/usr/bin/ld: /tmp/test1-5ff028.o: relocation R_386_GOTOFF against undefined symbol `stdout@@GLIBC_2.0' can not be used when making a shared object /usr/bin/ld: final link failed: bad value clang: error: linker command failed with exit code 1 (use -v to see invocation)
这些.s文件是通过llc -march=x86 xxx.ll --relocation-model=pic生成的,且/usr/lib/i386-linux-gnu/libc.so.6中存在glibc2.0。另外,不加-shared参数执行clang++ -m32 test1.s test2.s -o test.out可正常生成可执行文件并运行。
开发环境:
OS: WSL2 Ubuntu22.04 x86_64 clang: 13 llvm: 13 gcc-multilib/g++-multilib: installed
我尝试添加-lc -lm -ldl -lstdc++等参数,但均未解决问题,请问该如何修复?
解决方案
1. 修正stdout的引用方式
R_386_GOTOFF重定位依赖符号的绝对偏移,不适用于加载地址可变的动态库。问题核心是汇编中对stdout的引用方式错误:
- 32位glibc中
stdout并非直接全局符号,而是__iob_func()返回的文件指针数组的第2个元素(索引1,每个元素占4字节)。手动修改.s文件中的引用逻辑:# 替换原有的GOTOFF引用代码: movl stdout@GOTOFF(%ebx), %eax # 改为间接调用__iob_func的方式: call __iob_func movl 4(%eax), %eax # 取数组第2个元素(stdout)的地址
2. 调整LLC代码生成参数
除--relocation-model=pic外,添加32位动态库适配参数重新生成汇编:
llc -march=x86 xxx.ll --relocation-model=pic --code-model=small --enable-machine-outliner=false -o test.s
--code-model=small:确保生成适配32位小地址空间的代码--enable-machine-outliner=false:禁用代码大纲化,避免触发异常重定位
3. 修改链接命令的库与链接器指定
显式指定32位动态链接器和库路径,强制clang使用正确的32位链接逻辑:
clang++ -m32 test1.s test2.s -shared -fPIC -o test.so -dynamic-linker /lib/ld-linux.so.2 -L/usr/lib/i386-linux-gnu -lc
-dynamic-linker /lib/ld-linux.so.2:指定32位glibc的动态链接器-L/usr/lib/i386-linux-gnu:强制优先查找32位系统库
4. 检查自制编译器的IR生成逻辑
确保编译器生成的LLVM IR对stdout的引用符合动态库要求:
- IR中需声明
@stdout = external global i8*,并通过load i8*, i8** @stdout访问,而非硬编码绝对地址或偏移 - 避免生成直接绑定到符号绝对位置的IR指令
5. 尝试用gcc替代clang链接
如果clang的链接逻辑存在兼容性问题,改用gcc链接:
gcc -m32 test1.s test2.s -shared -fPIC -o test.so
若gcc链接成功,可通过添加-v参数对比两者的链接命令细节,定位clang的参数传递问题。
内容的提问来源于stack exchange,提问作者Dbettkk
相关产品推荐
相关产品推荐

