【原创】举例来说下Linux中如何创建和使用动态库(libxxx.so)和静态库(libxxx.a)?

blogdaren 2021-10-26 抢沙发 521人次

关于动态库:

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.City
3. 将静态库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:表示链接时对其后指定的库使用动态链接

【05】-fPIC:表示编译时生成位置独立的代码,经常在编译共享库时需要指定此参数。
【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 showWebsite
0x03、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 )原创,转载请保留文章出处。

本文链接:【原创】举例来说下Linux中如何创建和使用动态库(libxxx.so)和静态库(libxxx.a)?

Free Web Hosting