0%

C++ 链接库顺序导致的符号未定义问题

符号未定义是链接过程中常见的问题,有时候很明显,有时候却很隐晦,比如链接库的顺序导致的符号未定义问题。

问题描述

使用 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.somutex.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