linux 为什么用-fPIC -shared编译的共享对象文件的反汇编会引入“call 4”和“add 2”这样的伪地址?

yhxst69z  于 12个月前  发布在  Linux
关注(0)|答案(1)|浏览(170)

考虑下面的代码:

$ cat foo.c
static int foo = 100;

int function(void)
{
    return foo;
}

字符串
我理解libfoo.so的解散

$ gcc -m32 -fPIC -shared -o libfoo.so foo.c
$ objdump -D libfoo.so

000004cc <function>:
 4cc:   55                      push   %ebp
 4cd:   89 e5                   mov    %esp,%ebp
 4cf:   e8 0e 00 00 00          call   4e2 <__x86.get_pc_thunk.cx>
 4d4:   81 c1 c0 11 00 00       add    $0x11c0,%ecx
 4da:   8b 81 18 00 00 00       mov    0x18(%ecx),%eax
 4e0:   5d                      pop    %ebp
 4e1:   c3                      ret    

000004e2 <__x86.get_pc_thunk.cx>:
 4e2:   8b 0c 24                mov    (%esp),%ecx
 4e5:   c3                      ret    
 4e6:   66 90                   xchg   %ax,%ax
...

000016ac <foo>:
    16ac:   64 00 00                add    %al,%fs:(%eax)


function中,foo的地址计算为0x 4d 4(调用__x86.get_pc_thunk.cxecx的值)+ $0x11c0 + 0x 18 = 0x 16 ac。0x 16 ac是foo的地址。
但是我不懂拆卸

$ gcc -m32 -fPIC -shared -c foo.c
$ objdump -D foo.o
00000000 <function>:
   0:   55                      push   %ebp
   1:   89 e5                   mov    %esp,%ebp
   3:   e8 fc ff ff ff          call   4 <function+0x4>
   8:   81 c1 02 00 00 00       add    $0x2,%ecx
   e:   8b 81 00 00 00 00       mov    0x0(%ecx),%eax
  14:   5d                      pop    %ebp
  15:   c3                      ret    

00000000 <foo>:
   0:   64 00 00                add    %al,%fs:(%eax)

00000000 <__x86.get_pc_thunk.cx>:
   0:   8b 0c 24                mov    (%esp),%ecx
   3:   c3                      ret


为什么是call 4 <function+0x4>add $0x2,%ecx

更新:(在objdump中添加了-r标志,-R标志会产生错误not a dynamic object, Invalid operation

$ objdump -D -r foo.o
00000000 <function>:
   0:   55                      push   %ebp
   1:   89 e5                   mov    %esp,%ebp
   3:   e8 fc ff ff ff          call   4 <function+0x4>
        4: R_386_PC32   __x86.get_pc_thunk.cx
  8:    81 c1 02 00 00 00       add    $0x2,%ecx
        a: R_386_GOTPC  _GLOBAL_OFFSET_TABLE_
  e:    8b 81 00 00 00 00       mov    0x0(%ecx),%eax
        10: R_386_GOTOFF    .data
 14:    5d                      pop    %ebp
 15:    c3                      ret


现在4call 4 <function+0x4>中是有意义的,因为这个指令在文本部分的偏移量是4。我仍然不知道为什么0x2add $0x2,%ecx中。

ntjbwcob

ntjbwcob1#

链接器将执行重定位,使得final value = symbol + offset-PC。注意,这个公式中的PC是重定位本身的地址,而不是指令的地址,因为链接器不知道指令边界。然而,汇编器知道它们,可以创建适当的偏移量。
让我们看看call __x86.get_pc_thunk.cx是如何工作的。在x86上,call指令使用相对寻址,但PC的值已经递增以指向以下指令。您可以在第一次转储中验证这一点:

4cf:   e8 0e 00 00 00          call   4e2 <__x86.get_pc_thunk.cx>
 4d4:   81 c1 c0 11 00 00       add    $0x11c0,%ecx

字符串
注意指令中的偏移量是0e。已经递增的PC4d4,并且可以肯定的是跳转的目标4e2 = 4d4 + 0e(所有数字都是十六进制的)。
现在是重定位的版本:

3:   e8 fc ff ff ff          call   4 <function+0x4>
        4: R_386_PC32   __x86.get_pc_thunk.cx


它使用R_386_PC32,但这是在指令的第二个字节,而call需要从更新的PC偏移,这显然是4字节多。这意味着正确的结果是4少,因此该指令包含fffffffc,即-4。注意,无论call的地址是什么,这个偏移量总是-4。反汇编程序会自动将其添加到更新的PC中,在本例中是8,因此通过执行8-4,它到达call 4
上一篇:R_386_GOTPC

3:   e8 fc ff ff ff          call   4 <function+0x4>
        4: R_386_PC32   __x86.get_pc_thunk.cx
  8:    81 c1 02 00 00 00       add    $0x2,%ecx
        a: R_386_GOTPC  _GLOBAL_OFFSET_TABLE_


__x86.get_pc_thunk.cx函数只是将返回地址从堆栈加载到寄存器ecx中。在本例中,此返回地址为8。要实现的目标是将_GLOBAL_OFFSET_TABLE_的地址放在ecx中。我们需要知道它距离ecx中的引用PC有多远,并添加该距离。为此,使用R_386_GOTPC重定位,但这将给予从地址0a的偏移量,因为这是重定位条目所在的位置。从地址8的偏移量当然将是2更多。此2是在指令中编码的。

总结:存储在指令中的重定位偏移量是重定位地址与所需参考点之差:offset = PC-reference。在第一种情况下,该参考点高4字节,在第二种情况下,低2字节,分别给出偏移量-42

相关问题