1 代码样例
1. /**************************************************************************/
2. /*add.c*/
3. int add(int x, int y)
4. {
5. return x + y;
6.
7. return 0;
8. }
9. /*************************************************************************/
10. 然后add.h代码为:
11. /*add.h*/
12. #ifndef _ADD_H_
13. #define _ADD_H_
14.
15. int add(int, int);
16.
17. #endif
18. /***************************************************************************/
19. main函数代码:
20. /*main.c*/
21. #include <stdio.h>
22.
23. int main(void)
24. {
25. printf("2+3= %d\n", add(2,3));
26. return 0;
27. }
28. /**********************************************************************************/
2静态库的编译和使用
2.1 静态库的编译流程
将add.c 单独的源文件编译成静态库libadd.a
1. gcc -c add.c //生成 add.o
2. ar crv libadd.a add.o // 生成静态库libadd.a
2.2 静态库的使用
1. gcc -o main main.c -I. -L. -ladd (注: -I -L -l的意思在文章后面)
2. 也可以: gcc -o main main.c -I ./libadd.a (注:这里 ./libadd.a 可以是相对地址或者绝对地址 如:./lib/libadd.a , /home/lisi/lib/lib.a)
3 动态库的编译和使用
3.1 动态库的编译流程
1. gcc -fPIC -c add.c // 生成add.o
2. gcc -shared -o libadd.so add.o // 或者使用 ar crv libadd.so add.o
上面可合并成一行: gcc -fPIC -shared -o libadd.so add.c
注: -fPIC 使输出的对象模块是按照可重定位地址方式生成的(即与位置无关).
-shared 指定把对应的源文件生成对应的动态链接库库文件libstr.so文件
3.2 动态库的使用
3.2.1 隐式调用
代码编写与静态库一样,不需要包含到处函数的头文件,若主函数是C++程序(即.cpp), 则需要在main.cpp中用extern "C"{} 包含被调用的函数(add.c)的头文件(这里需要包含头文件是与.cpp和.c混合编译有关,同静态\动态库无关),用g++或者用gcc(加上一个链接的参数 -lstdc++)编译.
1 )代码编写:与静态库一样
2 ) 编译main.c 生成可执行程序(动态库隐式调用的使用)
1. // 第一种方式
2. gcc -o main main.c ./libadd.so
3. // 第二种方式
4. gcc -o main main.c -L. libadd.so
3.2.1.1 问题1
./main: error while loading shared libraries,cannot open shared object file: No such file or directory?
解决方案:
方法一: libadd.so放到/usr/lib 或 /lib 中去.
方法二: export LD_LIBRARY_PATH=$(pwd) 或者 export LD_LIBRARY_PATH=./ ,
可写入环境变量来支持当前目录寻找动态链接库
方法三: 在/etc/ld.so.conf文件加入我们生成的库目录(只支持绝对路径),然后执行 #/sbin/ldconfig.
关于 /etc/ld.so.conf 可以参看:ldd命令
1. // 第三种方式
2. // 将libadd.so 拷贝到目录 /user/lib 或者 /lib 中,然后执行
3. gcc -o main main.c libadd.so //此时不需要指定搜索路径
3.2.1.2 问题2
gcc编译文件时出现undefined reference to 'xxxx'的错误?
这是链接错误,不是编译错误,源程序代码本身没有问题,是你的编译时参数用的不对.你没有指定连接程序要用到的库,比如你的程序里用到了一些数学函数sqrt ,那么你就要在编译参数里指定程序要链接数学库。
1. gcc -o math math.c -lm
3.2.2 显示调用
3.2.2.1 dlopen
在main.c 中增加头文件 #include <dlfcn.h>引入dlopen , dlsym , dlclose , dlerror 几个系统调用
主要介绍dlopen()系统调用
第一个参数:指定共享库的名称,将会在下面位置查找指定的共享库.
-环境变量 LD_LIBARARY_PATH列出的用冒号间隔的所有目录
-文件/etc/ld.so.cache重找到库的列表,用 ldconfig 维护
-目录/usr/lib
-目录/lib
-当前目录
第二个参数:指定打开共享库.
-RTLD_NOW:将共享库重的所有函数加载到内存
-RTLD_LAZY:会退后共享库中的函数的加载操作,知道调用dlsym()时方加载某函数
返回值:返回动态库的句柄
1. #include <stdio.h>
2. #include <dlfcn.h>
3.
4. int main()
5. {
6. int (*func)(int , int );
7. void *dl = dlopen("./libadd.so", RTLD_LAZY);
8. if (dl == NULL)
9. return -1;
10. "add");
11. if (func == NULL)
12. return -1;
13. "2+3=%d", func(2, 3));
14. dlclose(dl);
15. return 0;
16. }
3.2.2.2 编译
编译main.c生成可执行程序,动态库已创建
1. gcc -o main main.c -lld // 注:使用libld.so库进行系统调用
4 gcc中一些参数的作用
4.1 -l 参数和 -L参数
-l 参数就是用来指定程序要链接的库, -l 参数紧接着就是库名, 那么库名跟真正的库文件名有什么关系呢?拿数学库来说,他的库名是m ,他的库文件名是 libm.so ,很容易看出,把库文件名的lib和尾.so 去掉就是库名了. 如第三方库名字叫做libtest.so 那么我们只需要把libtest.so 拷贝到 /usr/lib 或者 /usr/local/lib里,在编译时加上 -ltest 参数,我们就能用上libtest.so库了.
注意:要用libtest.so 库里的函数,我们还需要与libtest.so 配套的头文件.eg:gcc -o test test.c -ltest(libtest.so 的隐式调用)
—L 参数指定程序要链接的库所在的目录,可以指定库所在目录的相对路径或者绝对路径。
1. gcc -o main main.c -L. -ladd
2. gcc -o main main.c -L/home/user/test/ -ladd
4.2 -include 和-I参数
-include 用来包含头文件,但是一般情况下包含头文件都在源码里用#include xxx 实现, -include 参数很少用. -I参数用来指定头文件目录.
/usr/include 目录一般是不用指定的,gcc 知道去那里找,但是如果头文件不在 /usr/include 里我们就要用 -I参数指定. 比如 头文件放在 /myinclude目录里,那么便以命令行就加上-I/myinclude 参数,如果不加就会得到 "xx.h: No such file or deirectory "的错误. (注-I参数也可以用相对路径,比如当前目录,可以用 -I. 来指定).也可以通过设置环]境变量C_INCLUDE_PATH来指定头文件目录,怎样设置可参看
5 静态库和动态库链接时的搜索路径
5.1 静态库链接时搜索路径
1. ld会去找gcc命令中的参数-L
2. 再找gcc的环境变量LIBRARY_PATH
3. 再找内定目录 /lilb、/usr/lib、/usr/local/lib 这是当初compile gcc时写在程序内的
5.2 动态连接时执行时的搜索路径
1. 编译目标代码时指定的动态库搜索路径
gcc 使用-R或-rpath选项来在编译时就指定库的查找路径,并且该库的路径信息保存在可执行文件中,运行时它会直接到该路径查找库。
1. // 使用以下两种方式都可以,-Wl选项告诉编译器将后面的参数传递给连接器
2. #gcc -o main main.c -L. ladd -Wl,-R.
3. #gcc -o main main.c -L. ladd -Wl,rpath=.
4. #gcc -o main main.c -L. ladd -Wl,rpath .
2. 环境变量LD_LIBRARY_PATH指定的动态库搜索路径
LD_LIBRARY_PATH设定是全局的,过多的使用可能会影响到其他应用程序的运行,所以多用在调试。
1. // 当前调试时可以设置LD_LIBRARY_PATH环境变量
2. #LD_LIBRARY_PATH=.
3. #export LD_LIBRARY_PATH
4. // 或
5. #setenv LD_LIBRARY_PATH
3. 配置文件 /etc/ld.so.conf 中指定的动态库搜索路径
设置可参考 ldd命令
4. 默认的动态库搜索路径 /lib
5. 默认的动态库搜索路径 /usr/lib
5.3 有关环境变量
1. LIBRARY_PATH环境变量:指定程序静态链接库文件搜索路径
2. LD_LIBRARY_PATH环境变量:指定程序动态链接库文件搜索路径
5.4 库的链接时路径和运行时路径
现代连接器在处理动态库时将链接时路径(Link-time path)和运行时路径(Run-time path)分开,用户可以通过-L指定链接时库的路径,通过-R(或-rpath)指定程序运行时库的路径,大大提高了库应用的灵活性。
6 静态库和动态库的命名规则
6.1 Linux静态库的命名规则
static library filename =
lib +<library name> +
.a
静态库文件名中间那一部分是库的实际名称,链接器需要使用这个名称来进行链接。
6.2 Linux动态库命名规则
dynamic library filename =
lib + <library name> +
.so + <library version information>
动态库文件名中间那一部分是库的实际名称,链接器会在随后的构建时库文件搜索过程中使用这个名称,装在器也会在运行时库文件搜索过程中使用这个名称。
6.3 动态库的版本信息
dynamic library version information =
<M>.<m>.<p>
其中,每个助记符可能使用一个或多个数字来表示
M:主版本号
m:次版本号
p:补丁(很小的代码改动)版本号
6.4 动态库的soname
library
soname = lib + <library
name> +
.so + <library
major version digit(s)>
举例来说:
库文件libz.so.1.2.3.4的soname则是libz.so.1
实际上只有主版本号的数字在库soname中起作用,这意味着即使库的次版本号是不同的,也可能使用同一个soname值来表示。
动态库的soname通产由链接器嵌入二进制库文件的专有ELF字段中。通常使用特定的链接器选项,将表示库soname的字符串传递给链接器。
1. // eg:
2. $ gcc -shared <list of object files> -Wl, -soname, libfoo.so.1 -o libfoo.so.1.0.0
7 参考文章
参考文章6:linking with -Wl,-rpath and $(prefix)