编译、链接与装载学习笔记(二):可执行文件的文件结构

文章目录

    • 可执行文件的格式
    • ELF文件结构
      • ELF文件的主要组成部分
        • 文件头与段表信息

可执行文件的格式

目前主流的可执行文件格式主要分为两种:

  1. windows下的PE格式
  2. linux下的ELF格式

两种格式都是COFF格式的变种

下面接下来主要介绍ELF文件的文件结构
将使用下面两个工具查看文件结构:

  1. objdump(通用查看格式工具,具有反汇编功能)
  2. readelf (只能查看ELF文件格式,信息较为详细)

ELF文件结构

ELF文件类型:可重定位、可执行、共享目标文件、核心转储文件

  1. 可重定位文件:.o目标文件,.a静态库
  2. 可执行文件:.out可执行文件
  3. 共享目标文件:.so二进制文件
  4. 核心转储文件:一般程序崩溃的时候报错就是核心转储文件,典型的是Linux下的core dump

linux下可使用命令查看文件格式:file 文件

file demo.o
demo.o: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), not stripped
file a.out
a.out: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked,interpreter /lib64/l, for GNU/Linux2.6.32, 
BuildID[sha1]=3a49c2dfa1a516767e95146b666d6d444d6403b9, not stripped
file libopencv_hdf.so.3.3.1 
libopencv_hdf.so.3.3.1: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, 
BuildID[sha1]=8a273151900f0f70dca804faae8be408848fefd8, not stripped

linux下可以命令查看段表头对比
readelf -S -W 源文件.o
readelf -S -W 源文件.out

ELF文件的主要组成部分

  • ELF文件头:描述文件的基本属性,如ELF文件版本、目标机器型号、程序入口地址等。
  • 段表:描述包含的所有段的信息,包括段名、段的长度、在文件中的偏移、读写权限等。
  • 辅助结构:重定位表(重定位文件特有)、字符串表、符号表等。

目标文件以代码段的形式存储信息

常用的结构,常用的几个段大致为:

  • ELF文件头 在linux环境下,命令行输入readelf -h 目标文件 ,得到ELF文件头的信息
  • .text 存储函数代码段
  • .data 存储初始化全局变量以及静态初始化局部变量
  • .bss 存储未初始化全局变量和未初始化静态局部变量,初始化为0的变量也会被优化到bss段中
  • .rodata 存储常量,只读变量,权限只可读
  • .comment 存储编译器信息
  • .rel.text 重定位表
  • other section(包括自定义段)
  • 辅助结构(如字符串表,符号表)
    编译、链接与装载学习笔记(二):可执行文件的文件结构_第1张图片
目标文件存储结构
ELF Header
.text
.data
.bss
.rodata
.comment
…other section
string tables
symbol tables

以.开头的段是系统保留段,自定义不以.作为前缀否则容易冲突
自定义段保存可以使用__attribute__((section(“name”))) int a;,将变量a保存到name段中

文件头与段表信息

readelf -h 目标文件
查看ELF文件头

ELF Header:
  Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 -->7f为ascii码的del控制符,2-4位是ELF的ascii码,02表示64位,第6位表示小段存储,第7位是ELF版本,后面作为扩展
  Class:                             ELF64
  Data:                              2's complement, little endian-->存储方式,小段存储
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              EXEC (Executable file)     -->文件类型
  Machine:                           Advanced Micro Devices X86-64
  Version:                           0x1
  Entry point address:               0x4003e0       -->与可执行文件的_start函数地址相同,重定位文件为0
  Start of program headers:          64 (bytes into file)
  Start of section headers:          6600 (bytes into file) -->文件偏移大小后,段开始的位置
  Flags:                             0x0
  Size of this header:               64 (bytes)
  Size of program headers:           56 (bytes)
  Number of program headers:         9
  Size of section headers:           64 (bytes)-->段的大小
  Number of section headers:         31-->段的数量
  Section header string table index: 28

在/include/elf.h中有文件头的结构体,其对应变量与文件头的一一对应,描述如下:

typedef struct
{
  unsigned char	e_ident[EI_NIDENT];	/* Magic number and other info */		魔数
  Elf64_Half	e_type;			/* Object file type */ELF					文件类型
  Elf64_Half	e_machine;		/* Architecture */							机器类型
  Elf64_Word	e_version;		/* Object file version */					ELF版本
  Elf64_Addr	e_entry;		/* Entry point virtual address */			入口地址
  Elf64_Off	e_phoff;		/* Program header table file offset */
  Elf64_Off	e_shoff;		/* Section header table file offset */			段表偏移
  Elf64_Word	e_flags;		/* Processor-specific flags */
  Elf64_Half	e_ehsize;		/* ELF header size in bytes */				文件头大小
  Elf64_Half	e_phentsize;		/* Program header table entry size */
  Elf64_Half	e_phnum;		/* Program header table entry count */
  Elf64_Half	e_shentsize;		/* Section header table entry size */	段的长度
  Elf64_Half	e_shnum;		/* Section header table entry count */		段的数量
  Elf64_Half	e_shstrndx;		/* Section header string table index */
} Elf64_Ehdr;

objdump -x 目标文件可以看到段表详细的信息

同时readelf也能查看,我们使用readelf查看段表
raedelf -S -W 目标文件
查看段表

  [Nr] Name              Type            Address          Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            0000000000000000 000000 000000 00      0   0  0
  [ 1] .interp           PROGBITS        0000000000400238 000238 00001c 00   A  0   0  1
  [ 2] .note.ABI-tag     NOTE            0000000000400254 000254 000020 00   A  0   0  4
  [ 3] .note.gnu.build-id NOTE            0000000000400274 000274 000024 00   A  0   0  4
  [ 4] .gnu.hash         GNU_HASH        0000000000400298 000298 00001c 00   A  5   0  8
  [ 5] .dynsym           DYNSYM          00000000004002b8 0002b8 000048 18   A  6   1  8
  [ 6] .dynstr           STRTAB          0000000000400300 000300 000038 00   A  0   0  1
  [ 7] .gnu.version      VERSYM          0000000000400338 000338 000006 02   A  5   0  2
  [ 8] .gnu.version_r    VERNEED         0000000000400340 000340 000020 00   A  6   1  8
  [ 9] .rela.dyn         RELA            0000000000400360 000360 000018 18   A  5   0  8
  [10] .rela.plt         RELA            0000000000400378 000378 000018 18  AI  5  24  8
  [11] .init             PROGBITS        0000000000400390 000390 00001a 00  AX  0   0  4
  [12] .plt              PROGBITS        00000000004003b0 0003b0 000020 10  AX  0   0 16
  [13] .plt.got          PROGBITS        00000000004003d0 0003d0 000008 00  AX  0   0  8
  [14] .text             PROGBITS        00000000004003e0 0003e0 000192 00  AX  0   0 16
  [15] .fini             PROGBITS        0000000000400574 000574 000009 00  AX  0   0  4
  [16] .rodata           PROGBITS        0000000000400580 000580 000004 04  AM  0   0  4
  [17] .eh_frame_hdr     PROGBITS        0000000000400584 000584 000034 00   A  0   0  4
  [18] .eh_frame         PROGBITS        00000000004005b8 0005b8 0000f4 00   A  0   0  8
  [19] .init_array       INIT_ARRAY      0000000000600e10 000e10 000008 00  WA  0   0  8
  [20] .fini_array       FINI_ARRAY      0000000000600e18 000e18 000008 00  WA  0   0  8
  [21] .jcr              PROGBITS        0000000000600e20 000e20 000008 00  WA  0   0  8
  [22] .dynamic          DYNAMIC         0000000000600e28 000e28 0001d0 10  WA  6   0  8
  [23] .got              PROGBITS        0000000000600ff8 000ff8 000008 08  WA  0   0  8
  [24] .got.plt          PROGBITS        0000000000601000 001000 000020 08  WA  0   0  8
  [25] .data             PROGBITS        0000000000601020 001020 000014 00  WA  0   0  8
  [26] .bss              NOBITS          0000000000601034 001034 000004 00  WA  0   0  1
  [27] .comment          PROGBITS        0000000000000000 001034 000035 01  MS  0   0  1
  [28] .shstrtab         STRTAB          0000000000000000 0018d6 00010c 00      0   0  1
  [29] .symtab           SYMTAB          0000000000000000 001070 000660 18     30  47  8
  [30] .strtab           STRTAB          0000000000000000 0016d0 000206 00      0   0  1


在/include/elf.h中段的结构体,其对应变量与段表结构的一一对应,描述如下:

typedef struct
{
  Elf64_Word	sh_name;		/* Section name (string tbl index) */	段表名
  Elf64_Word	sh_type;		/* Section type */						段表类型
  Elf64_Xword	sh_flags;		/* Section flags */
  Elf64_Addr	sh_addr;		/* Section virtual addr at execution */	段表虚拟地址
  Elf64_Off	sh_offset;		/* Section file offset */					段表偏移
  Elf64_Xword	sh_size;		/* Section size in bytes */				段表长度
  Elf64_Word	sh_link;		/* Link to another section */
  Elf64_Word	sh_info;		/* Additional section information */
  Elf64_Xword	sh_addralign;		/* Section alignment */				段地址对齐
  Elf64_Xword	sh_entsize;		/* Entry size if section holds table */	项的大小
} Elf64_Shdr;

你可能感兴趣的:(编译,链接与装载)