ELF目标文件结构

来源:互联网 发布:诺基亚n8刷windows 编辑:程序博客网 时间:2024/06/04 18:51

目标文件结构

编译C/C++源文件会产生目标文件,目标文件是链接的基础。常见的目标文件后缀名是.o、.obj。在Windows下,目标文件(.obj)按照PE-COFF格式存储;在Linux下,目标文件按照ELF格式存储。PE-COFF、ELF格式都是Unix的COFF变种,COFF主要贡献是在目标文件中引入的“段”的机制。 下面以Linux下gcc编译产生的目标文件为主,介绍目标文件结构。

实验代码

首先编写如下代码的源文件,在Linux环境下,使用gcc编译器编译产生目标文件SimpleSection.o。以SimpleSection.o为例,分析目标文件结构。

// SimpleSection.c 源文件int printf(const char* format, ...);int global_init_var = 255;int global_uninit_var;void func1 (int i){    printf("%d\n", i);}int main(void){    static int static_var = 256;    static int static_var2;    int a = 1;    int b;    func1(static_var + static_var2 + a + b);    return a;}

编译产生目标文件,无需链接:gcc -c SimpleSection.c。

ELF目标文件总体结构

ELF目标文件的总体结构

ELF目标文件的总体结构如上图所示。不同操作系统下不同编译器生成的目标文件结构可能会稍有不同。

首先是ELF文件头(ELF Header)。像其他文件的文件头一下,ELF文件头保存了ELF文件的基本信息。根据文件头可以读取解析整个目标文件。文件头以下是各个段,主要包括代码段(.text)、数据段(.data)、bss段(.bss)等。之后还有段表(Section header table)、字符串表(String Tables)、符号表(Symbol Tables)等信息。

ELF文件头

文件头将目标文件基本信息保存在一个结构体中,如下:

 typedef struct {     unsigned char   e_ident[16];    //     Elf32_Half      e_type;     Elf32_Half      e_machine;     Elf32_Word      e_version;     Elf32_Addr      e_entry;     Elf32_Off       e_phoff;              Elf32_Off       e_shoff;             //  段表偏移    Elf32_Word      e_flags;     Elf32_Half      e_ehsize;     Elf32_Half      e_phentsize;     Elf32_Half      e_phnum;     Elf32_Half      e_shentsize;      //  段描述符大小    Elf32_Half      e_shnum;          //  段的数量    Elf32_Half      e_shstrndx;       //  段字符串表在段表中的下标} Elf32_Ehdr;

在终端输入readelf -h SimpleSection.o,可以查看SimpleSection.o文件头:

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: 1056 (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: 13
Section header string table index: 10

段表结构

typedef struct {     Elf32_Word sh_name;         Elf32_Word sh_type;     Elf32_Word sh_flags;        Elf32_Addr sh_addr;     Elf32_Off sh_offset;     Elf32_Word sh_size;         Elf32_Word sh_link;     Elf32_Word sh_info;     Elf32_Word sh_addralign;     Elf32_Word sh_entsize; } Elf32_Shdr;

查看段表readelf -S SimpleSection.o :
There are 13 section headers, starting at offset 0x420:

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 0000000000000054 0000000000000000 AX 0 0 1
[ 2] .rela.text RELA 0000000000000000 00000378 0000000000000078 0000000000000018 I 11 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 0000000000000004 0000000000000000 A 0 0 1
[ 6] .comment PROGBITS 0000000000000000 000000a0 000000000000002e 0000000000000001 MS 0 0 1
[ 7] .note.GNU-stack PROGBITS 0000000000000000 000000ce 0000000000000000 0000000000000000 0 0 1
[ 8] .eh_frame PROGBITS 0000000000000000 000000d0 0000000000000058 0000000000000000 A 0 0 8
[ 9] .rela.eh_frame RELA 0000000000000000 000003f0 0000000000000030 0000000000000018 I 11 8 8
[10] .shstrtab STRTAB 0000000000000000 00000128 0000000000000061 0000000000000000 0 0 1
[11] .symtab SYMTAB 0000000000000000 00000190 0000000000000180 0000000000000018 12 11 8
[12] .strtab STRTAB 0000000000000000 00000310 0000000000000066 0000000000000000 0 0 1

Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), l (large)
I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)
O (extra OS processing required) o (OS specific), p (processor specific)

符号表结构

 Typedef struct {    Elf32_Word     st_name;    Elf32_Addr      st_value;    Elf32_Word     st_size;    unsigned char st_info;    unsigned char st_other;    //     Elf32_Half       st_shndx;   // 符号所在的段} Elf32_Sym;

查看符号表readelf -s SimpleSection.o :
Symbol table ‘.symtab’ contains 16 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000000 0 FILE LOCAL DEFAULT ABS SimpleSection.c
2: 0000000000000000 0 SECTION LOCAL DEFAULT 1
3: 0000000000000000 0 SECTION LOCAL DEFAULT 3
4: 0000000000000000 0 SECTION LOCAL DEFAULT 4
5: 0000000000000000 0 SECTION LOCAL DEFAULT 5
6: 0000000000000004 4 OBJECT LOCAL DEFAULT 3 static_var.1731
7: 0000000000000000 4 OBJECT LOCAL DEFAULT 4 static_var2.1732
8: 0000000000000000 0 SECTION LOCAL DEFAULT 7
9: 0000000000000000 0 SECTION LOCAL DEFAULT 8
10: 0000000000000000 0 SECTION LOCAL DEFAULT 6
11: 0000000000000000 4 OBJECT GLOBAL DEFAULT 3 global_init_var
12: 0000000000000004 4 OBJECT GLOBAL DEFAULT COM global_uninit_var
13: 0000000000000000 33 FUNC GLOBAL DEFAULT 1 func1
14: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND printf
15: 0000000000000021 51 FUNC GLOBAL DEFAULT 1 main

SimpleSection.o结构

SimpleSection.o结构

根据文件头、段表、字符串表信息,可以分析出整个目标文件的结构,如上图所示。整个文件的大小0x00000760 = 1888 Byte。注意到,由于对齐的原因,.symtab、.rela.text的起始地址与前面的段并不连续。根据段表可以知道,.symtab、.rela.text段都要求起始地址能被8整除。

变量在ELF文件中的存储

已初始化全局变量 ——〉 .data
已初始化局部静态变量 ——〉 .data
未初始化全局变量 ——〉 .bss
未初始化局部静态变量 ——〉 .bss
只读数据 ——〉 .rodata
局部变量 ——〉 .text

对于C++程序

  • 段表:group段;
  • C++符号修饰:C构造函数,D析构函数,C/D0默认构造/析构函数;
  • 构造/析构函数:只要我们定义了构造函数,系统不再生成默认的构造函数;不论我们有没有定义析构函数,系统都会生成默认的析构函数;
  • 每个被明确调用成员函数都是一个段;
  • 数据段:类的初始化静态非const变量放在.data段,类的初始化静态const变量放在.rodata段。

待完善…

原创粉丝点击