看CSAPP第七章时,老是感觉迷迷糊糊的,所以实际看看程序的可重定位目标文件和可执行目标文件的内容。


我这里以书中的代码为例

//main.c
int sum(int *a, int n);
int array[2] = {1,2};
int main(){
  int val = sum(array,2);
  return val;
}

//sum.c
int sum(int *a, int n){
  int i,s = 0;
  for(i=0; i<n; i++){
    s += a[i];
  }
  return s;
}

然后在命令行中运行

gcc -Og -c main.c

可以得到main.c的可重定位目标文件main.o,然后使用以下命令行查看该ELF文件

readelf -a -x .text -x .data main.o

可重定位目标文件的ELF格式包含以下内容

我们依次查看其中不同的数据节

ELF头:

ELF Header:
  Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
  Class:                             ELF64
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              REL (Relocatable file)
  Machine:                           Advanced Micro Devices X86-64
  Version:                           0x1
  Entry point address:               0x0
  Start of program headers:          0 (bytes into file)
  Start of section headers:          704 (bytes into file)
  Flags:                             0x0
  Size of this header:               64 (bytes)
  Size of program headers:           0 (bytes)
  Number of program headers:         0
  Size of section headers:           64 (bytes)
  Number of section headers:         12
  Section header string table index: 11

可以看到ELF头主要描述改文件的生成信息,并且包含了节头部表的开始地址Start of section headers

节头部表:

Section Headers:
  [Nr] Name              Type             Address           Offset
       Size              EntSize          Flags  Link  Info  Align
  [ 0]                   NULL             0000000000000000  00000000
       0000000000000000  0000000000000000           0     0     0
  [ 1] .text             PROGBITS         0000000000000000  00000040
       000000000000001a  0000000000000000  AX       0     0     1
  [ 2] .rela.text        RELA             0000000000000000  00000218
       0000000000000030  0000000000000018   I       9     1     8
  [ 3] .data             PROGBITS         0000000000000000  00000060
       0000000000000008  0000000000000000  WA       0     0     8
  [ 4] .bss              NOBITS           0000000000000000  00000068
       0000000000000000  0000000000000000  WA       0     0     1
  [ 5] .comment          PROGBITS         0000000000000000  00000068
       000000000000002c  0000000000000001  MS       0     0     1
  [ 6] .note.GNU-stack   PROGBITS         0000000000000000  00000094
       0000000000000000  0000000000000000           0     0     1
  [ 7] .eh_frame         PROGBITS         0000000000000000  00000098
       0000000000000030  0000000000000000   A       0     0     8
  [ 8] .rela.eh_frame    RELA             0000000000000000  00000248
       0000000000000018  0000000000000018   I       9     7     8
  [ 9] .symtab           SYMTAB           0000000000000000  000000c8
       0000000000000120  0000000000000018          10     8     8
  [10] .strtab           STRTAB           0000000000000000  000001e8
       000000000000002d  0000000000000000           0     0     1
  [11] .shstrtab         STRTAB           0000000000000000  00000260
       0000000000000059  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)

可以看到,节头部表中含有一个表,其中每个数据项含有以下内容(我只挑我会的说):Nr数据项的索引;Name数据项的名字,即该数据项是描述哪个数据节的;Address数据节所在的内存地址;offset数据节在目标文件中的偏移量;size数据节的大小。

**注意:**编译器和汇编器会从地址0开始生成数据节,没有考虑实际的内存地址,所以这里的address都是0。

.text: