2023-07-27 elf文件格式布局解析(部分)

2024-04-19 elf文件格式布局解析


上文链接:2023-06-27 操作系统分页机制的深入浅出

1、简介
  1. ELF 指的是 Exeeutable and Linkable Format ,可执行链接格式。最初是由 UNIX 系统实验室 CUSL)
    作为应用程序二进制接口 CABI) 而开发和发行的。

    工具接口标准委员会( TIS )选择了它作为 IA32 体系结构上不同操作系统之间的可移植二进制文件格式,于是它就发展成为了事实上的二进制文件格式标准。

    在 ELF 规范中,把符合 ELF 格式协议的文件统称为“目标文件”或 ELF 文件,这与我们平时所说的目标文件是不同的。在大家平时的习惯中,咱们把编译后,但未经链接的文件称为目标文件,也称为待重定位文件( reloeatable file ),比如在 Linux 下用 gee -c参数生成的 .o 文件。

    而平时我们所说的 ELF 文件也是指经过编译链接后的二进制可执行文件,该文件能够直接运行。
    为了避免混淆,咱们采用与 ELF 规范相同的命名方式,本节中所说的目标文件即指各种类型符合 ELF
    规范的文件,如二进制可执行文件和 Linux 下 .o 结尾的目标文件和 .so 结尾的动态库文件。

    • elf文件包括二进制可执行文件、常说的目标文件(待重定向文件—可通过链接生成可执行文件)、动态链接库文件(共享文件)
  2. elf文件格式布局(详情参考: Linux 系统的/usr/include/elf.h)

    其中程序头表(Program header table)中的每个条目(entry—Elf32_Phdr—占32字节)对应着程序头表后续段(Segment ),其中描述着段的类型和段在文件中的偏移等关键信息

    待重定向文件可执行文件
    ELF头(ELF header)ELF头(ELF header)
    Program header table (程序头表)— 可选Program header table(程序头表)
    Section 1(节1)Segment 1(段1)
    Section n(节n)Segment n(段n)
    Section header table (节头表)Section header table (节头表)— 可选
    待重定向文件文件体可执行文件体

2、格式布局(部分)

  1. ELF头

    • 数据类型:

      • Elf32_Half(无符号2字节)
      • Elf32_Word(无符号4字节)
      • Elf32_Addr(无符号4字节),无符号程序运行地址
      • Elf32_Off(无符号4字节),无符号文件偏移量
    • 数据结构(共52字节):

      typedef struct
      {
        unsigned char	e_ident[16];	/* 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;
      
    • 含义:

      • e_ident[16](16字节)

        0~3: 这 4 位是固定的 ELF 文件的魔数,表明这就是 一 个 ELF文件

        4:值为 0 表示该文件是不可识别类型;值为 l 表示该文件是 32 位 elf 格式的文件;值为 2 表示该文件是 64 位 elf 格式的文件。

        5:值为 0 表示非法编码格式;值为 1 表示小端字节序,即 LSB (最低有效字节);值为 2 表示大端字节序,即 MSB (最高有效字节);补充:file /bin/ls 可查看系统elf字节序

        6:ELF 头的版本信息,默认为 1;值为 0 表示非法版本;值为 1 表示当前版本

        7~15:暂且不用,保留,均初始化为 0

      • e_type(2字节):来指定 elf 目标文件的类型

        取值作用如下:

        0:未知目标文件格式,忽略

        1:可重定位文件

        2:可执行文件

        3:动态共享目标文件

        4:core 文件,即程序崩溃时其内存映像的转储格式,俗称出 core 了

        0xff00:特定处理器文件的扩展下边界(硬件相关的参数)

        0xffff:特定处理器文件的扩展上边界(硬件相关的参数)

      • e_machine(2字节):用来描述 elf 目标文件的体系结构类型,也就是说该文件要在哪种硬件平台(哪
        种机器)上才能运行

        取值作用如下:

        0:未指定

        1:AT&T WE 32100

        2:SPARC

        3:Intel 80386

        4;Motorola 68000

        5:Motorola 88000

        7:Intel 80860

        8:MIPS RS3000

      • e_version(4字节):表示版本信息。

      • e_entry(4字节):指明操作系统运行该程序时,将控制权转交到的虚拟地址。

      • e_phoff(4字节):指明程序头表( program header table )在文件内的字节偏移量。如果没有程
        序头表,该值为 0 。

      • e_shoff(4字节):指明节头表( section header table )在文件内的字节偏移量。若没有节头表,
        该值为 0 。

      • e_flags(4字节):指明与处理器相关的标志

      • e_ehsize(2字节):指明 elf header 的字节大小。

      • e_phentsize(2字节):指明程序头表( program header table )中每个条目( entry )的字节大小,
        即每个用来描述段信息的数据结构的字节大小,该结构是后面要介绍的 struct Elf32 Phdr。

      • e_phnum(2字节):指明程序头表中条目的数量。实际上就是段的个数。

      • e_shentsize(2字节):指明节头表( section header table )中每个条目( entry)的字节大小,即
        每个用来描述节信息的数据结构的字节大小。

      • e_shnum(2字节):节头表中条目的数量。实际上就是节的个数。

      • e_shstrndx(2字节):指明 string name table 在节头表中的索引 index 。

  2. Program header table(程序头表)

    • 数据类型:

      • Elf32_Half(无符号2字节)
      • Elf32_Word(无符号4字节)
      • Elf32_Addr(无符号4字节),无符号程序运行地址
      • Elf32_Off(无符号4字节),无符号文件偏移量
    • 数据结构(共32字节):

      typedef struct
      {
        Elf32_Word	p_type;			/* Segment type */
        Elf32_Off	p_offset;		/* Segment file offset */
        Elf32_Addr	p_vaddr;		/* Segment virtual address */
        Elf32_Addr	p_paddr;		/* Segment physical address */
        Elf32_Word	p_filesz;		/* Segment size in file */
        Elf32_Word	p_memsz;		/* Segment size in memory */
        Elf32_Word	p_flags;		/* Segment flags */
        Elf32_Word	p_align;		/* Segment alignment */
      } Elf32_Phdr;
      
    • 含义:

      • p_type(4字节):指明程序中该段的类型。

        取值作用如下:

        0:忽略

        1;可加载程序段

        2;动态链接信息

        3:动态加载器名称

        4:一些辅助的附加信息

        5:保留

        6;程序头表

        0x70000000:(硬件相关的参数)

        0x7fffffff:(硬件相关的参数)

      • p_offset(4字节):指明本段在文件内的起始偏移字节。

      • p_vaddr(4字节):指明本段在内存中的起始虚拟地址 。

      • p_paddr(4字节):仅用于与物理地址相关的系统中,因为 System V 忽略用户程序中所有的物理地
        址,所以此项暂且保留,未设定。

      • p_filesz(4字节):指明本段在文件中的大小。

      • p_memsz(4字节):指明本段在内存中的大小。

      • p_flags(4字节):指明与本段相关的标志

        取值作用如下:

        1:本段具有可执行权限

        2:本段具有可写权限

        4:本段具有可读权限

        0xf0000000:(硬件相关的参数)

        0x0ff00000:本段与操作系统相关

      • p_align(4字节):指明本段在文件和内存中的对齐方式。如果值为 0 或 1 ,则表示不对齐。否
        则 p_align 应该是 2 的幂次数。

  3. 节头表(共40字节)

    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;
    

3、解析elf文件实例

  1. 使用xxd命令查看我内核可执行文件(xxd是自己参照书编写的脚本,kernel.bin也是参照书编写、链接的可执行文件)

    [xue@localhost 6]$ sh xxd.sh bin/kernel.bin 0 400
    0000000: 7F 45 4C 46 01 01 01 00 00 00 00 00 00 00 00 00  .ELF............
    0000010: 02 00 03 00 01 00 00 00 00 15 00 C0 34 00 00 00  ............4...
    0000020: 58 06 00 00 00 00 00 00 34 00 20 00 02 00 28 00  X.......4. ...(.
    0000030: 07 00 06 00 01 00 00 00 00 00 00 00 00 10 00 C0  ................
    0000040: 00 10 00 C0 3C 05 00 00 3C 05 00 00 05 00 00 00  ....<...<.......
    0000050: 00 10 00 00 51 E5 74 64 00 00 00 00 00 00 00 00  ....Q.td........
    0000060: 00 00 00 00 00 00 00 00 00 00 00 00 06 00 00 00  ................
    0000070: 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
    0000080: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
    *
    0000180: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
    
  2. elf头

    • 第一行: e_ident 数组(16),前 4 字节是固定的 elf 魔数,正如您看到的,它们是 Ox7f 和字符 ELF 的 ASCII:
      0x45 、0x4c 、0x46 。所以您在显示区看到了 ELF 的三个字符。紧跟其后的 三个 01 分别是 e_ident[4]、 e_ident[5 ]、e_ident[6] 三个成员,代表的意义是 32 位 elf 文件、小端字节序、当前版本。后面的 9 个 00 是 ident[7] ~e_ident[15] ,这些确实都已经初始化为 0。
    • 第二行:
      • e_type(2):小端字节序,所以其值为 0x0002—可执行文件
      • e_machine(2):0x0003—Intel 80386
      • e_version(4):0x0000 0001
      • e_entry(4):0xc000 1500—虚拟地址
      • e_phoff(4):0x0000 0034—程序头表在文件内的字节偏移量(与e_ehsize值相同,因为elf头结束后就是程序头表)
    • 第三行:
      • e_shoff(4):0x0000 0658—节头表在文件内的字节偏移量
      • e_flags(4):0x0000 0000
      • e_ehsize(2):0x0034—即52字节
      • e_phentsize(2):0x0020—即32字节(Elf32_Phdr结构大小)
      • e_phnum(2):0x0002 — 代表共有2个段
      • e_shentsize(2):0x0028— 即40字节(Elf_Shdr结构大小)
    • 第四行:
      • e_shnum(2):0x0007—节数量
      • e_shstrndx(2):0x0006—string table name
  3. 程序表头

    这里只解析程序头表的第一个条目(entry)即第一个段,注意从段偏移和段大小看,第一段的包括了整个elf头和程序头+第一段,第二段的偏移直接从第一段的p_filesz+1开始

    • 第四行:
      • p_type(4):0x0000 0001—可加载程序段
      • p_offset(4):0x0000 0000 —段1在文件内偏移地址
      • p_vaddr(4):0xc000 1000—段1在内存中的起始虚拟地址
    • 第五行:
      • p_paddr(4):0xc000 1000—段1在内存中的起始虚拟地址
      • p_filesz(4):0x0000 053c—段在文件中的大小
      • p_memsz(4):0x0000 053c—段在内存中的大小
      • p_flags(4):0x0000 0005—表示与本段相关的标志。 5=4+l=PF_R+PF_X,在此表示可读,可执行,根据此属性,我们推测此段为代码段 。
    • 第六行:
      • p_align(4):0x0000 1000—对齐

补充:

1、readelf 是一个用于显示和分析可执行文件(包括二进制可执行文件和共享对象文件)的命令行工具。它是 GNU Binutils 包的一部分,通常在类 Unix 系统上使用。

readelf 命令可以提供有关可执行文件的详细信息,包括文件头、节头表、符号表、重定位表、动态链接信息等。它可以帮助开发人员了解和调试程序的二进制结构。

以下是 readelf 命令的一些常用选项:

  • -a--all:显示所有可用的信息。
  • -h--file-header:显示文件头信息。
  • -S--sections:显示节头表信息。
  • -l--program-headers:显示程序头信息(仅适用于可执行文件)。
  • -s--symbols:显示符号表信息。
  • -r--relocs:显示重定位表信息。
  • -d--dynamic:显示动态链接信息。
  • -A--arch-specific:显示特定于体系结构的信息。

2、xxd.sh

#!/bin/bash
xxd -u -a -g 1 -s $2 -l $3 $1
  • 30
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值