符号未定义是链接过程中常见的问题,有时候很明显,有时候却很隐晦,比如链接库的顺序导致的符号未定义问题。
问题描述
使用 gcc/g++ 编译一个项目的时候,出现了未定义的符号,符号来源于一个开源库,确认了库的位置,库中符号正常定义,库及其路径都被正确的引用了。
这是一个典型的库链接顺序导致的符号未定义问题了。
链接顺序
gcc/g++ 在合并目标文件生成可执行文件的时候会存在库的依赖问题:
在命令行中,如果定义一个符号的库出现在引用这个符合的目标文件之前,那么引用就不能被解析,链接会失败。
因此,我们的编译命令需要符合下面的规则:
关于库的一般准则是将它们放在命令行的末尾。如果库是相互独立的,则顺序不重要。如果不是相互独立,那么必须对它们进行排序,使得对于每个目标文件的外部引用的符号
s
,在命令行中至少有一个s
的定义是在对s
的引用之后。
也就是说对于日常命令行编译命令,一般从左到右分别是可执行文件 ——> 高级库 ——> 底层库,避免循环依赖;越是底层的库,越是往后面写,可以参考下述命令通式:
g++ ... obj($?) -l(上层逻辑lib) -l(中间封装lib) -l(基础lib) -l(系统lib) -o $@
as-needed 选项
在高版本(本文用的是 5.4)gcc/g++ 中,默认开启了 ld 的 –as-needed
选项。这个选项也会导致一些符号未定义问题。分析这个编译命令:
g++ -shared PyGalaxy.o -lGalaxyParser -lxxx -lrt -o PyGalaxy.so
像这样链接一个 PyGalaxy.so
的时候,假设 PyGalaxy.so
里面用到了 libGalaxyParser.so
但是没有用到 libxxx.so
。当开启 –as-needed
的时候,PyGalaxy.so
将不会链接 libxxx.so
。–as-needed
就是忽略链接时没有用到的动态库,只将用到的动态库 set NEEDED。
就是因为 –as-needed
的忽略功能,会导致一些库虽然被声明链接了,实际并没有,所以也会导致其他需要用的库(当然定义在其后)产生符号未定义问题。下面举例说明:
g++ -Wl,--as-needed -lGalaxyRT -lc -lm -ldl -lpthread -L/home/ocaml/lib/ -lrt -o mutex mutex.o
假设 mutex
依赖 libGalaxyRT.so
中的东西。想想,因为 gcc 对库的顺序要求和 –as-needed
(因为 libGalaxyRT.so
在 mutex.o
的左边,所以 gcc 认为没有用到它,–as-needed
将其忽略),ld 忽略 libGalaxyRT.so
,定位 mutex.o
的符号的时候当然会找不到符号的定义!所以 undefined reference to 这个 错误是正常地!
正确的链接方式是:
g++ -Wl,--as-needed mutex.o -lGalaxyRT -lc -lm -ldl -lpthread -L/home/ocaml/lib/ -lrt -o mutex