《程序员的自我修养》读书笔记——使用KEIL研究ELF文件

工具

KEIL-MDK V5

安装目录下(C:\Keil_v5\ARM\ARMCC\bin)有以下工具:

主要用到的是fromelf.exe

 

把该路径加入到PATH环境变量中:

 

fromelf用法

命令格式:

fromelf [options] input_file 

 

fromelf -h即可打印出帮助信息。


Product: MDK Plus 5.29
Component: ARM Compiler 5.06 update 6 (build 750)
Tool: fromelf [4d35e3]
For support see http://www.arm.com/support
Software supplied by: ARM Limited

ARM image conversion utility
fromelf [options] input_file

Options:
       --help         display this help screen
       --vsn          display version information
       --output file  the output file. (defaults to stdout for -text format)
       --nodebug      do not put debug areas in the output image
       --nolinkview   do not put sections in the output image

Binary Output Formats:
       --bin          Plain Binary
       --m32          Motorola 32 bit Hex
       --i32          Intel 32 bit Hex
       --vhx          Byte Oriented Hex format

       --base addr    Optionally set base address for m32,i32

Output Formats Requiring Debug Information
       --fieldoffsets Assembly Language Description of Structures/Classes
       --expandarrays Arrays inside and outside structures are expanded

Other Output Formats:
       --elf         ELF
       --text        Text Information

                Flags for Text Information
                -v          verbose
                -a          print data addresses (For images built with debug)
                -c          disassemble code
                -d          print contents of data section
                -e          print exception tables
                -g          print debug tables
                -r          print relocation information
                -s          print symbol table
                -t          print string table
                -y          print dynamic segment contents
                -z          print code and data size information
ARM 映像转换工具
fromelf [options] input_file 
选项: 
--help 显示帮助信息 
--vsn 显示版本信息 
--output file 输出文件名. (默认输出 -text 格式) 
--nodebug 不要输出调试信息到映像文件中 
--nolinkview 不要输出段信息到映像文件中 

二进制输出格式: 
--bin 普通二进制 
--m32 摩托罗拉32位Hex码 
--i32 英特尔32位Hex码 
--vhx 定向字节的 Hex 格式 
--base addr 为 m32,i32设置基地址(可选的) 

输出格式要求的调试信息 
--fieldoffsets Structures/Classes的汇编描述 
--expandarrays Arrays inside and outside structures are expanded 

其他输出格式: 
--elf ELF格式 
--text 文本信息 
    文本信息的标志 
    -v 详细信息 
    -a 打印数据的地址信息 (得到的.axf映像文件) 
    -c 汇编码 
    -d 打印数据的段内容 
    -e 打印例表 
    -g 打印调试表 
    -r 打印重定位信息 
    -s 打印符号表 
    -t 打印字符表 
    -y 打印段内容分析 
    -z 打印代码与数据的大小信息

 

 参考官方文档:

Using fromelf

https://www.keil.com/support/man/docs/armutil/armutil_pge1362128877330.htm

 

或者系统自带帮助文档:

ARM ® Compiler v5.06 for µVision ® Version 5
fromelf User Guide

C:\Keil_v5\ARM\Hlp\DUI0459F_02_mdk_fromelf_user_guide.pdf

 

ELF文件整体结构:

  • ELF头部(ELF_Header): 每个ELF文件都必须存在一个ELF_Header,这里存放了很多重要的信息用来描述整个文件的组织,如: 版本信息,入口信息,偏移信息等。程序执行也必须依靠其提供的信息。
  • 程序头部表(Program_Header_Table): 可选的一个表,用于告诉系统如何在内存中创建映像,在图中也可以看出来,有程序头部表才有段,有段就必须有程序头部表。其中存放各个段的基本信息(包括地址指针)。
  • 节区头部表(Section_Header_Table): 类似与Program_Header_Table,但与其相对应的是节区(Section)。
  • 节区(Section): 将文件分成一个个节区,每个节区都有其对应的功能,如符号表,哈希表等。
  • 段(Segment): 嗯…就是将文件分成一段一段映射到内存中。段中通常包括一个或多个节区

 

32位ELF文件中常用的数据类型:

这里写图片描述

 

实例分析ELF文件

写个简单的程序,不生成调试信息,生成axf文件。

编译结果:指令代码336字节,只读数据320字节,以0初始化的变量1632字节。

指令代码+只读数据=336+320=656字节

再加上ZI-data=656+1632=2288字节

 

进入工程输出目录,输入命令:

......\project\ARM_prj\Objects>    fromelf -v STM32F103_study.axf

 

文件头部

先看文件头信息:

========================================================================

** ELF Header Information

    File Name: STM32F103_study.axf

    Machine class: ELFCLASS32 (32-bit)
    Data encoding: ELFDATA2LSB (Little endian)
    Header version: EV_CURRENT (Current version)
    Operating System ABI: none
    ABI Version: 0
    File Type: ET_EXEC (Executable) (2)
    Machine: EM_ARM (ARM)

    Image Entry point: 0x08000131
    Flags: EF_ARM_HASENTRY + EF_ARM_ABI_FLOAT_SOFT (0x05000202)

    ARM ELF revision: 5 (ABI version 2)

    Conforms to Soft float procedure-call standard

    Built with
    Component: ARM Compiler 5.06 update 6 (build 750) Tool: armasm [4d35ec]
    Component: ARM Compiler 5.06 update 6 (build 750) Tool: armlink [4d35ed]

    Header size: 52 bytes (0x34)
    Program header entry size: 32 bytes (0x20)
    Section header entry size: 40 bytes (0x28)

    Program header entries: 1
    Section header entries: 6

    Program header offset: 2588 (0x00000a1c)
    Section header offset: 2620 (0x00000a3c)

    Section header string table index: 5

========================================================================

 

解析结果

文件类型为:可执行文件。 【File Type: ET_EXEC (Executable) (2)】

机器类型为:ARM。【Machine: EM_ARM (ARM)】

文件头有0x34个字节:【Header size: 52 bytes (0x34)】

程序头的大小为32个字节【Program header entry size: 32 bytes (0x20)】

节头的大小为40个字节【Section header entry size: 40 bytes (0x28)】

 

包含1个程序段(program),和6个节段(section)。

【Program header entries: 1
    Section header entries: 6】

 

程序头和节头的偏移量:

 Program header offset: 2588 (0x00000a1c)
    Section header offset: 2620 (0x00000a3c)

 

 

程序头部

程序头部表是可选的。

========================================================================

** Program header #0

    Type          : PT_LOAD (1)
    File Offset   : 52 (0x34)
    Virtual Addr  : 0x08000000
    Physical Addr : 0x08000000
    Size in file  : 656 bytes (0x290)
    Size in memory: 2288 bytes (0x8f0)
    Flags         : PF_X + PF_W + PF_R + PF_ARM_ENTRY (0x80000007)
    Alignment     : 8


========================================================================

从中可以看出,程序在文件中的大小为656字节,位置从0x34开始(紧接着ELF文件头);程序在存储器中的大小为2288字节。与KEIL中的编译信息一致。

【Size in file  : 656 bytes (0x290)】

【Size in memory: 2288 bytes (0x8f0)】

虚拟地址和物理地址均为0x08000000

【Virtual Addr  : 0x08000000】

【Physical Addr : 0x08000000】

 

节信息

第一个节为ER_IROM1,656字节的code

** Section #1

    Name        : ER_IROM1
    Type        : SHT_PROGBITS (0x00000001)
    Flags       : SHF_ALLOC + SHF_EXECINSTR (0x00000006)
    Addr        : 0x08000000
    File Offset : 52 (0x34)
    Size        : 656 bytes (0x290)
    Link        : SHN_UNDEF
    Info        : 0
    Alignment   : 4
    Entry Size  : 0


====================================

 

第二个节为RW_IRAM1,1632个字节,为ZI-data

====================================

** Section #2

    Name        : RW_IRAM1
    Type        : SHT_NOBITS (0x00000008)
    Flags       : SHF_ALLOC + SHF_WRITE (0x00000003)
    Addr        : 0x20000000
    File Offset : 708 (0x2c4)
    Size        : 1632 bytes (0x660)
    Link        : SHN_UNDEF
    Info        : 0
    Alignment   : 8
    Entry Size  : 0


====================================

 

字段解析

文件头:

 

ELF文件头的定义参考linux下的头文件:/usr/include/elf.h

/* ELF Header */
#define EI_NIDENT 16
typedef struct elfhdr {
    unsigned char    e_ident[EI_NIDENT]; /* ELF Identification */
    Elf32_Half    e_type;        /* object file type */
    Elf32_Half    e_machine;    /* machine */
    Elf32_Word    e_version;    /* object file version */
    Elf32_Addr    e_entry;      /* virtual entry point */
    Elf32_Off     e_phoff;      /* program header table offset */
    Elf32_Off     e_shoff;      /* section header table offset */
    Elf32_Word    e_flags;      /* processor-specific flags */
    Elf32_Half    e_ehsize;     /* ELF header size */
    Elf32_Half    e_phentsize;    /* program header entry size */
    Elf32_Half    e_phnum;      /* number of program header entries */
    Elf32_Half    e_shentsize;    /* section header entry size */
    Elf32_Half    e_shnum;      /* number of section header entries */
    Elf32_Half    e_shstrndx;    /* section header table's "section 
                       header string table" entry offset */
} Elf32_Ehdr;

 

与文件中的字节对应起来:

  unsigned char e_ident[EI_NIDENT];//魔数:7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
  Elf32_Half e_type;     //它标识的是该文件的类型。// 02 00 = 0x0002=ET_EXEC,可执行文件
  Elf32_Half e_machine;  //表明运行该程序需要的体系结构。//28 00 = 0x0028=ARM
  Elf32_Word e_version;  // 表示文件的版本。//01 00 00 00 = 0x00000001
  Elf32_Addr e_entry;    //程序的入口地址。//31 01 00 08 = 0x08000131
  Elf32_Off e_phoff;   //表示Program header table 在文件中的偏移量//1C 0A 00 00=0x00000A1C
  Elf32_Off e_shoff;   //表示Section header table 在文件中的偏移量//3C 0A 00 00=0x00000A3C
  Elf32_Word e_flags;   // 对IA32而言,此项为0。//02 02 00 05 = 0x05000202
  Elf32_Half e_ehsize;  //表示ELF header大小 //34 00 = 0x0034 = 52
  Elf32_Half e_phentsize;  //表示Program header table中每一个条目的大小。//20 00 = 0x0020 = 32
  Elf32_Half e_phnum;   //表示Program header table中有多少个条目。//01 00 = 0x0001=1
  Elf32_Half e_shentsize; // 表示Section header table中的每一个条目的大小//28 00 = 0x0028=40
  Elf32_Half e_shnum;   //表示Section header table中有多少个条目。//06 00 = 0x0006=6
  Elf32_Half e_shstrndx;  //包含节名称的字符串是第几个节(从零开始计数)//05 00 = 0x0005=5

 

关于前面16个字节(e_ident[EI_NIDENT])的说明:

这里写图片描述

具体解释如下:

这里写图片描述

 

文件头的其他部分说明:

这里写图片描述

 

 

程序头

文件末尾为Program header entries和Section header entries,总大小为 32 * 1 + 40 * 6 = 272 = 0x110,位置为0x0A1C ~ 0x0B2B.

 

其中,程序头大小为32字节,位置为0x0A1C ~ 0x0A3B,内容为:

定义:

/* Program Header */
typedef struct {
    Elf32_Word    p_type;        /* segment type */
    Elf32_Off    p_offset;    /* segment offset */
    Elf32_Addr    p_vaddr;    /* virtual address of segment */
    Elf32_Addr    p_paddr;    /* physical address - ignored? */
    Elf32_Word    p_filesz;    /* number of bytes in file for seg. */
    Elf32_Word    p_memsz;    /* number of bytes in mem. for seg. */
    Elf32_Word    p_flags;    /* flags */
    Elf32_Word    p_align;    /* memory alignment */
} Elf32_Phdr;

其中,p_type的定义:

这里写图片描述

 

节(Section)

节头部有10个元素,共40个字节 ,具体定义如下:

/* Section Header */
typedef struct {
    Elf32_Word    sh_name;    /* name - index into section header
                       string table section */
    Elf32_Word    sh_type;    /* type */
    Elf32_Word    sh_flags;    /* flags */
    Elf32_Addr    sh_addr;    /* address */
    Elf32_Off     sh_offset;    /* file offset */
    Elf32_Word    sh_size;    /* section size */
    Elf32_Word    sh_link;    /* section header table index link */
    Elf32_Word    sh_info;    /* extra information */
    Elf32_Word    sh_addralign;    /* address alignment */
    Elf32_Word    sh_entsize;    /* section entry size */
} Elf32_Shdr;

解释:

这里写图片描述

 

sh_type节区的类型:

这里写图片描述

sh_flags 字段定义了一个节区中包含的内容是否可以修改、是否可以执行等信息。 如果一个标志位被设置,则该位取值为 1。 定义的各位都设置为 0。

这里写图片描述

其中已经定义了的各位含义如下:

  • SHF_WRITE: 节区包含进程执行过程中将可写的数据。
  • SHF_ALLOC: 此节区在进程执行过程中占用内存。某些控制节区- 并不出现于目标文件的内存映像中,对于那些节区,此位应设置为 0。
  • SHF_EXECINSTR: 节区包含可执行的机器指令。
  • SHF_MASKPROC: 所有包含于此掩码中的四位都用于处理器专用的语义。

根据节区类型的不同,sh_link 和 sh_info 的具体含义也有所不同:

这里写图片描述

 

特殊节区
系统使用的节区,以及它们 的类型和属性:

这里写图片描述

 

在分析这些节区的时候,需要注意如下事项:

  • 以“.”开头的节区名称是系统保留的。应用程序可以使用没有前缀的节区名称,以避 免与系统节区冲突。
  • 目标文件格式允许人们定义不在上述列表中的节区。
  • 目标文件中也可以包含多个名字相同的节区。
  • 保留给处理器体系结构的节区名称一般构成为:处理器体系结构名称简写 + 节区名称。
  • 处理器名称应该与 e_machine 中使用的名称相同。例如 .FOO.psect 街区是由FOO 体系结构定义的 psect 节区。

 

还有其他的节,比如字符串表,符号表,参见以下博文。

ELF格式文件详细分析

https://blog.youkuaiyun.com/xuehuafeiwu123/article/details/72963229

 

进一步学习资料:

ELF文件结构详解

https://bbs.pediy.com/thread-255670.htm

链接与装载视图

Elf文件有2个平行视角:一个是程序链接角度,一个是程序装载角度。从链接的角度来看,Elf文件是按“Section”(节)的形式存储;而在装载的角度上,Elf文件又可以按“Segment”(段)来划分。实际上,Section和Segment难以从中文的翻译上加以区分。因为很多时候Section也被翻译成段,比如Section Header Table,有的资料叫段表、有的称为节区。

 

 

这里写图片描述

这里写图片描述

 

计算机原理系列之二 -------- 详解ELF文件

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值