静态库:以静态文件的形式在编译的时候和将要运行的程序编译在一块,如果有多个程序需要使用静态库,那么就会在多个程序中就都会多出一个静态库的大小。
动态库:与静态库不同,动态库是在多个程序运行时共享,即多个程序会共享这个库,而不是每个程序都包含这个库,只会共享。
既然动态库比静态库有这么明显的优势,为什么还会存在静态库?
答案就是在调用函数的速度,静态库比动态库的速度快。
静态库在什么方面使用呢?
静态库:对空间要求低,对时间要求高的核心程序。
动态库:对事件要求低,对空间要求高的程序。
静态库的制作:
ar rcs libmylib.a file1.o
注意:必须是lib开头,.a为文件后缀名的规则定义静态文件
步骤:
1. 将 .c 生成 .o文件
gcc -c add.c -o add.o
2. 使用 ar 工具生成静态库
ar rcs libxx.a add.o sub.o
3. 编译静态库到可执行文件
gcc mian.c libxx.a -o main
zhangfengzhou@ubuntu:~/learn/linux_learn/static$ gcc -c add.c -o add.o
zhangfengzhou@ubuntu:~/learn/linux_learn/static$ gcc -c sub.c -o sub.o
zhangfengzhou@ubuntu:~/learn/linux_learn/static$ ls
add.c add.o sub.c sub.o
zhangfengzhou@ubuntu:~/learn/linux_learn/static$ ar rcs libmath.a add.o sub.o
zhangfengzhou@ubuntu:~/learn/linux_learn/static$ ls
add.c add.o libmath.a sub.c sub.o
zhangfengzhou@ubuntu:~/learn/linux_learn/static$ file libmath.a
libmath.a: current ar archive
zhangfengzhou@ubuntu:~/learn/linux_learn/static$
头文件守卫,防止头文件被重复包含:
1 #ifndef _MYMATH_H
2 #define _MYMATH_H
3
4 int add(int, int);
5 int sub(int, int);
6
7 #endif
比较完整的静态库制作步骤:
zhangfengzhou@ubuntu:~/learn/linux_learn/static$ ls
add.c add.o libmymath.a main main.c mymath.h sub.c sub.o
zhangfengzhou@ubuntu:~/learn/linux_learn/static$ mkdir inc
zhangfengzhou@ubuntu:~/learn/linux_learn/static$ mv *.h inc
zhangfengzhou@ubuntu:~/learn/linux_learn/static$ mkdir lib
zhangfengzhou@ubuntu:~/learn/linux_learn/static$ mv *.a lib
zhangfengzhou@ubuntu:~/learn/linux_learn/static$ ls
add.c add.o inc lib main main.c sub.c sub.o
zhangfengzhou@ubuntu:~/learn/linux_learn/static$ gcc main.c ./lib/libmymath.a -o hello -I ./inc
zhangfengzhou@ubuntu:~/learn/linux_learn/static$ ls
add.c add.o hello inc lib main main.c sub.c sub.o
zhangfengzhou@ubuntu:~/learn/linux_learn/static$ ./hello
6+2=8
6-2=4
zhangfengzhou@ubuntu:~/learn/linux_learn/static$
需要lib, inc 目录,lib目录是存放静态库文件的目录,inc是存放头文件的目录。
动态库制作:
1. 将 .c 生成 .o文件 (生成与位置无关的代码 -fPIC)
gcc -c add.c -o add.o -fPIC
2. 使用 gcc -shared 制作动态库
gcc -shared -fPIC -o lib库名.so add.o sub.o
3. 编译可执行程序时,指定所使用的动态库。-l: 指定库名 -L:指定路径
gcc main.c -o main -lmymath -L ./lib -I ./inc
注意:
1. mymath 是动态库名称 libmymath.so 去掉lib 和so后缀之后获取库名称
2. ./lib 是存放libmymath.so 动态库的目录
3. ./inc 是存放动态库头文件的目录
4. 运行可执行程序
./main
编译时错误:
zhangfengzhou@ubuntu:~/learn/linux_learn/dynamic$ ls
add.c add.o inc lib main.c sub.c sub.o
zhangfengzhou@ubuntu:~/learn/linux_learn/dynamic$ gcc main.c -o main -lmymath -L./lib -I./inc
zhangfengzhou@ubuntu:~/learn/linux_learn/dynamic$ ls
add.c add.o inc lib main main.c sub.c sub.o
zhangfengzhou@ubuntu:~/learn/linux_learn/dynamic$ ./main
./main: error while loading shared libraries: libmymath.so: cannot open shared object file: No such file or directory
zhangfengzhou@ubuntu:~/learn/linux_learn/dynamic$
报错原因分析:
原因:
链接器: 工作于链接阶段 工作时需要 -l 和 -L
动态链接器:工作于程序运行阶段,工作时需要提供动态库所在目录位置
通过环境变量: LD_LIBRARY_PATH=动态路径库
./main 成功 (临时生效,终端重启环境变量失效)
会去那么几个固定的地方去找动态库,那么这个地方就是环境变量
方法一:
export LD_LIBRARY_PATH=./lib 导出这个环境变量的新值,覆盖旧值,然后就可以找到动态库
解决方式:
zhangfengzhou@ubuntu:~/learn/linux_learn/dynamic$ export LD_LIBRARY_PATH=./lib
zhangfengzhou@ubuntu:~/learn/linux_learn/dynamic$ ./main
6+2=8
6-2=4
zhangfengzhou@ubuntu:~/learn/linux_learn/dynamic$
但是以上有个问题,新开一个窗口,再次执行./main 就会报错,原因是设置的环境变量失效,设置的环境变量只对当前的进程有效。解决方法:
方法二:
永久生效:写入 终端配置文件 .bashrc (建议是用绝对路径,不要使用 ./lib)
1) vim ~/.bashrc
2) 写入 export LD_ LIBRARY_PATH=动态库路径
// ~/.bashrc
export LD_LIBRARY_PATH=./lib
3). .bashrc 或者 source .bashrc 或者重启终端--> 让修改后的 .bashrc 生效
4)./main 顺利执行
为什么c语言从来不用指定搜索路径就可以直接使用,难道是因为c标准库的路径已经是默认搜索的路径,如果确实如此,那么我们是否可以将 libmymath.so 放到标准库的目录下,不用添加动态库搜索路径,也是可以执行的呢?
方法三:
zhangfengzhou@ubuntu:/lib/x86_64-linux-gnu$ pwd
/lib/x86_64-linux-gnu
zhangfengzhou@ubuntu:~/learn/linux_learn/dynamic$ cd lib
zhangfengzhou@ubuntu:~/learn/linux_learn/dynamic/lib$ sudo cp libmymath.so /lib/x86_64-linux-gnu/
[sudo] password for zhangfengzhou:
zhangfengzhou@ubuntu:~/learn/linux_learn/dynamic/lib$ cd ..
zhangfengzhou@ubuntu:~/learn/linux_learn/dynamic$ ./main
6+2=8
6-2=4
查看可执行文件在运行时所依赖的动态库以及其所在路径
zhangfengzhou@ubuntu:~/learn/linux_learn/dynamic$ ldd main
linux-vdso.so.1 => (0x00007fff611f7000)
libmymath.so => /lib/x86_64-linux-gnu/libmymath.so (0x00007f9f9b5d9000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f9f9b20f000)
/lib64/ld-linux-x86-64.so.2 (0x00007f9f9b7db000)
zhangfengzhou@ubuntu:~/learn/linux_learn/dynamic$
5)拷贝自定义动态库 到 /lib (标准C库所在的目录)
方法四:
6)配置文件法:
1. 修改/etc/ld.so.conf
sudo vi /etc/ld.so.conf 添加你的共享库路径
2. 更新查找共享库的路径
sudo ldconfig -v
3. 测试你的程序可否找到共享库
ldd main
具体执行步骤:
zhangfengzhou@ubuntu:~$ sudo vim /etc/ld.so.conf
[sudo] password for zhangfengzhou:
//追加搜索路径
1 include /etc/ld.so.conf.d/*.conf
2 /home/zhangfengzhou/learn/linux_learn/dynamic/lib
zhangfengzhou@ubuntu:~$ sudo ldconfig -v
zhangfengzhou@ubuntu:~/learn/linux_learn/dynamic$ ldd main
linux-vdso.so.1 => (0x00007ffc13bed000)
libmymath.so => /home/zhangfengzhou/learn/linux_learn/dynamic/lib/libmymath.so (0x00007f03ef9ed000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f03ef623000)
/lib64/ld-linux-x86-64.so.2 (0x00007f03efbef000)
objdump -dS hello > out 是反汇编 hello二进制文件生成汇编代码文件,并输出到out文件中
zhangfengzhou@ubuntu:~/learn/linux_learn/static$ objdump -dS hello >out
zhangfengzhou@ubuntu:~/learn/linux_learn/static$ ls
add.c add.o hello inc lib main main.c out sub.c sub.o
zhangfengzhou@ubuntu:~/learn/linux_learn/static$ vim out
zhangfengzhou@ubuntu:~/learn/linux_learn/static$
125 0000000000400526 <main>:
126 400526: 55 push %rbp
127 400527: 48 89 e5 mov %rsp,%rbp
128 40052a: 48 83 ec 20 sub $0x20,%rsp
129 40052e: 89 7d ec mov %edi,-0x14(%rbp)
130 400531: 48 89 75 e0 mov %rsi,-0x20(%rbp)
131 400535: c7 45 f8 06 00 00 00 movl $0x6,-0x8(%rbp)
132 40053c: c7 45 fc 02 00 00 00 movl $0x2,-0x4(%rbp)
133 400543: 8b 55 fc mov -0x4(%rbp),%edx
134 400546: 8b 45 f8 mov -0x8(%rbp),%eax
135 400549: 89 d6 mov %edx,%esi
136 40054b: 89 c7 mov %eax,%edi
137 40054d: e8 48 00 00 00 callq 40059a <add> # add 函数此处是有具体地址
138 400552: 89 c1 mov %eax,%ecx
139 400554: 8b 55 fc mov -0x4(%rbp),%edx
140 400557: 8b 45 f8 mov -0x8(%rbp),%eax
141 40055a: 89 c6 mov %eax,%esi
142 40055c: bf 44 06 40 00 mov $0x400644,%edi
143 400561: b8 00 00 00 00 mov $0x0,%eax
144 400566: e8 95 fe ff ff callq 400400 <printf@plt> #printf是动态库函数,无地址
145 40056b: 8b 55 fc mov -0x4(%rbp),%edx
146 40056e: 8b 45 f8 mov -0x8(%rbp),%eax
147 400571: 89 d6 mov %edx,%esi
148 400573: 89 c7 mov %eax,%edi
149 400575: e8 34 00 00 00 callq 4005ae <sub>
150 40057a: 89 c1 mov %eax,%ecx
151 40057c: 8b 55 fc mov -0x4(%rbp),%edx
152 40057f: 8b 45 f8 mov -0x8(%rbp),%eax
153 400582: 89 c6 mov %eax,%esi
154 400584: bf 4e 06 40 00 mov $0x40064e,%edi
155 400589: b8 00 00 00 00 mov $0x0,%eax
156 40058e: e8 6d fe ff ff callq 400400 <printf@plt>
157 400593: b8 00 00 00 00 mov $0x0,%eax
158 400598: c9 leaveq
159 400599: c3 retq
从上面的反汇编的结果来看,动态没有具体地址,在运行的时候才会进行地址回填。
@plt 表示动态库函数
zhangfengzhou@ubuntu:~/learn/linux_learn/dynamic$ file libmymath.so
libmymath.so: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, BuildID[sha1]=d1b8c90343011313d13b0a4c0642103468b6a216, not stripped
LSB 表示小尾端,与大尾端相对,是一种存储数据的方式。
以上解决动态库加载时出现问题的四种解决方法。
参考: