《嵌入式工程师自我修养/C语言》系列——bin、hex、exe、elf文件类型到底有何区别?readelf用法全面解读!
- 一、常见文件类型之:bin、hex、elf、exe
- 1.1 bin、hex、exe、elf文件类型到底有何区别?
- 1.2 如何读懂一份hex文件?(hex文件格式详细解读)
- 1.2.1 每行的结构
- 1.2.2 记录类型(Record type)
- 1.2.3 校验和的计算
- 1.2.4 实例解析
- 1.3 如何实现bin文件和hex文件的相互转换?
- 1.3.1 linux环境下
- 1.3.2 linux环境下bin转hex如何指定起始地址?
- 1.3.2 Windows环境下
- 二、readelf常见用法解读
- 2.1 elf文件及其组成结构
- 2.2 readelf常见用法详解
快速学习嵌入式开发其他基础知识?>>>>>>>>> 返回专栏总目录 《嵌入式工程师自我修养/C语言》<<<<<<<<<
Tip📌:鼠标悬停双虚线关键词/句,可获得更详细的描述
一、常见文件类型之:bin、hex、elf、exe
1.1 bin、hex、exe、elf文件类型到底有何区别?
在嵌入式软件开发中,不同的文件格式承载着不同的信息和用途。了解这些文件格式的差异对于开发人员来说是非常重要的,特别是对于那些处理固件编写、调试和程序部署的开发者。以下是几种常见文件格式(bin、hex、elf、exe)的详细描述和它们之间的主要区别:
类型 | 定义 | 用途 | 特点 |
---|---|---|---|
BIN | 二进制格式文件,直接存储二进制数据,没有结构或头信息,仅包含裸数据。 | 常用于嵌入式系统,用于存储固件镜像,直接烧录到闪存。 | 大小较小,只包含代码和数据,但缺少调试信息,调试困难。 |
HEX | 十六进制格式文件,以文本形式表示的二进制数据文件,使用ASCII字符。 | 用于嵌入式系统固件的存储和传输,易于阅读和编辑。 | 通常比BIN文件大,因ASCII字符表示,可包含地址信息,方便定位烧录。 |
ELF | 可执行链接格式,通用的标准文件格式。 | 用于嵌入式Linux和UNIX-like系统,适用于可执行程序和调试。 | 灵活可扩展,包含代码和数据以及调试和配置信息,支持静态和动态链接。 |
EXE | Windows系统中的可执行文件格式。 | 存储Windows环境下的可执行程序代码。 | 为Windows设计,包含运行程序所需的所有信息,不适用于嵌入式系统。 |
综上,这几种文件有以下几方面的区别:
a. 适用环境不同:
BIN和HEX文件通常用于嵌入式系统中固件的烧录;ELF文件广泛用于UNIX-like系统的可执行文件和调试;EXE文件是Windows环境下的可执行文件。
b. 内容和结构不同:
BIN是裸二进制文件,HEX是文本形式的二进制数据,ELF文件具有高度的灵活性,支持复杂的程序结构和调试信息,而EXE是Windows可执行文件格式,包含了运行所需的全部信息。
c. 调试信息不同:
BIN文件和HEX文件通常不包含调试信息;而EXE和ELF文件可以包含丰富的调试信息,有助于开发和调试过程。
1.2 如何读懂一份hex文件?(hex文件格式详细解读)
十六进制(HEX)文件格式通常用于在编程过程中传输二进制信息。这种文件格式常见于微控制器和小型嵌入式系统的固件更新中。Intel HEX是其中最常见的一种HEX文件格式,我们将以此为例来解释。
Tip📌:Intel HEX文件格式是一种以文本形式存储二进制数据的文件格式,它的每一行代表了在特定内存地址上的数据记录。这种格式使得数据易于人类阅读和机器解析,并且可以被许多程序编程工具所接受。
一个标准的Intel HEX文件由多行构成,每行代表一个数据记录(Record)。下面是一个例子:
:020000040000FA
:10010000214601360121470136007EFE09D2190140
:100110002146017E17C20001FF5F16002148011928
:0C012000FFFFFFFFFFFFFFFF0200000000FA
:00000001FF
1.2.1 每行的结构
每一行数据记录有以下顺序和结构:
字段 | 说明 |
---|---|
开始码 (Start code) | 一个冒号(:),表明这是一行新的记录。 |
字节计数 (Byte count) | 两个十六进制数字,表示这一行中数据字段的字节数,不包括本身这个字段和其他字段 (通常为10或者20,分别表示这行有16个字节或者32个字节的数据)。 |
地址 (Address) | 接着是四个十六进制数字,表示数据将要被写入的起始地址。 |
记录类型 (Record type) | 两个十六进制数字,00到05之间,定义了数据记录的类型。 00代表数据记录;01代表文件结束记录;02代表扩展段地址记录;04代表扩展线性地址记录;05代表开始线性地址记录。 |
数据 (Data) | 数据本身,长度由字节计数字段确定,可以是任意数量的字节(最多255个)。 |
校验和 (Checksum) | 两个十六进制数字,是前面所有字节的反码加1(即二进制求和后取反加1)。 |
1.2.2 记录类型(Record type)
Intel HEX文件中最常见的几种记录类型如下:
记录类型代码 | 记录类型描述 |
---|---|
00 | 数据记录(Data Record):包含了在一个指定地址开始的二进制数据。 |
01 | 文件结束记录(End Of File Record):文件的结尾,表明没有更多的记录。 |
02 | 扩展段地址记录(Extended Segment Address Record):用来标识接下来的数据记录的段地址。 |
04 | 扩展线性地址记录(Extended Linear Address Record):用来标识接下来的数据记录的高位地址。 |
05 | 开始线性地址记录(Start Linear Address Record):定义了程序的起始执行地址。 |
1.2.3 校验和的计算
校验和的计算是对一行中的所有字节进行二进制求和(不包括开始冒号和校验和自身),然后取反加1。简化这种计算逻辑后可以按照下面的方法计算校验和:
- 将字节计数、地址、记录类型、和数据字段的所有值相加。
- 取这个总和的最低字节。
- 用0xFF减去这个最低字节,再加1。
这样得到的结果就是校验和。校验和的目的是为了确保数据的完整性,在接收端可以通过重新计算校验和来确认数据是否在传输过程中被篡改。例如,我们计算上面第一行数据:020000040000FA
的校验和:
02 + 00 + 00 + 04 + 00 + 00 = 06
0xFF - 0x06 + 0x01 = 0xFA
1.2.4 实例解析
以上述示例中的第二行为例:
:10010000214601360121470136007EFE09D2190140
:
是开始码。10
说明接下来有16个数据字节。0100
是这一行数据的起始地址。00
是数据记录类型,表示这是一个数据记录。- 接下来的
214601360121470136007EFE09D21901
是数据本身,共16个字节。 - 最后的
40
是校验和。
Tip📌:hex文件每行记录数字很多,还是很难阅读的,博主工作中使用vscode比较多,推荐个vscode中的插件,每行记录的结构有不同的色彩区分,阅读起来相对友好一些:
1.3 如何实现bin文件和hex文件的相互转换?
1.3.1 linux环境下
在Linux环境下,可以使用一些工具和命令行程序来实现二进制文件(bin)和十六进制文件(hex)之间的相互转换。下面是使用objcopy
工具来实现这两种文件格式之间转换的方法,objcopy
是GNU二进制工具集(Binutils)的一部分,通常在大多数Linux发行版中可用。
Bin文件 ------> Hex文件
objcopy -I binary -O ihex input_file.bin output_file.hex
-I
指定输入文件的格式,这里是binary
。-O
指定输出文件的格式,这里是ihex
,即Intel Hex格式。input_file.bin
是要转换的二进制文件。output_file.hex
是转换后生成的十六进制文件。
Hex文件 ------> Bin文件
objcopy -I ihex -O binary input_file.hex output_file.bin
-I
指定输入文件的格式,这里是ihex
。-O
指定输出文件的格式,这里是binary
。input_file.hex
是要转换的十六进制文件。output_file.bin
是转换后生成的二进制文件。
Tips📌:
- 在使用这些命令之前,需要确保系统中已经安装了GNU二进制工具集(Binutils)。如果没有安装,可以使用
sudo apt-get install binutils
来安装。 - 这些命令不需要特殊的运行权限,但是需要对输入文件有读取权限,同时对输出文件的目录有写入权限。
- 使用
objcopy
命令将bin文件转换为hex文件时,直接通过objcopy
命令参数指定起始地址的功能是不支持的。objcopy
工具主要用于格式转换,而不直接提供在转换过程中设置起始地址的选项。
1.3.2 linux环境下bin转hex如何指定起始地址?
如果想要bin文件转hex文件的时候指定hex文件的起始地址,可以通过其他方式间接实现。一种常见的方法是先将bin文件转换为其他格式(如ELF),在这个过程中指定起始地址,然后再从该中间格式转换为hex格式。下面是一个示例流程:
步骤1:Bin转ELF,指定起始地址
ld --oformat=elf32-littlearm -Ttext=起始地址(如0x08000000) -o output_file.elf input_file.bin
--oformat=elf32-littlearm
指定输出格式为ELF,具体的格式根据目标架构而定。-Ttext=起始地址
指定section的起始地址。output_file.elf
是转换后的ELF文件。input_file.bin
是原始的二进制文件。
步骤2:ELF转Hex
objcopy -O ihex output_file.elf output_file.hex
output_file.elf
是上一步生成的ELF文件,而output_file.hex
则是最终的hex文件。
Tips📌:
- 这个方法需要
ld
和objcopy
工具,这两个工具都属于GNU Binutils工具集,通常在大多数Linux系统中都是预安装的。 - 指定起始地址时,需要确保它适用于目标平台和应用场景。
1.3.2 Windows环境下
windows环境下直接推荐给大家两个工具,我工作中用起来还是比较顺手的:第一个工具比较简洁,但功能有限,主要就是用来完成bin和hex文件的转换,hex和bin互转工具(点击获取)
第二个工具极其强大并高端,它甚至能完成文件的签名校验等功能,格式转换就更不在话下了:一个强大的hex工具(点击获取)
二、readelf常见用法解读
2.1 elf文件及其组成结构
为了更好的了解elf文件的组成结构,为后续readelf的学习做准备,首先我们来自己制作一个elf文件。需要你有一台可以运行Linux操作系统的计算机或虚拟机,并且在Linux环境下已经安装了GCC编译器和gcc-arm-linux-gnueabi交叉编译器,Ubuntu环境下交叉编译器可以用下面的指令安装:apt-get install gcc-arm-linux-gnueabi gcc
。
先编写一个很简单的c文件:
/********* File: sub.h *********/
int add(int a, int b);
int sub(int a, int b);
/********* File: sub.c *********/
int add(int a, int b)
{
return a + b;
}
int sub(int a, int b)
{
return a - b;
}
/********* File: main.c *********/
#include <stdio.h>
#include "sub.h"
int global_val = 1;
int uninit_val;
int main(void)
{
int a, b;
static int local_val = 2;
static int uninit_local_val;
a = add(2, 3);
b = sub(5, 4);
printf("a = %d\n", a);
printf("b = %d\n", b);
return 0;
}
如果我们想让上面的程序在ARM平台上运行,则要使用ARM交叉编译器将C源程序编译生成ARM格式的二进制可执行文件,执行编译指令arm-linux-gnueabi-gcc -o a.out main.c sub.c
。将生成的二进制文件a.out复制到ARM平台上就可以直接运行了。不考虑这个编译生成的过程,先来研究下在这个可执行文件。
——section header、ELF header以及节头表的概念
一个可执行文件通常由不同的段(section)构成:代码段、数据段、BSS段、只读数据段等。每个section会有一个section header来描述这个段的段名、段的类型、段的起始地址、段的偏移和段的大小等信息,将这些section headers集中放到一起,就是节头表(section header table)。
此外,可执行文件还会有一个文件头ELF header,用来描述文件类型、要运行的处理器平台、入口地址等信息。当程序运行时,加载器会根据此文件头来获取可执行文件的一些信息。
Tips📌:section header table自身也是以一个section的形式存储在可执行文件中的。
——温故常说的代码段、数据段、BSS段
C程序中定义的函数、变量、未初始化的全局变量经过编译后会放置在不同的段中:
- 函数翻译成二进制指令放在代码段中;
- 初始化的全局变量和静态局部变量放在数据段中;
- 未初始化的全局变量和静态变量放在BSS段中,但是因为它们未初始化,默认值全部是0,其实没有必要再单独开辟空间存储,为了节省存储空间,所以在可执行文件中BSS段是不占用空间的。但是BSS段的大小、起始地址和各个变量的地址信息会分别保存在节头表和符号表.symtab里,当程序运行时,加载器会根据这些信息在内存中紧挨着数据段的后面为BSS段开辟一片存储空间,为各个变量分配存储单元。
程序编译的过程实际上就是将程序代码中定义的函数、变量等加以分类,分别放置在可执行文件的代码段、数据段和BSS段中。程序中定义的一些字符串、printf函数打印的字符串常量则放置在只读数据段.rodata中。如果程序在编译时设置为debug模式,则可执行文件中还会有一个专门的.debug section,用来保存可执行文件中每一条二进制指令对应的源码位置信息。根据这些信息,GDB调试器就可以支持源码级的单步调试,否则你单步执行的都是二进制指令,可读性不高,不方便调试。在最后环节,编译器还会在可执行文件中添加一些其他section,如.init section,这些代码来自C语言运行库的一些汇编代码,用来初始化C程序运行所依赖的环境,如内存堆栈的初始化等。
2.2 readelf常见用法详解
readelf是GNU Binutils包中的一个工具(通常各个linux发行版中都会默认包含这个工具包),用于展示ELF文件的结构信息,包括段头表、节头表、符号表等。实际上,不必大费周章的搜它的用法,readelf -H
指令将详细展示各个选项参数的作用。
Tips📌:除了-v和-H之外,其它的选项必须有一个指定参数(即选项后面要加一个待读取信息的文件),-v用来显示readelf的版本信息。
我们这里将列举最常用的几个参数,如下所示:
如果对程序的编译链接过程不是很清楚的,这里可以先简单认识下前两个选项-h
和-S
即可,阅读《程序的编译、链接过程分析(简洁浓缩版)!》后,再回过头来看其他选项的作用会容易理解一些,不然可能还是不清楚每个选项罗列出来的信息具体有什么用。
-
选项
-h
(elf header)
主要用来获取可执行文件的头部信息,主要包括可执行文件运行的平台、软件版本、程序入口地址, 以及programheaders、section header等信息。通过文件的头部信息,我们可以知道在a.out可执行文件里一共有多少个section headers。
-
选项
-S
(section headers)大写的S
通过上面-h选项我们看到a.out中总共有29个section header,将这些section header集中放到一起,就是section header table-节头表。可以使用readelf-S命令来查看一个可执行文件的节头表。section header table里的各个section header用来描述各个section的名称、类型、起始地址、大小等信息。
-
选项
-l
(program headers)
用于显示ELF文件中的程序段(segments)信息。这些信息包括每个段的类型(如LOAD, DYNAMIC等)、地址、偏移、大小等,这对于理解程序如何被操作系统加载到内存中非常有用。建议先阅读《编译链接(待更新)》
-
选项
-s
(symbols)
可以列出ELF文件中的所有符号,包括函数、变量等。这些符号信息包括它们的名称、位置、大小、类型(如函数、对象等),对于调试程序和理解程序结构非常重要。
westen@westen-ubuntu:/mnt/hgfs/code/build$ readelf -s a.out
Symbol table '.dynsym' contains 5 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 00000000 0 NOTYPE LOCAL DEFAULT UND
1: 00000000 0 NOTYPE WEAK DEFAULT UND __gmon_start__
2: 00000000 0 FUNC GLOBAL DEFAULT UND printf@GLIBC_2.4 (2)
3: 00000000 0 FUNC GLOBAL DEFAULT UND abort@GLIBC_2.4 (2)
4: 00000000 0 FUNC GLOBAL DEFAULT UND __libc_start_main@GLIBC_2.4 (2)
Symbol table '.symtab' contains 112 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 00000000 0 NOTYPE LOCAL DEFAULT UND
1: 00010154 0 SECTION LOCAL DEFAULT 1
2: 00010168 0 SECTION LOCAL DEFAULT 2
3: 00010188 0 SECTION LOCAL DEFAULT 3
4: 000101ac 0 SECTION LOCAL DEFAULT 4
5: 000101d8 0 SECTION LOCAL DEFAULT 5
6: 00010228 0 SECTION LOCAL DEFAULT 6
7: 0001026c 0 SECTION LOCAL DEFAULT 7
8: 00010278 0 SECTION LOCAL DEFAULT 8
9: 00010298 0 SECTION LOCAL DEFAULT 9
10: 000102a0 0 SECTION LOCAL DEFAULT 10
11: 000102c0 0 SECTION LOCAL DEFAULT 11
12: 000102cc 0 SECTION LOCAL DEFAULT 12
13: 00010310 0 SECTION LOCAL DEFAULT 13
14: 00010520 0 SECTION LOCAL DEFAULT 14
15: 00010528 0 SECTION LOCAL DEFAULT 15
16: 0001053c 0 SECTION LOCAL DEFAULT 16
17: 00010544 0 SECTION LOCAL DEFAULT 17
18: 00020f10 0 SECTION LOCAL DEFAULT 18
19: 00020f14 0 SECTION LOCAL DEFAULT 19
20: 00020f18 0 SECTION LOCAL DEFAULT 20
21: 00021000 0 SECTION LOCAL DEFAULT 21
22: 00021020 0 SECTION LOCAL DEFAULT 22
23: 00021030 0 SECTION LOCAL DEFAULT 23
24: 00000000 0 SECTION LOCAL DEFAULT 24
25: 00000000 0 SECTION LOCAL DEFAULT 25
26: 00000000 0 FILE LOCAL DEFAULT ABS /usr/lib/gcc-cross/arm-li
27: 00010168 0 NOTYPE LOCAL DEFAULT 2 $d
28: 00010310 0 NOTYPE LOCAL DEFAULT 13 $a
29: 0001053c 0 NOTYPE LOCAL DEFAULT 16 $d
30: 00010340 0 NOTYPE LOCAL DEFAULT 13 $d
31: 00010528 0 NOTYPE LOCAL DEFAULT 15 $d
32: 00021020 0 NOTYPE LOCAL DEFAULT 22 $d
33: 00000000 0 FILE LOCAL DEFAULT ABS /usr/lib/gcc-cross/arm-li
34: 0001034c 0 NOTYPE LOCAL DEFAULT 13 $a
35: 0001034c 0 FUNC LOCAL DEFAULT 13 call_weak_fn
36: 00010368 0 NOTYPE LOCAL DEFAULT 13 $d
37: 000102c0 0 NOTYPE LOCAL DEFAULT 11 $a
38: 00010520 0 NOTYPE LOCAL DEFAULT 14 $a
39: 00000000 0 FILE LOCAL DEFAULT ABS /usr/lib/gcc-cross/arm-li
40: 000102c8 0 NOTYPE LOCAL DEFAULT 11 $a
41: 00010524 0 NOTYPE LOCAL DEFAULT 14 $a
42: 00000000 0 FILE LOCAL DEFAULT ABS crtstuff.c
43: 00010370 0 NOTYPE LOCAL DEFAULT 13 $a
44: 00010370 0 FUNC LOCAL DEFAULT 13 deregister_tm_clones
45: 00010390 0 NOTYPE LOCAL DEFAULT 13 $d
46: 0001039c 0 NOTYPE LOCAL DEFAULT 13 $a
47: 0001039c 0 FUNC LOCAL DEFAULT 13 register_tm_clones
48: 000103c8 0 NOTYPE LOCAL DEFAULT 13 $d
49: 00021024 0 NOTYPE LOCAL DEFAULT 22 $d
50: 000103d4 0 NOTYPE LOCAL DEFAULT 13 $a
51: 000103d4 0 FUNC LOCAL DEFAULT 13 __do_global_dtors_aux
52: 000103f8 0 NOTYPE LOCAL DEFAULT 13 $d
53: 00021030 1 OBJECT LOCAL DEFAULT 23 completed.9929
54: 00020f14 0 NOTYPE LOCAL DEFAULT 19 $d
55: 00020f14 0 OBJECT LOCAL DEFAULT 19 __do_global_dtors_aux_fin
56: 000103fc 0 NOTYPE LOCAL DEFAULT 13 $a
57: 000103fc 0 FUNC LOCAL DEFAULT 13 frame_dummy
58: 00020f10 0 NOTYPE LOCAL DEFAULT 18 $d
59: 00020f10 0 OBJECT LOCAL DEFAULT 18 __frame_dummy_init_array_
60: 00021030 0 NOTYPE LOCAL DEFAULT 23 $d
61: 00000000 0 FILE LOCAL DEFAULT ABS main.c
62: 00021028 0 NOTYPE LOCAL DEFAULT 22 $d
63: 0001052c 0 NOTYPE LOCAL DEFAULT 15 $d
64: 00010400 0 NOTYPE LOCAL DEFAULT 13 $a
65: 00010454 0 NOTYPE LOCAL DEFAULT 13 $d
66: 00021034 4 OBJECT LOCAL DEFAULT 23 uninit_local_val.4621
67: 00021034 0 NOTYPE LOCAL DEFAULT 23 $d
68: 0002102c 4 OBJECT LOCAL DEFAULT 22 local_val.4620
69: 00000000 0 FILE LOCAL DEFAULT ABS sub.c
70: 0001045c 0 NOTYPE LOCAL DEFAULT 13 $a
71: 00000000 0 FILE LOCAL DEFAULT ABS elf-init.oS
72: 000104bc 0 NOTYPE LOCAL DEFAULT 13 $a
73: 00010514 0 NOTYPE LOCAL DEFAULT 13 $d
74: 0001051c 0 NOTYPE LOCAL DEFAULT 13 $a
75: 00000000 0 FILE LOCAL DEFAULT ABS crtstuff.c
76: 00010544 0 NOTYPE LOCAL DEFAULT 17 $d
77: 00010544 0 OBJECT LOCAL DEFAULT 17 __FRAME_END__
78: 00000000 0 FILE LOCAL DEFAULT ABS
79: 00020f14 0 NOTYPE LOCAL DEFAULT 18 __init_array_end
80: 00020f18 0 OBJECT LOCAL DEFAULT 20 _DYNAMIC
81: 00020f10 0 NOTYPE LOCAL DEFAULT 18 __init_array_start
82: 00021000 0 OBJECT LOCAL DEFAULT 21 _GLOBAL_OFFSET_TABLE_
83: 000102cc 0 NOTYPE LOCAL DEFAULT 12 $a
84: 000102dc 0 NOTYPE LOCAL DEFAULT 12 $d
85: 000102e0 0 NOTYPE LOCAL DEFAULT 12 $a
86: 0001051c 4 FUNC GLOBAL DEFAULT 13 __libc_csu_fini
87: 00021038 4 OBJECT GLOBAL DEFAULT 23 uninit_val
88: 00021020 0 NOTYPE WEAK DEFAULT 22 data_start
89: 0001045c 48 FUNC GLOBAL DEFAULT 13 add
90: 00000000 0 FUNC GLOBAL DEFAULT UND printf@@GLIBC_2.4
91: 00021030 0 NOTYPE GLOBAL DEFAULT 23 __bss_start__
92: 0002103c 0 NOTYPE GLOBAL DEFAULT 23 _bss_end__
93: 00021030 0 NOTYPE GLOBAL DEFAULT 22 _edata
94: 00010520 0 FUNC GLOBAL DEFAULT 14 _fini
95: 0002103c 0 NOTYPE GLOBAL DEFAULT 23 __bss_end__
96: 00021028 4 OBJECT GLOBAL DEFAULT 22 global_val
97: 00021020 0 NOTYPE GLOBAL DEFAULT 22 __data_start
98: 00000000 0 FUNC GLOBAL DEFAULT UND __libc_start_main@@GLIBC_
99: 00000000 0 NOTYPE WEAK DEFAULT UND __gmon_start__
100: 00021024 0 OBJECT GLOBAL HIDDEN 22 __dso_handle
101: 00010528 4 OBJECT GLOBAL DEFAULT 15 _IO_stdin_used
102: 000104bc 96 FUNC GLOBAL DEFAULT 13 __libc_csu_init
103: 0002103c 0 NOTYPE GLOBAL DEFAULT 23 _end
104: 00010310 0 FUNC GLOBAL DEFAULT 13 _start
105: 0002103c 0 NOTYPE GLOBAL DEFAULT 23 __end__
106: 00021030 0 NOTYPE GLOBAL DEFAULT 23 __bss_start
107: 00010400 92 FUNC GLOBAL DEFAULT 13 main
108: 00021030 0 OBJECT GLOBAL HIDDEN 22 __TMC_END__
109: 0001048c 48 FUNC GLOBAL DEFAULT 13 sub
110: 00000000 0 FUNC GLOBAL DEFAULT UND abort@@GLIBC_2.4
111: 000102c0 0 FUNC GLOBAL DEFAULT 11 _init
-
选项
-r
(relocs)
用于显示ELF文件中的重定位表信息。重定位表包含了程序在运行时需要修改的地址信息,这对于理解动态链接和地址修正过程至关重要。
-
选项
-d
(dynamic)
可以查看ELF文件的动态段信息,这对于动态链接的ELF文件(如共享库)特别有用。它显示了动态链接器需要的信息,如所需的共享库、动态符号表等。
-
选项
-A
(arch-specific)
显示CPU构架信息,这个其实也是不常用的,偶尔用来看一下enum类型。
-
选项
-x, hex-dump=<number or name>
以16进制方式显示指定段内内容,通过-x选项后跟节(section)名称或者索引号,可以将指定节的内容以十六进制形式转储出来。这对于直接查看节中的原始数据非常有用,特别是在分析程序行为或调试时。
再次提醒,如果对程序的编译链接过程不是很清楚的,建议阅读《程序的编译、链接过程分析(简洁浓缩版)!》后,再回过头来看这些选项的作用,不然可能还是不清楚每个选项罗列出来的信息具体有什么用。
>>>>>>>>> 返回专栏总目录 《嵌入式工程师自我修养/C语言》<<<<<<<<<