对于共享库(so文件),linux编译时和运行时的搜索顺序,可不像windows,他有他自己的规则,需要在开发的时候注意,否则发布之后,就会出现找不到库文件的错误。
在开始讲解这个问题的时候,我们先来看看两个linux命令:
ldd
这个命令,可以查看当前库或应用程序的依赖项
readelf
这个命令,可以读取ELF文件的一些相关信息
以上2个命令,都是查看文件信息非常重要和好用的命令,大家需要掌握。
好了,我们开始讲述本文的正题吧,先假设我们有一个main.c文件,一个libA.c文件,main.c会编译成应用程序main.out,libA.c会编译成共享库libA.so,main.out依赖libA.so。
如果我们在编译时不指定依赖库的搜索路径以及库,而是直接依赖:
gcc -g main.c -o main.out -ldl libA.so
这会怎么样呢?
先用ldd看看
依赖库似乎被硬编码到了文件中,我们用readelf印证一下看看
那么显然这个应用程序是不能发布给其他人使用的(你总不能让别人创建和你一样的目录吧)
那么如果加上-L -l
指令呢?我们来看看:
gcc -g main.c -o main.out -ldl -L/path/to/your/library/ -lA
还是先用ldd看看
这次没有硬编码的路径了,虽然提示找不到依赖库,但是编译是没问题的。这就是编译时搜索路径和运行时搜索路径的区别。
让我们再用readelf看看
那么这个时候如果发布了程序,程序到哪里找依赖呢?默认就是
/lib
/usr/lib
或者是配置了LD_LIBRARY_PATH
环境变量,或者是/etc/ld.so.conf
,总之和windows不一样,不做特殊处理的话,不会在当前目录寻找。
所以如果我们有一些依赖的第三方库要打包一起发布,又不想去动客户机的环境的话,那么就需要指定加载器在你指定的目录加载,比如应用程序当前目录下。
gcc -g main.c -o main.out -ldl -L/path/to/your/library/ -lA -Wl,-rpath='$ORIGIN:$ORIGIN/lib'
这就是rpath的作用,现在咱们再用ldd看看
这里,和第一次硬编码的结果一样,但是是完全不同的两种情况,因为ldd根据rpath找到了依赖库,我们用readelf看看
注意看,多了一个RPATH。
以上是so文件的静态加载方式,除了这种方式以外,还有一种方式,就是动态加载方式,这种方式就是使用dlopen dlsym
函数来加载,好处是可以随意指定目录,不好的地方,就是你得额外写一些函数接口声明。
好了,以上就是linux so 文件的一些问题了,希望看完之后,能对你有所帮助。