linux的链接库
概述
程序函数库可分为3种类型:静态函数库(static libraries)、共享函数库(shared libraries)、动态加载函数库(dynamically loaded libraries)
程序函数库可分为下面几种类型:
- 静态函数库(
static libraries
):在编译期间(compile-time)静态链接库会全部拷贝进编译对象中,一般以.a
文件的存在 - 动态函数库(
shared libraries
):在程序启动的时候加载到程序中,它可以被不同的程序共享,一般以.so
文件存在- 动态加载函数库(
dynamically loaded libraries
),在进程运行期间,使用dlfcn.h
中的函数加载、调用、关闭动态库
- 动态加载函数库(
注意: LD_LIBRARY_PATH
这个也是很关键的环境变量,一般的linux系统里都没设置这个 export LD_LIBRARY_PATH=/usr/local/lib/:/usr/lib/
如何指定链接的是动态库还是静态库?
如果我们在gcc中使用参数-l
来链接某个库,gcc会首先查找动态库,动态库没有再查找静态库,当然我们可以通过-Wl,-Bstatic
的方式使用静态库,使用完成之后记得加上-Wl,-Bdynamic
收尾,否则所有-Wl,-Bstatic
之后的库都变成静态库了
在CMake中,如果一个库既有动态版本,又有静态版本,可以在CMake里面指定target_link_libraries(xxx.a)
,指向静态库
链接库的顺序
GCC在链接过程中,对参数中的库的顺序是有要求的,参数右侧的库会先于左侧的库加载,也就是说参数的解析是从右往左的。
假设库B依赖与库A,则链接的时候要写为:
gcc -o bin -lB -lA
如果写为:
gcc -o bin -lA -lB
则在B中引用的A中的内容就会无法链接通过。
静态库链接问题
静态库本质上就是使用ar
命令打包一堆.o
文件
但是静态库和.o文件有不同的地方:
- 编译命令里连接了.o文件,那么一定会连接进最后的可执行文件。.o文件中的静态变量、被
__attribute__((constructor))
修饰的函数,也会正常的在main函数前被调用 - 静态库.a文件,如果编译到它发现没有被之前的文件调用,则编译器会忽略这个静态库文件,这会导致一些静态变量错误的没有被初始化。
gcc链接参数
-L
:告诉编译器搜索库的时候可以去哪个目录去找,如-L/usr/local/lib
-l
:指定链接某动态库或静态库,如-ltcmalloc
,也可以显式指定链接库的名字-l:libtest.so
-I
:告诉编译器搜索头文件的目录,如-I/usr/local/include
库链接参数
--whole-archive
调整库的链接顺序可以解决大部分问题,但当静态库之间存在环形依赖时,则无法通过调整顺序来解决。
使用--whole-archive
可以告诉编译器把静态库中的所有.o .a都进行链接、
--no-whole-archive
- 这个参数是跟在–whole-archive之后,作用是告诉编译器,后面的库不需要全部都链接了
- 也就是说只有跟在这两个语句中的参数才会全部被链接
# 例子
g++ -o program main.o \
-Wl,--whole-archive -lmylib \
-Wl,--no-whole-archive -llib1 -llib2
--wrap
-Wl,–wrap -Wl,free
--wrap=symbol
Use a wrapper function for symbol. Any undefined reference to
symbol will be resolved to "__wrap_symbol". Any undefined
reference to "__real_symbol" will be resolved to symbol.
This can be used to provide a wrapper for a system function. The
wrapper function should be called "__wrap_symbol". If it wishes to
call the system function, it should call "__real_symbol".
Here is a trivial example:
void *
__wrap_malloc (size_t c)
{
printf ("malloc called with %zu/n", c);
return __real_malloc (c);
}
If you link other code with this file using --wrap malloc, then all
calls to "malloc" will call the function "__wrap_malloc" instead.
The call to "__real_malloc" in "__wrap_malloc" will call the real
"malloc" function.
You may wish to provide a "__real_malloc" function as well, so that
links without the --wrap option will succeed. If you do this, you
should not put the definition of "__real_malloc" in the same file
as "__wrap_malloc"; if you do, the assembler may resolve the call
before the linker has a chance to wrap it to "malloc".
–start-group
--end-group
位于--start-group
--end-group
中的所有静态库将被反复搜索,而不是默认的只搜索一次,直到不再有新的unresolved symbol
产生为止。也就是说,出现在这里的.o
如果发现有unresolved symbol
,则可能回到之前的静态库中继续搜索。
ld
-
ld 命令是二进制工具集 GNU Binutils 的一员,是 GNU 链接器,用于将目标文件与库链接为可执行文件或库文件。相当于编译里面的链接环节
-
gcc里面使用
-Wl,
开头的参数都是传递给ld的参数,如果直接调用ld,则不需要加-Wl,
常用参数:
- -WL,–verbose 打印所有的过程
- -Wl,-Bdynamic 后面所有的库都使用动态版本
- -Wl,-Bstatic 后面所有的库使用静态版本
- -Wl,-soname 加动态链接库的版本号如1.0
- -Wl,–as-needed 只链接需要的库,不需要的不链接,gcc中默认打开,因为ld的顺序是从右到左的,所以如果链接顺序有问题,会导致某些库没有链接进去,导致后面的依赖库找不到没有导入的前面库的符号,因而编译失败
- -Wl,–version-script 指定库的输出符号,spdk使用如
spdk_event.map
之类的文件导出函数符号
cc
在linux下,cc就是gcc,之所以搞了个cc,是为了和unix兼容,cc在unix下是c语言的编译器
动态链接库
LD_PRELOAD
可以作为参数,增加动态链接库的指向,例如:LD_PRELOAD=/data/tools/lib/libzookeeper_mt.so:/data/tools/lib/libprotobuf.so
参考链接
- 浅析静态库链接原理好文,并且介绍了一些编译连接测参数
- ld linker question: the --whole-archive option
- How to force gcc to link an unused static library
- 解压静态库.a文件
- linux动态链接库的加载顺序
rpath
>LD_LIBRARY_PATH
>ldconfig
> 系统默认位置/lib
/usr/lib
- 动态库(.so)链接静态库(.a)的情况总结
- gcc 链接库的顺序问题
- 深入理解LINUX下动态库链接器/加载器ld-linux.so.2
- linux 热替换so文件 热替换需要先dlclose,然后再换掉动态库,切换的过程中不能调用动态库中的函数
- Linux下g++编译与使用静态库和动态库
- How static library in c++ work with name mangle?C++编译动态库文件,会遇到因为c++对函数改名,而产生name mangling问题
- 2.1 Command-line Options ld所有的选项
- When and why would the C linker exclude unused symbols?
- C 多个动态库存在同名函数问题处理方法:-fvisibility=hidden
- gcc编译参数-fPIC的一些问题
- GCC选项_-Wl,-soname 及 DT_NEEDED 的解释
- 链接脚本(Linker Scripts)语法和规则解析(翻译自官方手册)
GROUP(file, file, …) / GROUP(file file …)
AS_NEEDED(file, file, …) / AS_NEEDED(file file …)
- LD上文是对这个LD文档的部分翻译
- LD --version-script及脚本文件控制符号输出