Linux下动态链接实现原理(2)

如上最后一行,这条记录记录了在当前编译单元中,哪儿对 g_share 进行了引用,其中 offset 用于指明需要修改的位置在该段中的偏移,TYPE 则指明要怎样去修改,因为 cpu 的寻址方式不是唯一的,寻址方式不同,地址的形式也有所不同,这个 type 用于指明怎么去修改, value 则是配合 type 来最后计算该符号地址的。

有了如上信息,链接器在把目标文件合并成一个可执行文件并分配好各段的加载地址后,就可以重新计算那些需要重定位的符号的具体地址了, 如下我们可以看到在可执行文件中,对 g_share(0x40496处), g_func(0x4047a处)的访问已经被修改成了具体的地址:

-bash-3.00$ gcc -o am a.o main.o -bash-3.00$ objdump -S am // skip some of the ouput extern int g_func(int a); int main() { 400468: 55 push %rbp 400469: 48 89 e5 mov %rsp,%rbp 40046c: 48 83 ec 10 sub $0x10,%rsp int a = 42; 400470: c7 45 fc 2a 00 00 00 movl $0x2a,0xfffffffffffffffc(%rbp) a = g_func(a); 400477: 8b 7d fc mov 0xfffffffffffffffc(%rbp),%edi 40047a: e8 0d 00 00 00 callq 40048c <g_func> 40047f: 89 45 fc mov %eax,0xfffffffffffffffc(%rbp) return 0; 400482: b8 00 00 00 00 mov $0x0,%eax } 400487: c9 leaveq 400488: c3 retq 400489: 90 nop 40048a: 90 nop 40048b: 90 nop 000000000040048c <g_func>: int g_share = 1; int g_func(int a) { 40048c: 55 push %rbp 40048d: 48 89 e5 mov %rsp,%rbp 400490: 89 7d fc mov %edi,0xfffffffffffffffc(%rbp) g_share += a; 400493: 8b 45 fc mov 0xfffffffffffffffc(%rbp),%eax 400496: 01 05 dc 03 10 00 add %eax,1049564(%rip) # 500878 <g_share> return a * 2; 40049c: 8b 45 fc mov 0xfffffffffffffffc(%rbp),%eax 40049f: 01 c0 add %eax,%eax } 4004a1: c9 leaveq 4004a2: c3 retq // skip some of the ouput

当然,重定位时修改指令的具体方式还牵涉到比较多的细节很啰嗦,这里就不细说了。

加载时符号重定位

前面描述了静态链接时,怎么解决符号重定位的问题,那么当我们使用动态链接来构建程序时,这些符号重定位问题是怎么解决的呢?目前来说,Linux 下 ELF 主要支持两种方式:加载时符号重定位及地址无关代码。地址无关代码接下来会讲,对于加载时重定位,其原理很简单,它与链接时重定位是一致的,只是把重定位的时机放到了动态库被加载到内存之后,由动态链接器来进行。

int g_share = 1; int g_func(int a) { g_share += a; return a * 2; } int g_func2() { int a = 2; int b = g_func(3); return a + b; } // compile on 32bit linux OS -bash-3.00$ gcc -c a.c main.c -bash-3.00$ gcc -shared -o liba.so a.o -bash-3.00$ gcc -o am main.o -L. -la -bash-3.00$ objdump -S liba.so // skip some of the output 000004f4 <g_func>: int g_share = 1; int g_func(int a) { 4f4: 55 push %ebp 4f5: 89 e5 mov %esp,%ebp g_share += a; 4f7: 8b 45 08 mov 0x8(%ebp),%eax 4fa: 01 05 00 00 00 00 add %eax,0x0 return a * 2; 500: 8b 45 08 mov 0x8(%ebp),%eax 503: d1 e0 shl %eax } 505: c9 leave 506: c3 ret 00000507 <g_func2>: int g_func2() { 507: 55 push %ebp 508: 89 e5 mov %esp,%ebp 50a: 83 ec 08 sub $0x8,%esp int a = 2; 50d: c7 45 fc 02 00 00 00 movl $0x2,0xfffffffc(%ebp) int b = g_func(3); 514: 6a 03 push $0x3 516: e8 fc ff ff ff call 517 <g_func2+0x10> 51b: 83 c4 04 add $0x4,%esp 51e: 89 45 f8 mov %eax,0xfffffff8(%ebp) return a + b; 521: 8b 45 f8 mov 0xfffffff8(%ebp),%eax 524: 03 45 fc add 0xfffffffc(%ebp),%eax } 527: c9 leave // skip some of the output

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/ff8ee0804422736d6a822e7015c50de4.html