ELF文件

预处理、编译、汇编和链接。

汇编阶段会生成.o目标文件(待重定位文件)。重定位是指文件里面的符号没有安排地址,这些符号的地址需要将来与其他目标文件组成一个可执行文件时在重新安排地址

nm test.o//可以看到符号地址都是0

00000000 T main
                 U puts

 使用其他命令读取到的地址也是0,因为还没有安排地址

ELF文件_第1张图片ELF文件_第2张图片

链接阶段则是将多个目标文件链接成一个二进制可执行文件。在链接时我们得到了所有的目标文件,这个时候就能够对各个符号的地址进行编排了。经过了链接,可执行的文件里面的符号就有了地址

ELF文件_第3张图片

任何程序都需要被加载到内存里面才能运行。那我们应该如何去解析程序呢?elf就提供了一种解析程序的方式。elf文件里面有程序头,描述了程序的布局信息 。我们按照一定的规则去加载该elf文件就能将程序跑起来。

linux下可执行文件格式是elf(executable and linkable format).elf有三种文件类型

ELF文件_第4张图片

程序中的段和节是真正的程序体。段是由节组成了,多个节经过链接之后就合并成一个段了(elf文件里面的section header和program header其实都是描述的同一个东西)

摘抄自https://www.cnblogs.com/0xHack/p/11575444.html

section和segment的区别:

  • section称为节,是指在汇编源码中经由关键字section或segment修饰、逻辑划分的指令或数据区域。
  • segment称为段,是根据目标文件中属性相同的多个section合并后的section集合,这个集合称为segment。我们平时所说的可执行程序内存空间中的代码段和数据段就是指的segment。
  • section主要提供给Linker使用, 而segment提供给Loader用。Linker需要关心.text、.rel.text、.data、.rodata等,因为Linker需要做relocation,而Loader只需要知道Read/Write/Execute的属性
  • executable的ELF文件可以没有section,但必须有segment。ELF文件中间部分是共用的(也就是代码段、数据段等),如shared objects就可以同时拥有Program header table和Section Header Table,这样load完后还可以relocate。
  • 这样设定之后,使得Loader需要做的工作大大减少了,一定程度上提高了程序加载的效率。

由于段和节的数量不确定。因此就产生了程序头表以及节头表来描述所有的段和节。这些表就相当于一个数组,程序头表里面的元素就对应了一个段,节头表的原因对应了一个节。

由于段和节的数量不固定,自然程序头表以及节头表的大小也不固定了。因此需要在一个固定的位置,用一个固定大小的数据去描述这些表的大小以及位置。这个数据结构便是elf header

ELF文件_第5张图片

上图就是elf文件的布局。从上图可以看到,段其实就是多个section组成的。

elf header的信息如下:从elf header中能够看到程序头表以及节头表的起始位置,以及对应的元素个数等信息。(该elf文件是静态编译的)

ELF Header:
  Magic:   7f 45 4c 46 01 01 01 03 00 00 00 00 00 00 00 00 
  Class:                             ELF32
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - GNU
  ABI Version:                       0
  Type:                              EXEC (Executable file)
  Machine:                           Intel 80386
  Version:                           0x1
  Entry point address:               0x8048d2a
  Start of program headers:          52 (bytes into file)
  Start of section headers:          663836 (bytes into file)
  Flags:                             0x0
  Size of this header:               52 (bytes)
  Size of program headers:           32 (bytes)
  Number of program headers:         6
  Size of section headers:           40 (bytes)
  Number of section headers:         31
  Section header string table index: 28

 elf header的数据结构用如下数据结构描述

#define EI_NIDENT (16)
 
typedef struct
{
  unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */
  Elf32_Half    e_type;         /* Object file type */
  Elf32_Half    e_machine;      /* Architecture */
  Elf32_Word    e_version;      /* Object file version */
  Elf32_Addr    e_entry;        /* Entry point virtual address */
  Elf32_Off	e_phoff;        /* Program header table file offset */
  Elf32_Off	e_shoff;        /* Section header table file offset */
  Elf32_Word    e_flags;        /* Processor-specific flags */
  Elf32_Half    e_ehsize;       /* ELF header size in bytes */
  Elf32_Half    e_phentsize;    /* Program header table entry size */
  Elf32_Half    e_phnum;        /* Program header table entry count */
  Elf32_Half    e_shentsize;    /* Section header table entry size */
  Elf32_Half    e_shnum;        /* Section header table entry count */
  Elf32_Half    e_shstrndx;     /* Section header string table index */
} Elf32_Ehdr;

以下是section信息。可以看到确实是32个section

Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            00000000 000000 000000 00      0   0  0
  [ 1] .note.ABI-tag     NOTE            080480f4 0000f4 000020 00   A  0   0  4
  [ 2] .note.gnu.build-i NOTE            08048114 000114 000024 00   A  0   0  4
  [ 3] .rel.plt          REL             08048138 000138 000070 08   A  0   5  4
  [ 4] .init             PROGBITS        080481a8 0001a8 000023 00  AX  0   0  4
  [ 5] .plt              PROGBITS        080481d0 0001d0 0000e0 00  AX  0   0 16
  [ 6] .text             PROGBITS        080482b0 0002b0 075c64 00  AX  0   0 16
  [ 7] __libc_freeres_fn PROGBITS        080bdf20 075f20 000b26 00  AX  0   0 16
  [ 8] __libc_thread_fre PROGBITS        080bea50 076a50 000076 00  AX  0   0 16
  [ 9] .fini             PROGBITS        080beac8 076ac8 000014 00  AX  0   0  4
  [10] .rodata           PROGBITS        080beae0 076ae0 01bff0 00   A  0   0 32
  [11] __libc_subfreeres PROGBITS        080daad0 092ad0 00002c 00   A  0   0  4
  [12] __libc_atexit     PROGBITS        080daafc 092afc 000004 00   A  0   0  4
  [13] __libc_thread_sub PROGBITS        080dab00 092b00 000004 00   A  0   0  4
  [14] .eh_frame         PROGBITS        080dab04 092b04 00e0a0 00   A  0   0  4
  [15] .gcc_except_table PROGBITS        080e8ba4 0a0ba4 0000b3 00   A  0   0  1
  [16] .tdata            PROGBITS        080e9f40 0a0f40 000010 00 WAT  0   0  4
  [17] .tbss             NOBITS          080e9f50 0a0f50 000018 00 WAT  0   0  4
  [18] .init_array       INIT_ARRAY      080e9f50 0a0f50 000008 00  WA  0   0  4
  [19] .fini_array       FINI_ARRAY      080e9f58 0a0f58 000008 00  WA  0   0  4
  [20] .jcr              PROGBITS        080e9f60 0a0f60 000004 00  WA  0   0  4
  [21] .data.rel.ro      PROGBITS        080e9f80 0a0f80 000070 00  WA  0   0 32
  [22] .got              PROGBITS        080e9ff0 0a0ff0 000008 04  WA  0   0  4
  [23] .got.plt          PROGBITS        080ea000 0a1000 000044 04  WA  0   0  4
  [24] .data             PROGBITS        080ea060 0a1060 000f20 00  WA  0   0 32
  [25] .bss              NOBITS          080eaf80 0a1f80 00136c 00  WA  0   0 32
  [26] __libc_freeres_pt NOBITS          080ec2ec 0a1f80 000018 00  WA  0   0  4
  [27] .comment          PROGBITS        00000000 0a1f80 00004f 01  MS  0   0  1
  [28] .shstrtab         STRTAB          00000000 0a1fcf 00014c 00      0   0  1
  [29] .symtab           SYMTAB          00000000 0a25f4 008bd0 10     30 1063  4
  [30] .strtab           STRTAB          00000000 0ab1c4 007f1c 00      0   0  1

section用以下数据结构进行描述

typedef struct
{ 
  Elf32_Word    sh_name;        /* Section name (string tbl index) */
  Elf32_Word    sh_type;        /* Section type */
  Elf32_Word    sh_flags;       /* Section flags */
  Elf32_Addr    sh_addr;        /* Section virtual addr at execution */
  Elf32_Off 	sh_offset;      /* Section file offset */
  Elf32_Word    sh_size;        /* Section size in bytes */
  Elf32_Word    sh_link;        /* Link to another section */
  Elf32_Word    sh_info;        /* Additional section information */
  Elf32_Word    sh_addralign;   /* Section alignment */
  Elf32_Word    sh_entsize;     /* Entry size if section holds table */
} Elf32_Shdr;

程序头表信息,6个段。以及这6个段是由哪些section组成的。

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  LOAD           0x000000 0x08048000 0x08048000 0xa0c57 0xa0c57 R E 0x1000
  LOAD           0x0a0f40 0x080e9f40 0x080e9f40 0x01040 0x023c4 RW  0x1000
  NOTE           0x0000f4 0x080480f4 0x080480f4 0x00044 0x00044 R   0x4
  TLS            0x0a0f40 0x080e9f40 0x080e9f40 0x00010 0x00028 R   0x4
  GNU_STACK      0x000000 0x00000000 0x00000000 0x00000 0x00000 RW  0x10
  GNU_RELRO      0x0a0f40 0x080e9f40 0x080e9f40 0x000c0 0x000c0 R   0x1

 Section to Segment mapping:
  Segment Sections...
   00     .note.ABI-tag .note.gnu.build-id .rel.plt .init .plt .text __libc_freeres_fn __libc_thread_freeres_fn .fini .rodata __libc_subfreeres __libc_atexit __libc_thread_subfreeres .eh_frame .gcc_except_table 
   01     .tdata .init_array .fini_array .jcr .data.rel.ro .got .got.plt .data .bss __libc_freeres_ptrs 
   02     .note.ABI-tag .note.gnu.build-id 
   03     .tdata .tbss 
   04     
   05     .tdata .init_array .fini_array .jcr .data.rel.ro .got 

 可以看到,段和节其实就是同一个东西。只不过段是由连续多个的节组成。他们只是描述同一个事物的两种方式而已。

那之前我们说的数据段,代码段这些东西去哪里了啊。我怎么没有在程序头表里面看到呢?

可执行程序有至少有以下几个段(看网上说的,那看这个意思是还可以存在其他的段了)

名称 内容
代码段.text 可执行代码、字符串常量
数据段.data 已初始化全局变量、已初始化全局静态变量、局部静态变量、常量数据
BSS段.bss 未初始化全局变量,未初始化全局静态变量
局部变量、函数参数
动态内存分配

感觉代码段、数据段、bss段就是分别对应了.text、.data、.bss这几个section呢?(可能还有其他的section)。至于栈区感觉是加载器给程序分配的,堆区就不是很清楚了

#include 
 
long global1; // 未初始化的全局变量 存放bss段
long global2 = 10; //已初始化的全局变量 存放数据段data
 
long sum_func(long a, long b)//存放text段  
{
  static long local_static1; //  存放bss段
  static long local_static2 = 123;//存放数据段data
  static long local_static3 = 456;//存放数据段data
  return a + b;
}
 
int main(void)//存放text段
{
  long sum = sum_func(global1, global2);
  printf("sum=%ld\n", sum);
  return 0;
}

gcc main.c -o main

readelf -a main

  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al

  [13] .text             PROGBITS        08048320 000320 0001c2 00  AX  0   0 16
  [24] .data             PROGBITS        0804a018 001018 000014 00  WA  0   0  4
  [25] .bss              NOBITS          0804a02c 00102c 00000c 00  WA  0   0  4

可以看到section.text的范围是0x8048320-0x80484E2;.data:0x804a018-0x804A02C

.bss: 0x0804a02c-0x804A038

理论上global2 、local_static2 、local_static3存放在section .data。global1和local_static1放在section.bss,main和sum_func在.text.

通过读取出来的符号地址可以看到上述的几个符号的地址,也确实落在了对应的section中

0x804a018 < global2 、local_static2 、local_static3<0x804A02C

0x0804a02c

0x8048320

    37: 0804a024     4 OBJECT  LOCAL  DEFAULT   24 local_static3.1831
    38: 0804a028     4 OBJECT  LOCAL  DEFAULT   24 local_static2.1830
    39: 0804a030     4 OBJECT  LOCAL  DEFAULT   25 local_static1.1829
    55: 0804a034     4 OBJECT  GLOBAL DEFAULT   25 global1
    66: 0804842a    63 FUNC    GLOBAL DEFAULT   13 main
    70: 0804841d    13 FUNC    GLOBAL DEFAULT   13 sum_func
    72: 0804a020     4 OBJECT  GLOBAL DEFAULT   24 global2

你可能感兴趣的:(linux)