现在有如下代码:
main.c:
#include<stdio.h> void print() { printf("print in main.c \n"); } int main() { func(); return 0; }
temp.c:
#include<stdio.h> void print() { printf("print in temp.c \n"); } void func() { print(); }
将temp.c编译成动态库:gcc -fPIC -shared -o libtemp.so temp.c
然后构建可运行程序:gcc -o main main.c libtemp.so
运行之后发现输出:
printf("print in main.c \n");
这是因为:
如果一个全局符号在多个库中进行了定义,那么多该符号的引用会被绑定到扫描库时找到的第一个定义,其中扫描顺序是按照这些库在静态链接命令行中列出时从左到右的顺序。
我们可以通过readelf -r libtemp.so 查看动态relocation section的内容:
Relocation section '.rela.plt' at offset 0x480 contains 3 entries: Offset Info Type Sym. Value Sym. Name + Addend 000000201018 000200000007 R_X86_64_JUMP_SLO 0000000000000000 puts + 0 000000201020 000800000007 R_X86_64_JUMP_SLO 00000000000005ec print + 0 000000201028 000500000007 R_X86_64_JUMP_SLO 0000000000000000 __cxa_finalize + 0
这说明print并没有绑定到当前库中的print符号上。那么,如果要想库中的全局符号绑定到该符号在库中的定义上,方法如下:
gcc -fPIC -shared -Wl,Bsymbolic -o libtemp.so temp.c
加上Bsymbolic之后,再次采取readelf -r 查看动态库:
Relocation section '.rela.plt' at offset 0x480 contains 2 entries: Offset Info Type Sym. Value Sym. Name + Addend 000000201018 000200000007 R_X86_64_JUMP_SLO 0000000000000000 puts + 0 000000201020 000500000007 R_X86_64_JUMP_SLO 0000000000000000 __cxa_finalize + 0
这个时候说明print已经不采用动态解析了,因为已经绑定到当前库中的定义上了。为了确实这一点,objdump -d libtemp.so输出如下:
00000000000005cc <print>: 5cc: 55 push %rbp 5cd: 48 89 e5 mov %rsp,%rbp 5d0: 48 8d 3d 5f 00 00 00 lea 0x5f(%rip),%rdi # 636 <_fini+0xe> 5d7: e8 04 ff ff ff callq 4e0 <puts@plt> 5dc: c9 leaveq 5dd: c3 retq 00000000000005de <func>: 5de: 55 push %rbp 5df: 48 89 e5 mov %rsp,%rbp 5e2: b8 00 00 00 00 mov $0x0,%eax 5e7: e8 e0 ff ff ff <span style="color:#FF0000;">callq 5cc <print></span> 5ec: c9 leaveq 5ed: c3 retq 5ee: 66 90 xchg %ax,%ax
最后,如果一个库中的函数不对外公开,只是内部使用,那么最好是在函数的定义前面加上static。