静态、动态链接库编译及Makefile编写
-
重要的几个概念
- 利用gcc生成可执行文件,可以有三种“模式”:
- 多文件编译
- 静态库链接
- 动态库链接
其中多文件编译和静态库链接两种方式有些相似
- 静态库文件通常以.a结尾,静态库在程序编译时会被链接到目标代码中,库中目标文件所含的所有将要被使用的函数的机器码会拷贝进最终的可执行文件中,所以程序运行时将不再需要该静态库。
- 动态库文件通常以.so结尾,动态库在程序编译时不会被链接到目标代码中,与动态库链接的可执行文件只包含它所需要的函数表,而不是所有的函数代码,而代码是在程序运行时才被载入,因此在程序运行时还需要动态库存在,若库被更新,不需要重新编译与它链接的源程序。
- 无论静态库还是动态库,都是有.o文件创建的。
===============================================================================================================
假设,有 main.c -------> call calc() function function.h -------> declare calc() function.h -------> define calc() 三个文件,关系如上图所示,要求最后生成的可执行文件名字为 8
- 利用gcc生成可执行文件,可以有三种“模式”:
-
方式1:多文件编译
ren@rjx: gcc -o function.o -c function.c ren@rjx: gcc -o main.o -c main.c ren@rjx: gcc -o 8 main.o function.o ren@rjx: ./8
这是最简单也是最直接的一种方式,但是倘若某个文件需要修改,则整个程序需要重新编译链接一遍
-
方式2:静态库链接
ren@rjx: gcc -o function.o -c function.c ren@rjx: ar -r libfunction.a function.o ar: creating libfunction.a ren@jrx: gcc -L . -o 8 main.c -lfunction ren@rjx: ./8
注解:
-
ar 命令类似
tar
,起打包作用,将命令行中的文件打包入其中,用来创建、修改静态库文件
-
-L 参数是指定要链接的库文件的路径,
-L .
表示在当前目录下找库文件 - -lfunction 编译器查找链接库时有隐含的命名规则,即在给出的名字前加上lib,后面加上.so/.a来确定库的名称,因此-lfunction 就等同于 libfunction.a
- 此时即使删除libfunction.a文件,可执行文件8依旧可以运行
-
-
方式3:动态库链接
ren@rjx: gcc -o function.o -c function.c ren@rjx: gcc -fPIC -shared -o libfunction.so function.o ren@rjx: gcc -L . -o 8 main.c -lfunction
注解:
- -fPIC 表示编译为位置独立的代码,不用此选项的话编译后的代码是位置相关的,所以动态载入时时通过代码拷贝的方式来满足不同进程的需要,而不能达到真正代码段共享的目的
-
-shared 该选项指定生成动态链接库,不用该标志外部程序无法链接,相当于一个可执行文件
此时,可执行程序 8 还无法运行 = =
ren@rjx: ./8 1110011 10010 ./8: error while loading shared libraries: libfunction.so:cannot open shared object file : No such file or directory
Why:
gcc -L只负责在编译阶段的linking,且编译阶段的linking只负责把动态库的名字解析到可执行文件符号中(符号列表),但不记录路径,所以在 执行 时还需要为linker指定搜索路径。编译时刻的linking和运行时刻的linking不是一回事。有人说,可以参考有关ELF文件的生成与加载。-L参数在链接时使用,编译可以通过,但是并没有把这个库路径添加到运行时查找的路径中。
==============================================================================================================
解决办法:
a. 将动态库转移动系统缺省的库路径下
动态链接时、执行时搜索的路径顺序: ---- ----- 1. 编译目标代码时指定的动态库搜索路径; 2. 环境变量LD_LIBRARY_PATH指定的动态库搜索路径; 3. 配置文件/etc/ld.so.conf中指定的动态库搜索路径; 4. 默认的动态库搜索路径/lib; 5. 默认的动态库搜索路径/usr/lib.
ren@rjx: mv libfunction.so /usr/lib 亲测可以
b. 设置加载库的环境变量 LD_LIBRARY_PATH
ren@rjx: export LD_LIBRARY_PATH=`pwd` 亲测可以
c. 通过ldconfig将指定目录下的动态库添加到系统共享中,也就是在缓存文件/etc/ld.so.cache中追加指定目录下的共享库
ren@rjx: ldconfig /home/ren/8 /home/ren/8/目录下存在该动态库
========================================================================================================
此时,可执行文件就可以动态加载共享库文件libfunction.so啦!!!
可以利用
ldd
(List Dynamic Dependencies) 命令查看程序8需要哪些动态库了ren@rjx: ldd 8
e.(重点)上面几种方法在本地部署设置比较容易,另外export的方法还需要额外的shell脚本,当移植到其他机器上的时候就不再那么通用。
gcc在链接的时候有个-rpath选项。它可以把动态库的路径直接写到elf文件中去,指定编译好的程序在运行时动态库的目录。
-
makefile 文件
注意的地方:
makefile文件中的tab是命令的开始标志,tab后面的被当做命令执行,使用空格代替可能会造成格式的错误从而导致make识别makefile文件失败。
See Also: 跟我一起写makefile
-
多文件编译(makefile-original)
-
静态库链接(makefile-static)
-
动态库链接(makefile-dynamic)
-