可重定位目标文件

  • Post author:
  • Post category:其他


可重定位目标文件(Relocatable Object Files)。

代码示例:

#include <stdio.h>

int count = 10;
int value;

void func(int sum)
{
    printf("sum is:%d\n", sum);
}

int main()
{
    static int a = 1;
    static int b = 0;
    int x = 1;
    func(a + b + x);
    return 0;
}

编译为可重定位目标文件.o,-c编译选项表示只进行编译和汇编,不执行链接操作。

gcc -c main.c
//-c : Compile and assemble, but do not link

使用wc(Word Count)命令查看main.o的大小,-c选项表示查看文件包含的字节。

wc -c main.o
1848 main.o

可重定位目标文件可分为三个部分,分别是ELF header,不同的section,以及描述这些section信息的表。其中ELF是可执行可链接格式的首字母缩写(Executable and Linkable Format, ELF)。

图示:

ELF header

使用readelf查看ELF header的具体内容,-h选项表示只显示header信息。

  • 开头4字节7f 45 4c 46称为ELF文件的魔数,分别与ASCII码中的DEL控制符、字符E、字符L、字符F对应,用于操作系统加载文件时确认文件类型。
  • 第5字节表示ELF文件类型,01表示32位,02表示64位。
  • 第6字节表示字节序,01表示小端序,02表示大端序。
  • 第7字节表示ELF文件版本号,通常都是1。
  • 最后9个字节ELF的标准中没有定义,用0填充。
# readelf -h main.o
ELF 头:
  Magic:  7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 
  类别:                              ELF64
  数据:                              2 补码,小端序 (little endian)
  版本:                              1 (current)
  OS/ABI:                            UNIX - System V
  ABI 版本:                          0
  类型:                              REL (可重定位文件)
  系统架构:                           Advanced Micro Devices X86-64
  版本:                              0x1
  入口点地址:                        0x0
  程序头起点:                        0 (bytes into file)
  Start of section headers:         1016 (bytes into file)
  标志:             0x0
  本头的大小:        64 (字节)
  程序头大小:        0 (字节)
  Number of program headers:         0
  节头大小:         64 (字节)
  节头数量:         13
  字符串表索引节头:   12

ELF Section header table

使用readelf查看表信息,-S选项表示打印整个表信息。除了表中第一项,其他每一个表项都对应着一个section,整个ELF文件包含12个section。offset偏移量表示每个section的起始位置,size表示section的大小。根据offset和size可以确定每个section在ELF文件中的具体位置。

  • .text段存放编译好的机器代码。查看机器代码,需要使用反汇编工具objdump将机器代码转换成汇编代码。由于4字节对齐,.text大小为0x51,实际占用空间大小为0x54。
  • .data段用来存放已初始化的全局变量和静态变量的值。
  • .bss段存放未初始化的全局变量和静态变量,需要注意的是,被初始化为0的全局变量和静态变量也存放在bss中。bss可理解为Better Save Space。局部变量既不在data中,也不在bss中。实际上bss段并不占据实际的空间,它仅仅是只是一个占位符。
  • .rodata段存放只读数据,ro是read only的缩写。例如printf中的格式串和switch语句中的跳转表就是存放在这个区域。
# readelf -S main.o
共有 13 个节头,从偏移量 0x3f8 开始:

节头:
  [号] 名称              类型             地址              偏移量
       大小              全体大小          旗标   链接   信息   对齐
  [ 0]                   NULL             0000000000000000  00000000
       0000000000000000  0000000000000000           0     0     0
  [ 1] .text             PROGBITS         0000000000000000  00000040
       0000000000000051  0000000000000000  AX       0     0     1
  [ 2] .rela.text        RELA             0000000000000000  000002e8
       0000000000000078  0000000000000018   I      10     1     8
  [ 3] .data             PROGBITS         0000000000000000  00000094
       0000000000000008  0000000000000000  WA       0     0     4
  [ 4] .bss              NOBITS           0000000000000000  0000009c
       0000000000000004  0000000000000000  WA       0     0     4
  [ 5] .rodata           PROGBITS         0000000000000000  0000009c
       000000000000000b  0000000000000000   A       0     0     1
  [ 6] .comment          PROGBITS         0000000000000000  000000a7
       000000000000002e  0000000000000001  MS       0     0     1
  [ 7] .note.GNU-stack   PROGBITS         0000000000000000  000000d5
       0000000000000000  0000000000000000           0     0     1
  [ 8] .eh_frame         PROGBITS         0000000000000000  000000d8
       0000000000000058  0000000000000000   A       0     0     8
  [ 9] .rela.eh_frame    RELA             0000000000000000  00000360
       0000000000000030  0000000000000018   I      10     8     8
  [10] .symtab           SYMTAB           0000000000000000  00000130
       0000000000000180  0000000000000018          11    11     8
  [11] .strtab           STRTAB           0000000000000000  000002b0
       0000000000000035  0000000000000000           0     0     1
  [12] .shstrtab         STRTAB           0000000000000000  00000390
       0000000000000061  0000000000000000           0     0     1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
  L (link order), O (extra OS processing required), G (group), T (TLS),
  C (compressed), x (unknown), o (OS specific), E (exclude),
  l (large), p (processor specific)

图示:

反汇编工具objdump

查看机器代码,需要使用反汇编工具objdump将机器代码转换成汇编代码。

# objdump -s -d main.o

string.o:     文件格式 elf64-x86-64

Contents of section .text:
 0000 554889e5 4883ec10 897dfc8b 45fc89c6  UH..H....}..E...
 0010 bf000000 00b80000 0000e800 000000c9  ................
 0020 c3554889 e54883ec 10c745fc 01000000  .UH..H....E.....
 0030 8b150000 00008b05 00000000 01c28b45  ...............E
 0040 fc01d089 c7e80000 0000b800 000000c9  ................
 0050 c3                                   .               
Contents of section .data:
 0000 0a000000 01000000                    ........        
Contents of section .rodata:
 0000 73756d20 69733a25 640a00             sum is:%d..     
Contents of section .comment:
 0000 00474343 3a202847 4e552920 342e382e  .GCC: (GNU) 4.8.
 0010 35203230 31353036 32332028 52656420  5 20150623 (Red 
 0020 48617420 342e382e 352d3434 2900      Hat 4.8.5-44).  
Contents of section .eh_frame:
 0000 14000000 00000000 017a5200 01781001  .........zR..x..
 0010 1b0c0708 90010000 1c000000 1c000000  ................
 0020 00000000 21000000 00410e10 8602430d  ....!....A....C.
 0030 065c0c07 08000000 1c000000 3c000000  .\..........<...
 0040 00000000 30000000 00410e10 8602430d  ....0....A....C.
 0050 066b0c07 08000000                    .k......        

Disassembly of section .text:

0000000000000000 <func>:
   0:   55                      push   %rbp
   1:   48 89 e5                mov    %rsp,%rbp
   4:   48 83 ec 10             sub    $0x10,%rsp
   8:   89 7d fc                mov    %edi,-0x4(%rbp)
   b:   8b 45 fc                mov    -0x4(%rbp),%eax
   e:   89 c6                   mov    %eax,%esi
  10:   bf 00 00 00 00          mov    $0x0,%edi
  15:   b8 00 00 00 00          mov    $0x0,%eax
  1a:   e8 00 00 00 00          callq  1f <func+0x1f>
  1f:   c9                      leaveq 
  20:   c3                      retq   

0000000000000021 <main>:
  21:   55                      push   %rbp
  22:   48 89 e5                mov    %rsp,%rbp
  25:   48 83 ec 10             sub    $0x10,%rsp
  29:   c7 45 fc 01 00 00 00    movl   $0x1,-0x4(%rbp)
  30:   8b 15 00 00 00 00       mov    0x0(%rip),%edx        # 36 <main+0x15>
  36:   8b 05 00 00 00 00       mov    0x0(%rip),%eax        # 3c <main+0x1b>
  3c:   01 c2                   add    %eax,%edx
  3e:   8b 45 fc                mov    -0x4(%rbp),%eax
  41:   01 d0                   add    %edx,%eax
  43:   89 c7                   mov    %eax,%edi
  45:   e8 00 00 00 00          callq  4a <main+0x29>
  4a:   b8 00 00 00 00          mov    $0x0,%eax
  4f:   c9                      leaveq 
  50:   c3                      retq