ELF目标文件结构

目标文件结构

编译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段。

待完善…

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值