【问题标题】:Why do ELF object files contain dummy addresses for string literals and stdlib functions?为什么 ELF 目标文件包含字符串文字和 stdlib 函数的虚拟地址?
【发布时间】:2015-04-08 12:36:58
【问题描述】:

我用 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。那么真实地址是什么?

【问题讨论】:

标签: gcc assembly disassembly


【解决方案1】:

答案是链接器应用了各种修正。当我执行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 更详细地描述了该过程并正确地指出,虽然某些重定位发生在链接时,但加载程序也可以提供重定位数据。

【讨论】:

    猜你喜欢
    • 2011-09-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-28
    • 1970-01-01
    • 1970-01-01
    • 2020-07-25
    • 1970-01-01
    相关资源
    最近更新 更多