linux下共享库的注意点之-fpic

在编译共享库必须加上-fpic。这是为什么呢?

首先看一个简单的例子:

#include <stdio.h>

int fun1()
{
        printf("fun1\n");
}

先不加-fpic的情况下生成库,反汇编查看fun1的机器码

0000044c <fun1>:
 44c:   55                      push   %ebp
 44d:   89 e5                   mov    %esp,%ebp
 44f:   83 ec 18                sub    $0x18,%esp
 452:   c7 04 24 b2 04 00 00    movl   $0x4b2,(%esp)
 459:   e8 fc ff ff ff          call   45a <fun1+0xe>
 45e:   c9                      leave  
 45f:   c3                      ret  

可以看出调用printf的位置是那个唯一的一个call,并不是跳转到plt表,有关plt表的内容可以查看我前面的博文。也就是说在该库被加载时需要修改代码段来达到重定位的效果。那么每一个加载这个共享库的程序都要有这个库的一份拷贝,这样实际上就没有达到共享库的效果。

看下运行时的机器码

   0xb771d44c <+0>:     55      push   %ebp
   0xb771d44d <+1>:     89 e5   mov    %esp,%ebp
   0xb771d44f <+3>:     83 ec 18        sub    $0x18,%esp
   0xb771d452 <+6>:     c7 04 24 b2 d4 71 b7    movl   $0xb771d4b2,(%esp)
   0xb771d459 <+13>:    e8 42 b2 ea ff  call   0xb75c86a0 <puts>
   0xb771d45e <+18>:    c9      leave  
   0xb771d45f <+19>:    c3      ret 

显然代码段被修改了。

再看一下再加了-fpic的情况下生成的库,反汇编看下fun1的机器码

0000045c <fun1>:
 45c:   55                      push   %ebp
 45d:   89 e5                   mov    %esp,%ebp
 45f:   53                      push   %ebx
 460:   83 ec 14                sub    $0x14,%esp
 463:   e8 ef ff ff ff          call   457 <__i686.get_pc_thunk.bx>
 468:   81 c3 8c 1b 00 00       add    $0x1b8c,%ebx
 46e:   8d 83 ee e4 ff ff       lea    -0x1b12(%ebx),%eax
 474:   89 04 24                mov    %eax,(%esp)
 477:   e8 04 ff ff ff          call   380 <puts@plt>
 47c:   83 c4 14                add    $0x14,%esp
 47f:   5b                      pop    %ebx
 480:   5d                      pop    %ebp
 481:   c3                      ret    
 482:   90                      nop
 483:   90                      nop
 484:   90                      nop
 485:   90                      nop
 486:   90                      nop
 487:   90                      nop
 488:   90                      nop

看过很多汇编代码的人知道printf有时候是puts,所以这段机器码中printf就对应第二个call,也就是跳转到plt表中去查找puts符号,那么这样就达到了共享库的效果,此时每一个需要该库的程序只是有一个plt表的拷贝,而代码段所有应用程序是共享的。

再看下运行时机器码

   0xb773045c <+0>:     55      push   %ebp
   0xb773045d <+1>:     89 e5   mov    %esp,%ebp
   0xb773045f <+3>:     53      push   %ebx
   0xb7730460 <+4>:     83 ec 14        sub    $0x14,%esp
   0xb7730463 <+7>:     e8 ef ff ff ff  call   0xb7730457 <__i686.get_pc_thunk.bx>
   0xb7730468 <+12>:    81 c3 8c 1b 00 00       add    $0x1b8c,%ebx
   0xb773046e <+18>:    8d 83 ee e4 ff ff       lea    -0x1b12(%ebx),%eax
   0xb7730474 <+24>:    89 04 24        mov    %eax,(%esp)
   0xb7730477 <+27>:    e8 04 ff ff ff  call   0xb7730380 <puts@plt>
   0xb773047c <+32>:    83 c4 14        add    $0x14,%esp
   0xb773047f <+35>:    5b      pop    %ebx
   0xb7730480 <+36>:    5d      pop    %ebp
   0xb7730481 <+37>:    c3      ret 

显然是一致的。

所以,在编译共享库时是必须加上-fpic的选项的,否则共享库省下的仅仅是硬盘上的空间,而没有省下内存。

posted @ 2016-07-21 13:51  __sipl  阅读(2788)  评论(0编辑  收藏  举报