http://my.oschina.net/u/1270343/blog/167137
这两天在看《Linux C程序设计大全》,吴岳编著,清华大学出版社。这本书是在一个培训机构看到的,在网上查了下该书的相关信息。从目录而言,该书涵盖了Linux下C程序设计的较多内容,包括C语言基础(主要讲解C语法结构)、C语言开发环境(主要介绍VIM使用、GCC使用、makefile编写、gdb使用)、Linux进程操作、Linux文件操作、Linux网络编程这几部分。阅读了该书几个小章节,总体而言,书的部分内容较充实,但是存在较多编辑错误,部分样例实际运行时与书中内容不符。
这里使用该书P192-P199的例子,总结下gcc编译静态及动态链接库的方法及步骤。
程序清单如下:
test.c文件内容:
test.h文件内容:
05 | extern int add( int a, int b); |
06 | extern int sub( int a, int b); |
07 | extern int mul( int a, int b); |
08 | extern int div ( int a, int b); |
main.c文件内容:
07 | printf ( "please input a and b\n" ); |
08 | scanf ( "%d%d" , &a, &b); |
10 | printf ( "The add : %d\n" , add(a, b)); |
11 | printf ( "The sub : %d\n" , sub(a, b)); |
12 | printf ( "The mul : %d\n" , mul(a, b)); |
13 | printf ( "The div : %d\n" , div (a, b)); |
1. 首先总结使用gcc生成静态库及使用静态库的方法:
在此例中,test.c用于编译生成静态库libtest.a,test.h为libtest.a对应的头文件。
step 1: 生成test.o目标文件,使用如下命令:
1 | [xgqin@xgqin-desktop so]$ ls |
3 | [xgqin@xgqin-desktop so]$ gcc -c test .c -o test .o |
4 | [xgqin@xgqin-desktop so]$ ls |
5 | main.c test .c test .h test .o |
在第一步中使用gcc -c test.c -o test.o首先生成test.o目标文件。
step 2: 使用ar将test.o打包成libtest.a静态库,使用如下命令:
1 | [xgqin@xgqin-desktop so]$ ar rcs -o libtest.a test .o |
2 | [xgqin@xgqin-desktop so]$ ls |
3 | libtest.a main.c test .c test .h test .o |
step 3:生成libtest.a静态库后,可以使用ar t参数查看libtest.a文件中包含哪些文件:
1 | [xgqin@xgqin-desktop so]$ ar t libtest.a |
3 | [xgqin@xgqin-desktop so]$ |
step 4: 编译main.c,并使用libtest.a静态库,链接时-l参数后不加空格指定所需链接的库,这里库名是libtest.a,但是只需要给出-ltest即可,ld会以libtest作为库的实际名字(例如-lm参数,实际表示链接libm库,也就是数学库):
1 | [xgqin@xgqin-desktop so]$ gcc -o app_static main.c -L. -ltest |
2 | [xgqin@xgqin-desktop so]$ ls |
3 | app_static libtest.a main.c test .c test .h test .o |
或使用如下方式编译main.c,并使用libtest.a静态库:
1 | [xgqin@xgqin-desktop so]$ gcc -o app_static main.c libtest.a |
2 | [xgqin@xgqin-desktop so]$ ls |
3 | app_static libtest.a main.c test .c test .h test .o |
step 5: 运行app_static:
1 | [xgqin@xgqin-desktop so]$ ./app_static |
8 | [xgqin@xgqin-desktop so]$ |
step 6: 使用readelf 查看app_static的符号表,观察sub, add, mul, div等函数是否处于app_static的.text段中。注意,section headers中指出 .text代码段的编号是13,而再symbol table '.symtab'中则显示add, div, mul, sub均处于13段中,也就是.text代码段中。因此libtest.a静态库经过链接后对应的函数代码已加入app_static程序代码段中,此为静态链接。
01 | [xgqin@xgqin-desktop so]$ readelf -a app_static |
04 | [Nr] Name Type Address Offset |
05 | Size EntSize Flags Link Info Align |
07 | [13] .text PROGBITS 0000000000400520 00000520 |
08 | 0000000000000274 0000000000000000 AX 0 0 16 |
09 | [14] .fini PROGBITS 0000000000400794 00000794 |
10 | 0000000000000009 0000000000000000 AX 0 0 4 |
11 | [15] .rodata PROGBITS 00000000004007a0 000007a0 |
12 | 0000000000000062 0000000000000000 A 0 0 8 |
14 | [29] .strtab STRTAB 0000000000000000 00001fb8 |
15 | 000000000000027b 0000000000000000 0 0 1 |
16 | Symbol table '.symtab' contains 72 entries: |
17 | Num: Value Size Type Bind Vis Ndx Name |
19 | 49: 00000000004006c4 20 FUNC GLOBAL DEFAULT 13 add |
21 | 53: 0000000000400701 19 FUNC GLOBAL DEFAULT 13 div |
23 | 64: 0000000000400610 179 FUNC GLOBAL DEFAULT 13 main |
24 | 65: 00000000004006ee 19 FUNC GLOBAL DEFAULT 13 mul |
26 | 70: 00000000004006d8 22 FUNC GLOBAL DEFAULT 13 sub |
注:《Linux C程序设计大全》中给出的三种链接静态库的方法在实际使用时均出现报错,如下所示:
01 | [xgqin@xgqin-desktop so]$ gcc main.c -llibtest.a -o app |
02 | /usr/bin/ld: cannot find -llibtest.a |
03 | collect2: error: ld returned 1 exit status |
04 | [xgqin@xgqin-desktop so]$ gcc main.c -ltest.a -o app |
05 | /usr/bin/ld: cannot find -ltest.a |
06 | collect2: error: ld returned 1 exit status |
07 | [xgqin@xgqin-desktop so]$ gcc main.c -ltest -o app |
08 | /usr/bin/ld: cannot find -ltest |
09 | collect2: error: ld returned 1 exit status |
10 | [xgqin@xgqin-desktop so]$ gcc -L. main.c -o app |
11 | /tmp/ccEpq6Fp.o: In function `main': |
12 | main.c:(.text+0x37): undefined reference to `add' |
13 | main.c:(.text+0x57): undefined reference to `sub' |
14 | main.c:(.text+0x77): undefined reference to `mul' |
15 | collect2: error: ld returned 1 exit status |
16 | [xgqin@xgqin-desktop so]$ gcc main.c -static ./libtest.a -o app |
17 | /usr/bin/ld: cannot find -lc |
18 | collect2: error: ld returned 1 exit status |
19 | [xgqin@xgqin-desktop so]$ |
具体出错原因,个人推测应与ld相关。
2. 再总结使用gcc生成动态库及使用动态库的方法:
step 1: 生成test.o目标文件,使用如下命令。此处需要添加-fPIC参数,该参数用于生成位置无关代码已共生成动态库使用:
1 | [xgqin@xgqin-desktop so]$ gcc -c -o test .o -fPIC test .c |
2 | [xgqin@xgqin-desktop so]$ ls |
3 | app_static libtest.a main.c test .c test .h test .o |
4 | [xgqin@xgqin-desktop so]$ file test .o |
5 | test .o: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), not stripped |
step 2: 使用-shared参数生成动态库,使用如下命令:
1 | [xgqin@xgqin-desktop so]$ gcc -shared -o libmyshare.so test .o |
2 | [xgqin@xgqin-desktop so]$ ls |
3 | app_static libmyshare.so libtest.a main.c test .c test .h test .o |
4 | [xgqin@xgqin-desktop so]$ file libmyshare.so |
5 | libmyshare.so: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, BuildID[sha1]=0x4053caa2d2d48b9b026576a69929a4a44abedcb0, not stripped |
上述两个命令可以合在一块,如下所示:
1 | [xgqin@xgqin-desktop so]$ gcc -shared -fPIC -o libmyshare.so test .c |
2 | [xgqin@xgqin-desktop so]$ file libmyshare.so |
3 | libmyshare.so: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, BuildID[sha1]=0x4053caa2d2d48b9b026576a69929a4a44abedcb0, not stripped |
step 3: 编译main.c,使用libmyshare.so动态库:
1 | [xgqin@xgqin-desktop so]$ gcc -o app_share main.c -L. -lmyshare |
2 | [xgqin@xgqin-desktop so]$ ls |
3 | app_share app_static libmyshare.so libtest.a main.c test .c test .h test .o |
4 | [xgqin@xgqin-desktop so]$ ldd app_share |
5 | linux-vdso.so.1 => (0x00007fff3972c000) |
6 | libmyshare.so => not found |
7 | libc.so.6 => /lib64/libc.so.6 (0x0000003039600000) |
8 | /lib64/ld-linux-x86-64.so.2 (0x0000003039200000) |
使用ldd命令查看app_share使用的动态库,发现提示libmyshare无法找到,如果直接执行app_share,则出现如下错误:
1 | [xgqin@xgqin-desktop so]$ ./app_share |
2 | ./app_share: error while loading shared libraries: libmyshare.so: cannot open shared object file : No such file or directory |
查看相关资料后,提示与LD_LIBRARY_PATH相关,首先使用export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH将当前目录加入LD_LIBRARY_PATH变量中。再次运行ldd app_share,如下:
01 | [xgqin@xgqin-desktop so]$ gcc -o app_share main.c -L. -lmyshare |
02 | [xgqin@xgqin-desktop so]$ ldd app_share |
03 | linux-vdso.so.1 => (0x00007fff26b70000) |
04 | libmyshare.so => not found |
05 | libc.so.6 => /lib64/libc.so.6 (0x0000003039600000) |
06 | /lib64/ld-linux-x86-64.so.2 (0x0000003039200000) |
07 | [xgqin@xgqin-desktop so]$ export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH |
08 | [xgqin@xgqin-desktop so]$ echo $LD_LIBRARY_PATH |
10 | [xgqin@xgqin-desktop so]$ ldd app_share |
11 | linux-vdso.so.1 => (0x00007fff55dbe000) |
12 | libmyshare.so => ./libmyshare.so (0x00007fb8c3b18000) |
13 | libc.so.6 => /lib64/libc.so.6 (0x0000003039600000) |
14 | /lib64/ld-linux-x86-64.so.2 (0x0000003039200000) |
15 | [xgqin@xgqin-desktop so]$ ./app_share |
注:注意观察export前后的两次ldd执行时,libmyshare.so的指向情况。
另一种编译main.c,并链接libmyshare.so的方式如下(该方式通过./libmyshare.so直接指定使用当前目录下的libmyshare.so文件:
01 | [xgqin@xgqin-desktop so]$ gcc -o app_share main.c ./libmyshare.so |
02 | [xgqin@xgqin-desktop so]$ ldd app_share |
03 | linux-vdso.so.1 => (0x00007fff066c3000) |
04 | ./libmyshare.so (0x00007f945b6b0000) |
05 | libc.so.6 => /lib64/libc.so.6 (0x0000003039600000) |
06 | /lib64/ld-linux-x86-64.so.2 (0x0000003039200000) |
07 | [xgqin@xgqin-desktop so]$ ./app_share |
14 | [xgqin@xgqin-desktop so]$ |
本文中使用到的readelf属于binutils工具集,感兴趣的读者可以访问google或者百度。