AC_CHECK_LIB就是告诉configure我们需要检查哪些lib,并根据检查结果做什么处理。正是在这里遇到了问题并有所收获,才促使我写这篇blog记录一下。
问题是这样,我想检查一下libXv.so这个库是否存在,不存在的话就让configure直接退出。你可能要问人家是so你为啥要在编译阶段检查一个动态库是否存在,答案是因为代码里静态链接了libXv.so里的某些function.
于是乎,我在configure.in里写了如下代码:
2 |
AC_CHECK_LIB(Xv, XvGetVideo , [], [ |
3 |
echo "Error! You need to have libXv installed!" |
- AC_CHECK_LIB的第一个参数是库名,我们这里依赖libXv,自然是填Xv。
- 第二个参数是函数名,填的要么是你代码里真正reference到的function;如果你不知道或者懒得去查代码的话,用nm -D查看这个so,随便挑一个出来填上即可。
- 第三个参数是如果check结果为yes,做什么操作。我想继续,所以啥也不填。
- 第四个参数代表如果check结果为no,做什么处理。你也看到了——我打印一条报错信息,然后exit。
看上去没什么问题,我也由此生成了configure,但是在执行configure时却有了如下的报错:
1 |
checking for XvGetVideo in -lXv... no |
没道理啊!我是用yum install libXv安装的Xv库呀,而且XvGetVideo 这个名字都是通过
1 |
nm -D /usr/lib64/libXv.so.1 |
得到的啊。
路径问题?不能啊,我/usr/lib和/usr/lib64下面都有libXv.so.1啊。虽然我确信我这个64bit的机器用的只会是/usr/lib64.
好在configure执行期间会输出config.log,这也顺便让我知道了AC_CHECK_LIB的工作原理。
打开config.log,找到下面出错的片段:
1 |
configure:4415: checking for XvGetVideo in -lXv |
2 |
configure:4440: gcc -o conftest -g -O2 conftest.c -lXv >&5 |
3 |
/tmp/ccFbn6ak.o: In function `main': |
4 |
/home/kongc/workspace/haohao/conftest.c:23: undefined reference to `XvGetVideo' |
5 |
collect2: ld returned 1 exit status |
7 |
configure: failed program was: |
9 |
| /* Override any GCC internal prototype to avoid an error. |
10 |
| Use char because int might match the return type of a GCC |
11 |
| builtin and then its argument prototype would still apply. */ |
19 |
| return XvGetVideo (); |
23 |
configure:4449: result: no |
原来AC_CHECK_LIB这个宏是生成了一个.c程序,在里面调用我们指定的函数,再让gcc编译这个.c程序通过-l去link我们指定的库,如果link成功,则代表check通过。
而我们之所以没有通过check,log里也给出了原因:ld returned 1 exit status
ld哪里出错了呢?为了找到原因,我手动创建了这个test.c:
然后执行
这下得到了ld失败的真正原因:
1 |
/usr/bin/ld: cannot find -lXv |
路径问题??No,刚才已经否定了这个因素了。
软连接问题??No. 我ls看了,/usr/lib64/libXv.so.1 -> libXv.so.1.0.0 一点问题都没有,两个文件都好好的。
就连ldconfig -p也显示libXv被成功load了啊啊!
上下求索了好久,最终在stackoverflow找到了答案:
对于一个so来说,在程序执行时被动态link,这时link对象的名字叫做SONAME,对于一个so来说它的SONAME可以通过objdump看到:
1 |
$ objdump -p /usr/lib64/libXv.so.1 | grep SONAME |
这个SONAME的用途:是程序在执行时,在LD_LIBRARY_PATH下查找这个SONAME,找到对应的so文件,再进行动态绑定执行。这篇说绿色软件的文章依照的就是这个原理。
这也是为何我用yum安装libXv,/usr/lib和/usr/lib64下面各被安置了一个libXv.so.1的原因。
所以你也看到了,我一直在说执行和动态链接。显然调用gcc时并不是在执行,也不是在动态链接。对于ld来说,-l{libname} 这个参数只会让ld去系统库目录,也就是/usr/lib或/usr/lib64下以及通过-L指定的目录下去找lib{libname}.so这个文件,然后静态链接。如果找不到就会报usr/bin/ld: cannot find -lxxxxxd 的错误!而我的/usr/lib64/和/usr/lib下面都是没有libXv.so这个文件的。
所以解决办法很简单,创建一个/usr/lib64/libXv.so 的软连接指向/usr/lib64/libXv.so.1即可。
上面那个stackoverflow的帖子里同时也提供了另一个解决方案,那就是安装对应库的devel包。对于libXv来说,就是安装libXv-devel。装好后的效果其实是一样:
1 |
$ yum install libXv-devel |
2 |
$ ls -la /usr/lib64/libXv.so |
3 |
lrwxrwxrwx 1 root root 14 09-25 13:45 /usr/lib64/libXv.so -> libXv.so.1.0.0 |
虽然有点杀鸡用牛刀的感觉,但也很好解释了devel阶段的link跟使用时的link的区别——说白了就是对于so来说,静态链接和动态链接时使用的名字不一样。静态链接使用libxxxx.so,动态连接使用SONAME。
总结
说的东一句西一句貌似没有什么主题,但这次折腾让我明白了:
- AC_CHECK_LIB的工作原理
- 对于一个so来说ld时使用的名字和执行时被动态绑定时,使用的名字是不一样的
- 对于一个技术问题,80%中文搜索结果都是重复且无用的(况且我用的还是google)
- 对于技术问题,更快得到更好答案的方式可能是search in stackoverflow rather than in google. 当然,case by case:)