【原创】举例来说下Linux中如何创建和使用动态库(libxxx.so)和静态库(libxxx.a)?
关于动态库:
0x01、什么是动态库?
动态库本质就是一种二进制程序, 动态库在和其他目标文件进行链接时,并不会被打包进最终的可执行文件,而是在将来执行期间以独立的模块动态载入。
0x02、动态库的命名格式:
libxxx.so:其中lib为库前缀,xxx为库名称,.so为库扩展名称。
0x03、如何创建动态库:
假设我们要创建一个动态库:libwebsite.so
1. 设定当前测试路径为:
$ /tmp/website/website.c
2. 编写库源文件:website.c
#include <stdio.h> void showWebsite(); void showWebsite() { printf("http://www.phpcreeper.com\n"); }
3. 编译出目标文件:
$ gcc website.c -c -fPIC -o website.o
其中参数-fPIC 表示创建与位置无关的代码(PIC:position independent code),目的是为了能够在多个应用程序间共享。
4. 编译出动态库文件:
$ gcc website.o -shared -o libwebsite.so
0x04、如何使用动态库:
1. 编写测试项目源文件:main.c
#include <stdio.h> int main() { showWebsite(); return 0; }
2. 编译后直接执行项目主程序:
[root@node1 /tmp/website]$ gcc main.c && ./a.out /tmp/ccGH0ZcN.o: In function `main': main.c:(.text+0xa): undefined reference to `showWebsite' collect2: ld 返回 1
这时会报错提示:找不到`showWebsite`这个符号,很明显源文件就没有定义,所以势必报错。
3. 尝试链接website库解决上述报错:
[root@node1 /tmp/website]$ gcc -L./ -lwebsite main.c && ./a.out ./a.out: error while loading shared libraries: libwebsite.so: cannot open shared object file: No such file or directory
运行后继续报错提示:cannot open shared object file: No such file or directory
看上去有些奇怪:链接的时候-L和-l参数成功定位到了website库,但是执行的时候却提示找不到,这是因为链接期间和执行期间定位库路径的方式是不一样的,执行期间定位库路径的优先顺序是:
DT_RPATH ---> LD_LIBRARY_PATH ---> /etc/ld.so.cache ---> /lib64 或者 /lib 或者 /usr/lib
我们的解决方案有两种:
【1】将 libwebsite.so 放到 /lib64 目录下,切记测试完记得删除,防止污染标准库目录;
参数-L:表示要链接的库所在目录
参数-l :表示要链接的库名称,注意既可以是动态库,也可以是静态库,默认先查找动态库,如果找不到,则继续查找同名的静态库。
【2】编译时通过指定 --rpath 参数,其语法需要遵循格式"Wl, ......",其中Wl就是用来告诉GCC,其后面的内容就是传递给linker的option,比如:-Wl, --rpath /path/to/lib,其含义为:优先从/path/to/lib查找运行时库文件,换句话,其优先级要在LD_LIBRARY_PATH 和 /etc/ld.so.conf之上。
4. 我们采用上述第一个方案,再次尝试执行,顺利输出:
[root@node1 /tmp/website]$ gcc -L./ -lwebsite main.c && ./a.out http://www.phpcreeper.com
5. 尝试将 /lib64/libwebsite.so 重命名为 /lib64/libwebsite.so.dist,然后再次运行:
[root@node1 /tmp/website]$ gcc -L./ -lwebsite main.c && ./a.out /usr/bin/ld: cannot find -lwebsite collect2: ld 返回1
结果报告:/usr/bin/ld: cannot find -lwebsite,这充分说明动态库是在执行期间动态载入的,换句话就是说我们的主项目在运行时是严格依赖动态库的,动态库文件必须配套有效存在。
关于静态库:
0x01、什么是静态库?
和动态库一样,静态库本质也是一种二进制程序, 静态库在和其他目标文件进行链接时,会被直接打包进最终的可执行文件。
0x02、静态库命名格式:
libxxx.a:其中lib为库前缀,xxx为库名称,.a为库扩展名称。
0x03、如何创建静态库:
假设我们要创建一个静态库:libcity.a
1. 设定当前测试路径为:
$ /tmp/website/city.c
2. 编写库源文件:city.c
#include <stdio.h> void showCity(); void showCity() { printf("ShanXi.YunCheng.City\n"); }
3. 编译出目标文件:
$ gcc city.c -c -o city.o
4. 编译出静态库文件:
$ ar rcs libcity.a city.o
参数r:表示将目标文件插入静态库中。
参数c:表示创建静态库,不管库是否存在。
参数s:表示将一个目标文件索引写到库中,或者更新一个已存在的目标文件索引。
0x04、如何使用静态库:
1. 修改测试项目源文件:main.c
#include <stdio.h> int main() { showCity(); return 0; }
2. 编译后直接执行项目主程序:
[root@node1 /tmp/website]$ gcc main.c -L./ -lcity && ./a.out ShanXi.YunCheng.City3. 将静态库libcity.a删除,然后再次执行:
[root@node1 /tmp/website]$ ./a.out ShanXi.YunCheng.City
和预期的一样,顺利执行成功,这说明静态库已经被打包进了a.out这个可执行文件当中,换句话项目主程序已经和外部的静态库没有任何依赖或瓜葛了,拿到任何地方或其他任何平台都可以独立执行,可见静态库对于项目移植性很友好。
最后再罗列一些常用的库相关命令
0x01、g++/gcc编译选项【01】-shared:表示编译时生成动态链接库。
【02】-static:表示链接时对所有的库使用静态链接,一般不推荐使用这种方式。
【03】-Wl,-Bstatic:表示链接时对其后指定的库使用静态链接。
【04】-Wl,-Bdynamic:表示链接时对其后指定的库使用动态链接。
【06】-L:表示链接时上哪个目录里去寻找动态库或静态库(注意是大写L)。
【07】-l :表示链接时要链接哪个动态库或静态库,默认优先链接动态库,如果找不到,则继续链接同名的静态库,但是如果编译时额外指定了-static参数,则表明链接时将对所有的库使用静态链接(注意是小写l)。
【08】-Wall :表示生成所有警告信息。【09】-ggdb :表示尽可能的生成gdb可以使用的调试信息。
【10】-g :表示生成调试信息。
【11】-c :表示只进行预处理、编译和汇编,但是不会进行链接,也就是只生成目标文件(.o文件) 。
【12】-Wl, options :把参数(options)传递给链接器ld,如果options 中间有逗号,就将options分成多个选项,然后再传递给链接器。
0x02、nm命令
有时候可能需要查看一个库中到底有哪些函数,nm命令可以打印出库中的涉及到的所有符号。库既可以是静态的也可以是动态的。nm列出的符号有很多,常见的有三种:
【01】一种是在库中被调用,但并没有在库中定义(表明需要其他库支持),用U表示;
【02】一种是库中定义的函数,用T表示,这是最常见的;
【03】一种是所谓的弱态符号,它们虽然在库中被定义,但是可能被其他库中的同名符号覆盖,用W表示。
[root@node1 /tmp/website]$nm libwebsite.so 0000000000200678 a _DYNAMIC 0000000000200810 a _GLOBAL_OFFSET_TABLE_ w _Jv_RegisterClasses 0000000000200650 d __CTOR_END__ 0000000000200648 d __CTOR_LIST__ 0000000000200660 d __DTOR_END__ 0000000000200658 d __DTOR_LIST__ 0000000000000640 r __FRAME_END__ 0000000000200668 d __JCR_END__ 0000000000200668 d __JCR_LIST__ 0000000000200838 A __bss_start w __cxa_finalize@@GLIBC_2.2.5 0000000000000590 t __do_global_ctors_aux 00000000000004d0 t __do_global_dtors_aux 0000000000200670 d __dso_handle w __gmon_start__ 0000000000200838 A _edata 0000000000200848 A _end 00000000000005c8 T _fini 0000000000000460 T _init 00000000000004b0 t call_gmon_start 0000000000200838 b completed.6364 0000000000200840 b dtor_idx.6366 0000000000000550 t frame_dummy U puts@@GLIBC_2.2.5 000000000000057c T showWebsite0x03、ldd命令
ldd命令可以查看一个可执行程序所依赖的共享库。
[root@node1 /tmp/website]$ ldd a.out linux-vdso.so.1 (0x00007fff43dff000) libwebsite.so => /lib64/libwebsite.so (0x00007fe7d13f5000) libc.so.6 => /lib64/libc.so.6 (0x0000003d18c00000) /lib64/ld-linux-x86-64.so.2 (0x0000003d18800000)
版权声明:除非注明,本文由( blogdaren )原创,转载请保留文章出处。