前言
在编译程序时,考虑如下场景:
代码中使用到了A库的函数,此外再无其它依赖库。但是,A库中使用了B库的函数,B库依赖C库,C库依赖D库。因此在编译可执行文件时,需要找到ABCD,并指定库的路径,在链接时才不会爆出找不到某个定义的错误。
如果在交叉编译时A库的依赖项高达几十甚至更多个,会让编译变得非常麻烦,需要指定每个库的位置和库名,有些情况可能需要给库添加软链。有没有什么方法在编译时不需要指定依赖?
一、命令行gcc编译或shell脚本编译
// libfunA.so
void funA() {printf("libFunA.so\n");funB();}
// libfunB.so
void funB() {printf("libFunB.so\n");funC();}
// libfunC.so
void funC() {printf("libFunC.so\n");}
int main()
{printf("main\n");funA();}
编译动态库
gcc -fPIC -shared xxx.c -o libfunA.so
-
此时生成的动态库中只含有一个libfunA.so字符串
-
通过ldd命令查看libfunA.so的依赖,其中没有libfunB.so,即依赖项没有添加入动态库的二进制文件中
二、cmake编译
Cmake简单使用
cmake 编译时会默认指定一些参数,因此和上面简单的指定参数相比cmake会加入更多选项。
生成动态库时:
- 此时生成的动态库中含有两个libfunA.so字符串
- 当未指定动态库的依赖项时,ldd命令没有显示依赖项libfunB.so; 当指定依赖项时,ldd显示依赖libfunB.so
三、取消编译时的依赖
#include <dlfcn.h> // link with -ldl
dlopen
dlerror
dlsym
dlclose
// loads the dynamic shared object
void *handle(const char*filename, int flags);
- filename: filename, if is null, the return handle is for the main program.
if contain slash("/"), relative or absolute pathname
filename: filename, if is null, the return handle is for the main program.if contain slash(“/”), relative or absolute pathname
动态库搜索:
- 如果过调用对象中包含DT_RPATH,不包含 DT_RUNPATH,则搜索DT_RPATH中的路径
- 环境变量LD_LIBRARY_PATH中的路径顺序
- DT_RUNPATH的目录
- 缓存文件/etc/ld.so.cache(由ldconfig(8)维护)
- 目录/lib然后/usr/lib
flags: 以下二者之一
- RTLD_LAZY: 使用时加载
- RTLD_NOW: 立即加载
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
#include <gnu/lib-names.h> // GNU libc so name macros
int main()
{
void* handle; double(*cosine)(double); char* error;
handle = dlopen(LIBM_SO, RTLD_LAZY);
if (!handle) {exit(1);}
dlerror(); // clear error
cosine = (double(*)(double))dlsym(handle, "cos");
error = dlerror();
if (error != NULL) {printf("%s\n", error); exit(1);}
printf("%f\n", cosine(3.1415926));
dlclose(handle);
}
四、运行时指定依赖库路径
- 将动态库路径放入LD_LIBRARY_PATH
总结
在编译可执行文件时,需要找到所有已使用符号的定义,如果该符号在动态库中就需要指定动态库; 如果动态库依赖了另外的动态库,也需要指明该库。
通过dl库可在编译时避免指定动态库,方便编译。