为什么ELF目标文件包含字符串文字和stdlib函数的虚拟地址?

时间:2015-04-08 12:36:58

标签: gcc assembly disassembly

我用C编写了这个Hello World:

#include<stdio.h>

int main() {
  printf("Hello world !\n");
  return 0;
}

使用gcc编译汇编代码 我明白了:

    .file   "file.c"
    .section    .rodata.str1.1,"aMS",@progbits,1
.LC0:
    .string "Hello world !"
    .section    .text.unlikely,"ax",@progbits
.LCOLDB1:
    .section    .text.startup,"ax",@progbits
.LHOTB1:
    .p2align 4,,15
    .globl  main
    .type   main, @function
main:
.LFB11:
    .cfi_startproc
    subq    $8, %rsp
    .cfi_def_cfa_offset 16
    movl    $.LC0, %edi
    call    puts
    xorl    %eax, %eax
    addq    $8, %rsp
    .cfi_def_cfa_offset 8
    ret
    .cfi_endproc
.LFE11:
    .size   main, .-main
    .section    .text.unlikely
.LCOLDE1:
    .section    .text.startup
.LHOTE1:
    .ident  "GCC: (GNU) 4.9.2 20150304 (prerelease)"
    .section    .note.GNU-stack,"",@progbits

这里没问题。但现在,我想将汇编代码与objdump反汇编的代码进行比较:

对于主要功能,我得到了这个:

0000000000000000 <main>:
   0:   48 83 ec 08             sub    $0x8,%rsp
   4:   bf 00 00 00 00          mov    $0x0,%edi
            5: R_X86_64_32  .rodata.str1.1
   9:   e8 00 00 00 00          callq  e <main+0xe>
            a: R_X86_64_PC32    puts-0x4
   e:   31 c0                   xor    %eax,%eax
  10:   48 83 c4 08             add    $0x8,%rsp
  14:   c3                      retq   

我不明白两件事:

为什么在edi上移动数字0表示加载字符串“Hello world”?

此外,指令callq调用地址e。但收件人e的说明不是函数puts,而是xor。那真正的地址是什么?

1 个答案:

答案 0 :(得分:2)

答案是链接器应用了各种修正。当我做objdump -d hello.o时,我得到了这个:

Disassembly of section .text:

0000000000000000 <main>:
   0:   55                      push   %rbp
   1:   48 89 e5                mov    %rsp,%rbp
   4:   bf 00 00 00 00          mov    $0x0,%edi
   9:   e8 00 00 00 00          callq  e <main+0xe>
   e:   b8 00 00 00 00          mov    $0x0,%eax
  13:   5d                      pop    %rbp
  14:   c3                      retq   

然而,objdump -d hello的摘录产生了这个:

400536: 55                      push   %rbp
400537: 48 89 e5                mov    %rsp,%rbp
40053a: bf e0 05 40 00          mov    $0x4005e0,%edi
40053f: e8 cc fe ff ff          callq  400410 <puts@plt>
400544: b8 00 00 00 00          mov    $0x0,%eax
400549: 5d                      pop    %rbp
40054a: c3                      retq   

不同之处在于,字符串偏移的零和puts的地址现在实际上由链接器填充。您可以使用objdump -r hello.o

找到这些重定位条目
hello.o:     file format elf64-x86-64

RELOCATION RECORDS FOR [.text]:
OFFSET           TYPE              VALUE 
0000000000000005 R_X86_64_32       .rodata
000000000000000a R_X86_64_PC32     puts-0x0000000000000004

这就是链接器找到.rodata的实际地址(这是字符串的地址)并将其放在偏移0x5和库puts的地址代码并将其放在偏移0xa

This article on relocation更详细地描述了该过程,并正确地指出,虽然在链接时发生了一些重定位,但加载器也可以提供重定位数据。