C++ Undefined Symbol

如何查看编译好的.so 动态链接库所有的symbol

列出libName.so中所有的symbol

1
nm -D libName.so | grep ...

列出libName.so所有关联的动态链接库

1
ldd libName.so

知道了函数修饰后的名字,怎么推函数声明

1
c++filt _Z3bari

就可以反推函数声明是什么。这里要注意一点,C++规定函数不同是函数名称和参数列表,跟函数返回值没有关系,函数返回值不一样的话不能算重载。
这个反推挺重要的,因为我就遇到过sdf::ElementPtrsdf::v9::ElementPtr不同的状况。会导致函数名不一样。

运行时出现undefined symbol

详尽参考https://github.com/Captain1986/CaptainBlackboard/blob/master/D%230001-undefined_reference_to_XXX/D%230001.md

搬运总结一下出现“undefined reference”问题的直接原因和解决方法。

Case1 链接是缺少定义了XXX的源文件或目标文件或库文件

1.1 缺源文件:这会导致编译不通过
1.2 缺目标文件:也是编译不通过,使用g++编译的时候要包含关联的目标文件
1.3 缺库文件:一般也就是动态链接库不存在,与目标文件一样。库文件其实也是一种目标文件

Case2 链接顺序不对

(这个我还没经历过,因为没有直接写makefile或者直接用g++来编译大型工程)
在给编译器输入源文件,目标文件或者动态库静态库文件时,如果B文件依赖于A文件的内容,那么B文件应该放在A文件的左边

Case3 函数符号修饰不一样

先说一下符号修饰(Name Mangling),函数从源代码编译到目标文件时,函数在目标文件中的名字是会改变的(这个改变的规则是编译器厂商定的,一般会包含函数参数列表信息,namespace信息等),在后边链接阶段,链接器是按照函数改变后的名字来索引函数的实现机器码。只要是函数符号修饰不一样,那么链接器就会认为没有找到!
3.1 函数定义与声明不一致:这个一般也不会通过编译
3.2 C和C++混合编程:如果开发的代码用C++,但是有些第三方库或者代码,用的是C语言编译的,那么在应用C语言库的时候要加上extern "C", 例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
#ifndef _BAR_H_
#define _BAR_H_

#ifdef __cplusplus
extern "C"{
#endif

void baz(void);

#ifdef __cplusplus
}
#endif
}

3.3 编译器版本或者编译器选项不一致:不同的编译器版本,或者编译器版本和链接器版本不一致,用的运行时库不一样,都不一定兼容;编译器选项不一样,也可能会影响到函数签名。在开发时,最后技术团队每个程序员都用一套标准的编译器和编译器选项配置

Case4 把模板函数写进了cpp文件

这个可以看前一篇博文https://floodshao.github.io/2020/06/15/A-so-link-problem/#more
如果把模板函数写进了实现文件.cpp中,那么编译器就会认为这是一个独立的编译单元进行编译。然而因为他是模板函数,编译器不能确定到底要将他特化到哪个实现,编译器也不会搜索整个工程中所有的cpp文件来确定在当时会特化成什么类型。所以就干脆不会特化。这样你在链接时就找不到函数定义了。 看这里https://blog.csdn.net/imred/article/details/80261632

Case5 api hinden

如果动态库libA.so的确实现了foo(...),但是在编译.so的时候使用了-fvisibility=hidden类似的开关,那么使用者也是链接不了的(但是nm还是能够看得到)。这种情况常见于一些开源库里边的不兼容,前边版本api可以用,库升级后,这个版本编程hinden了。