本文共 1579 字,大约阅读时间需要 5 分钟。
最近做项目,同一份动态库代码,A同学编译出来可以正常加载运行; B同学编译也能成功,但加载的时候提示 动态库有未定义符号 XXXX。 A同学编译出来的so里面,没有这个符号。
1. 用nm 查看未定义符号XXXX : nm a.so | grep XXXX 查看该符号是不是 未定义的。
2. 查看这个未定义的符号来自哪里。 看看nm的参数有没有办法把源文件打印出来。
nm [option(s)] [file(s)]
有用的options:
- -A 在每个符号信息的前面打印所在对象文件名称;
- -C 输出demangle过了的符号名称;
- -D 打印动态符号;
- -l 使用对象文件中的调试信息打印出所在源文件及行号;
- -n 按照地址/符号值来排序;
- -u 打印出那些未定义的符号;
nm -l a.so | grep XXXX 查看到下面的信息
nm告诉我们为定义符号来源于 libcryto.so.xx库,但这个库却没有这个未定义符号。说明nm给出的信息有误。
3. 那如何找到这个未定义的符号来源哪里呢? 我们知道so是由一组 *.o 文件经过 -fPIC -shared 链接而来。我们可以去 *.o 文件中去找是哪里引入这个未定义符号。
cd 到编译目录,然后 nm *.o | grep XXXX 看到的信息如下:
4. 找到了引起这个未定义的cpp文件,这个时候就要问 为什么A同学编译的文件没问题, B同学编译的提示 未定义符号。 这时候我们就需要把源CPP的预编译文件打印出来后对比下了。
使用 g++ -E 命令生成预编译文件 output.i. 然后在这个文件中搜索未定义符号 XXXX. 发现它来自 /usr/local/include/openssl/evp.h 这个头文件。 这时候就想, 如果A同学编译也用到这个头文件,编译出来的so也应该包含 未定义符号。但A同学编译出来的so没有这个符号,很神奇。
5. 在A同学的环境中也生成预编译文件,然后在里面查找 enp.h 文件的路径。 发现A同学用的是 /usr/include/openssl/evp.h 目录下的头文件。 原来两个人用的头文件不是同一个。通过对比两个目录下的evp.h,发现他们是两个版本,里面的内容变动很大。 /usr/include/openssl/evp.h里面没有未定义的符号。 原因找到了。
6. 那A同学的环境里面多了什么,或者少了什么, 导致查找系统头文件目录的优先级发生了变动? 通过查询信息,发现:
gcc 在编译时如何去寻找所需要的头文件 :
※所以header file的搜寻会从-I开始
※然后找gcc的环境变量 C_INCLUDE_PATH,CPLUS_INCLUDE_PATH,OBJC_INCLUDE_PATH。
C_INCLUDE_PATH
仅对预处理C有效,CPLUS_INCLUDE_PATH
仅对预处理C++有效,而CPATH
对所有语言均有效。※再找内定目录
/usr/include
/usr/local/include
/usr/lib/gcc-lib/i386-linux/2.95.2/include
/usr/lib/gcc-lib/i386-linux/2.95.2/../../../../include/g -3
/usr/lib/gcc-lib/i386-linux/2.95.2/../../../../i386-linux/include
这里去A同学的环境变量中查找有没有设置 CPLUS_INCLUDE_PATH。 果然,A同学配置了这样的信息
,而B同学没有配置。 至此问题的根本原因发现了
转载地址:http://qlrti.baihongyu.com/