ld链接动态库静态库问题

在使用redisC代码的时候遇到的问题,安装redis make install可以看到:

mkdir -p/usr/local/include/hiredis /usr/local/lib
cp -a hiredis.h async.h read.h sds.h adapters/usr/local/include/hiredis
cp -a libhiredis.so/usr/local/lib/libhiredis.so.0.13
cd /usr/local/lib &&ln -sflibhiredis.so.0.13 libhiredis.so
cp -a libhiredis.a/usr/local/lib


redis的头文件放进了/usr/local/include/hiredis,把动态库libhiredis.so和静态库libhiredis.a都放进了/usr/local/lib

于是我就在我的Makefile里面写:

gcc main.c -I/usr/local/include-L/usr/local/lib -lhiredis -o main


OKgcc通过生成可执行文件main,但是一运行main报错:

error while loading shared libraries:

libhiredis.so.0.13: cannot open sharedobject file: No such file or directory

 

于是产生了两个疑问:

1. usr/local/lib 里面libhiredis.so和静态库libhiredis.a都有,为什么用的是.so而不是.agcc编译链接时写法都是-L库所在路径 -l库名,是否有写法可以区分指明要链接动态库还是静态库?

2. 我在Makefile里面不是已经指明so的位置,并且相应位置确实有libhiredis.so.0.13,为什么运行的时候还是找不到?

 

针对第一个问题,查了资料之后发现:

ld在链接的时候如果路径下相同库名的静态库和动态库都有,那么会优先使用动态链接库,动态链接库不存在才会使用静态链接库;

如果指明要使用静态链接库,那么需要在编译命令中加入-static参数;

gcc main.c -I/usr/local/include-L/usr/local/lib -lhiredis -static -o main

后来又做了测试发现,-static是一个全局参数,并不是只对一个链接生效,对所有的链接都生效。也就是一旦写上之后所有库都只会去找静态库,就不能指定某些库链静态库,而其它库链动态库;

 

针对第二个问题其实是很显然的,"link time"(链接时)和"runtime"(运行时),两个基本概念要弄清楚,不能混在一起。-L选项属于链接时,编译出来的可执行文件不知道-L选项后面的值,当然找不到对应的so。而-rpath,LD_LIBRARY_PATH都属于运行时

 

查资料之后发现运行的时候查找动态库路径顺序如下:

1. 可执行文件中自带的编译时写入的RPATH、RUNPATH

2. LD_LIBRARY_PATH 指定的地方
3. ldconfig 指定的地方:根据/etc/ld.so.cache查找
4. /lib
5. /usr/lib

 

所以相对应的解决办法有:

1.      使用rpath把so的路径指明并记录到二进制文件中

gcc main.c -I/usr/local/include -L/usr/local/lib -lhiredis –o main -Wl,--rpath=/usr/local/lib


重新编译再运行发现无报错了

关于RPATHRUNPATH的详细相关作用方法见http://gotowqj.iteye.com/blog/1926771

2.      把libredis.so所在路径加入到LD_LIBRARY_PATH环境变量中

export LD_LIBRARY_PATH= /usr/local/lib:$LD_LIBRARY_PATH


3.      使用ldconfig修改/etc/ld.so.cache

ldconfig命令的用途主要是在默认搜寻目录/lib/usr/lib以及动态库配置文件/etc/ld.so.conf内所列的目录下,搜索出可共享的动态链接库(格式如lib*.so*,进而创建出动态装入程序(ld.so)所需的连接和缓存文件(一般默认为/etc/ld.so.cache),此文件保存已排好序的动态链接库名字列表

ldconfig通常在系统启动时运行,而当用户安装了一个新的动态链接库时,就需要手工运行这个命令。

由于我们是新安装了redis的动态链接库,因此需要手动把它加到缓存文件中去,具体做法为:

/etc/ld.so.conf下面加一行/usr/local/lib /usr/local/mysql/lib(需要root权限),保存过后ldconfig一下(需要root权限)

4.      把新安装的so拷到/lib或/usr/lib下

 

衍生问题:那么程序在链接so的时候ld到底做了些什么?(有待后续研究)

你可能感兴趣的:(C开发,ld,动态库,静态库,redis)