ELF 文件详解

ELF(Executable and Linkable Format,可执行与可链接格式)是一种用于二进制文件、可执行文件、目标代码、共享库和核心转储的标准文件格式。它最初由 Unix 系统实验室(USL)设计,现已成为类 Unix 系统(如 Linux、FreeBSD、Android 等)中最常见的文件格式之一。


ELF 文件的主要类型
1.可执行文件(Executable)

可直接由操作系统加载运行的程序(如 /bin/ls)。

包含程序代码(text)、数据(data)、以及如何加载到内存的信息。

2.目标文件(Relocatable Object File)

由编译器生成(.o 文件),需通过链接器(linker)处理生成可执行文件或共享库。

包含代码和数据,但地址未最终确定(可重定位)。

3.共享库(Shared Object)

动态链接库(如 .so 文件),在运行时被加载到进程内存中(如 libc.so)。

允许多个程序共享同一份代码,节省内存。

4.核心转储文件(Core Dump)

程序崩溃时生成的内存快照,用于调试。


ELF 文件格式详解

1. ELF 文件基本结构

ELF 文件由以下几部分组成:

  1. ELF 头(ELF Header)
    • 描述文件的总体信息,如文件类型(可执行/目标文件)、目标架构(x86/ARM)、程序入口地址、节头表(Section Header)和程序头表(Program Header)的位置等。

    • 通过 readelf -h <file> 可查看。

  2. 程序头表(Program Header Table,对可执行文件重要)
    • 告诉操作系统如何将文件加载到内存(例如哪些段需要映射到内存)。

    • 每个条目描述一个段(Segment),如代码段(LOAD)、动态链接信息(INTERP)等。

    • 通过 readelf -l <file> 查看。

  3. 节头表(Section Header Table,对链接重要)
    • 包含文件的所有节(Section)信息,如代码节(.text)、数据节(.data)、符号表(.symtab)、字符串表(.strtab)等。

    • 链接器使用此表处理目标文件。

    • 通过 readelf -S <file> 查看。

2. ELF 头部 (ELF Header)

ELF 头部位于文件开头,包含文件的基本信息。

主要字段包括:

  • Magic Number:标识 ELF 文件的魔数(7f 45 4c 46)

  • Class:32位(01)或64位(02)

  • Data:字节序(小端序/大端序)

  • Version:ELF 版本

  • OS/ABI:目标操作系统和应用二进制接口

  • Type:文件类型(可执行、共享库、目标文件等)

  • Machine:目标架构(x86、ARM等)

  • Entry point address:程序入口点

  • Program header table offset:程序头表偏移

  • Section header table offset:节头表偏移

  • Flags:处理器特定标志

  • Header size:ELF 头部大小

  • Program header size:程序头表中每个条目的大小

  • Number of program headers:程序头表中条目数量

  • Section header size:节头表中每个条目的大小

  • Number of section headers:节头表中条目数量

  • Section name string table index:包含节名称的字符串表的索引

3. 程序头表 (Program Header Table)

描述如何创建进程内存映像,仅存在于可执行文件和共享库中。使用 readelf -l <file> 查看。

包含多个程序头(Program Header),每个描述一个段(Segment),常见类型包括:

  • PT_LOAD:可加载段

  • PT_DYNAMIC:动态链接信息

  • PT_INTERP:程序解释器路径(通常是动态链接器)

  • PT_NOTE:辅助信息

  • PT_PHDR:程序头表自身的位置和大小

每个程序头包含:

  • 段类型

  • 文件偏移

  • 虚拟地址

  • 物理地址(系统相关)

  • 文件中的大小

  • 内存中的大小

  • 权限标志(读/写/执行)

  • 对齐方式

4. 节头表 (Section Header Table)

描述文件的各个节(Section),用于链接和调试。使用 readelf -S <file> 查看。

常见节包括:

  • .text:可执行代码

  • .data:已初始化的数据

  • .bss:未初始化的数据

  • .rodata:只读数据

  • .symtab:符号表

  • .strtab:字符串表

  • .shstrtab:节名称字符串表

  • .rel. / .rela.**:重定位信息

  • .dynamic:动态链接信息

  • .interp:程序解释器路径名

  • .got / .plt:全局偏移表和过程链接表(用于动态链接)

每个节头包含:

  • 节名称(字符串表索引)

  • 节类型

  • 标志(可写、可分配、可执行等)

  • 虚拟地址

  • 文件偏移

  • 节大小

  • 链接到其他节的索引

  • 额外信息

  • 对齐要求

  • 条目大小(对于表格类型的节)


工具和命令

  • readelf:查看 ELF 文件信息

  • objdump:反汇编和查看目标文件信息

  • nm:列出符号表

  • ldd:列出动态依赖

  • file:识别文件类型

  • strip:删除符号表和调试信息


以"Hello World" 程序为例,展示如何在 Linux 下分析 ELF 文件。

1. 创建 Hello World 程序
首先创建一个简单的 C 程序:
// hello.c
#include <stdio.h>

int main() {
    printf("Hello, World!\n");
    return 0;
}

编译它(不优化并包含调试信息以便更好观察):

gcc -g -o hello hello.c
2. 查看 ELF 文件类型
file hello

输出:

hello: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=..., for GNU/Linux 3.2.0, with debug_info, not stripped

这告诉我们:

64位 ELF 文件,小端序 (LSB),动态链接,使用 /lib64/ld-linux-x86-64.so.2 作为解释器,包含调试信息,符号表未被剥离

3. 查看 ELF 头部
readelf -h hello

输出示例(部分):

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:                              DYN (Shared object file)
  Machine:                           Advanced Micro Devices X86-64
  Version:                           0x1
  Entry point address:               0x1060
  Start of program headers:          64 (bytes into file)
  Start of section headers:          14712 (bytes into file)
  Flags:                             0x0
  Size of this header:               64 (bytes)
  Size of program headers:           56 (bytes)
  Number of program headers:         13
  Size of section headers:           64 (bytes)
  Number of section headers:         36
  Section header string table index: 35
4. 反汇编查看代码
objdump -d hello

输出示例(部分):

0000000000001060 <main>:
    1060:       55                      push   %rbp
    1061:       48 89 e5                mov    %rsp,%rbp
    1064:       48 8d 3d 99 0f 00 00    lea    0xf99(%rip),%rdi        # 2004 <_IO_stdin_used+0x4>
    106b:       e8 e0 fe ff ff          callq  f50 <puts@plt>
    1070:       b8 00 00 00 00          mov    $0x0,%eax
    1075:       5d                      pop    %rbp
    1076:       c3                      retq   

5. 查看依赖的动态库
ldd hello

输出示例:

        linux-vdso.so.1 (0x00007ffd5b3e6000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f8c4a6c4000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f8c4a8d7000)
6. 查看其他的(篇幅原因不做演示)

查看程序头表(段信息):readelf -l hello

查看节头表(节信息):readelf -S hello

查看符号表:readelf -s hello

查看动态段信息:readelf -d hello

通过这些工具和命令,可以全面了解一个 ELF 可执行文件的结构和内容。从头部信息到各个段和节,从符号表到动态链接信息,这些构成了 Linux 下可执行文件的基本要素。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值