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