arm64环境nm显示.so含已定义函数但ld链接找不到对应符号
报错原因
一共有4处配置错误,其中直接触发符号找不到的核心原因是链接顺序错误:
- GNU ld默认按从左到右的顺序扫描输入文件,左侧文件的未定义符号只会到右侧的输入文件/库中查找。你当前的编译命令把
-llib1放在了源文件a.c/a.cc的前面,从ld的执行日志可以看到实际传入的参数顺序是-llib1 /tmp/cc0K5Hhf.o(后者是源文件编译生成的临时目标文件)。ld扫描到-llib1时,还没有读取到后续目标文件里对foo_incremented的未定义引用,加上默认开启的--as-needed参数会直接丢弃没有被前置文件依赖的库,等后续扫描到目标文件的未定义符号时,不会回头重新扫描已经处理过的-llib1,就会报符号找不到。 - 共享库生成参数错误:Linux下生成位置无关的共享库,编译阶段需要加
-fPIC参数生成位置无关代码,链接阶段需要加-shared参数指定生成共享库。你当前写的cc -dynamic -o $@ $<里,-dynamic是生成动态链接可执行文件的参数,不是生成共享库的参数,生成出来的文件不是合法的共享库,即使解决链接顺序问题后续运行也会出错。 - 头文件标准宏写错:C/C标准中用来判断当前是否为C编译环境的宏是
__cplusplus,你写的__cplusplus__前后都带双下划线,是不存在的自定义宏,会导致编译C文件时extern "C"声明不生效,后续会触发C符号修饰不匹配的问题。 - acc目标的编译参数写错:你写的
-Wl,-v,-rpath=.会把-v参数传给链接器,同时rpath参数的传递格式也不规范,另外两个可执行文件的依赖项没有加上liblib1.so,库文件修改后不会自动触发可执行文件重编译。
修复步骤
- 修正共享库的编译规则,添加
-fPIC和-shared参数:
liblib1.so: $(lib1_source) cc -fPIC -shared -o $@ $<
- 修正
lib1.h中的宏拼写错误:
// lib1.h #ifndef LIB1_H #define LIB1_H #ifdef __cplusplus extern "C" { #endif extern int foo_incremented(int x); #ifdef __cplusplus } #endif #endif
- 修正两个可执行文件的编译规则,调整链接顺序(把库参数放到源文件后面),修正rpath参数,补全依赖项:
a: a.c liblib1.so lib1.h cc -Wl,-rpath=. -L. -I. -o $@ $< -llib1 acc: a.cc liblib1.so lib1.h c++ -Wl,-rpath=. -L. -I. -o $@ $< -llib1
- (可选规范修正)把Makefile开头的伪目标声明修正为标准写法:
.PHONY: clean
修正后先执行make clean && make liblib1.so,再执行make a或make acc即可正常编译链接。
内容的提问来源于stack exchange,提问作者Michaelzh
相关产品推荐
相关产品推荐

