2023年12月2日发(作者:)

关于嵌入式的bin、hex、axf、map别在注释里陷得太深——注释很可能会误导你,你要调试的只是代码。前言个人博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 nixgnauhcuy's blog!如需转载,请标明出处!记录工作中学习到的知识,在这里做些笔记,方便自己后面温习。bin、hex、axfbin、hex、axf 之间的关系bin数据hex数据地址axf数据地址调试信息所以同一工程中 bin、hex、axf 的文件大小为

.bin < .hex < .axf假设bin文件是一个三无产品,那么hex就是一个带有信息的产品,而axf文件则是带有信息并且附了一张使用说明的产品。(我也不知道这样举例合不合理,意思到位就行)因为bin文件没有地址信息,而hex文件带了地址信息,所以实际上我们使用烧录软件 () 是这样的:导入.bin时,因为没有地址信息,所以我们需要为它指定烧写的起始地址image导入.hex时,因为包含了地址信息,所以我们不需要指定起始地址,烧录工具会自动读取要烧录的地址imagebin 文件上面已经说了 bin 文件起始不包含地址信息,所以 bin 文件只是单纯的二进制文件,是没有格式的程序文件,只是包含了程序数据。我们看到烧录到单片机的是 .hex 文件,但是实际上,烧录软件会帮我们将 hex 文件的地址解析提取,最后还是烧录的 .bin 文件下面这张图可以看出,J-Flash 解析 地址信息后,实际上要烧录的还是 的内容:imagehex 文件Hex 是由 Intel 制定的一种十六进制标准文件格式,是由编译器转换而成的一种用于下载到处理器里面的文件。Hex 文件格式是由一行一行的十六进制数据组成,每行包含:开始、长度、数据、类型、校验和等重要信息。一般 Hex 文件的记录格式如下:Record mark冒号:Lengthbyte(1)数据长度Load offsetbyte(2~3)起始地址Record typebyte(4)数据类型Info or Databyte(5~n)数据Chksumbyte(n+1)校验和Record mark:标记头(数据头)Length:表示本行的数据长度(Info or Data)Load offset:表示本行数据的起始地址Record type:数据类型,共分:0x00 - Data Rrecord,数据记录0x01 - End of FileRecord,用来标识文件结束,放在文件的最后,标识HEX文件的结尾0x02 - Extended Segment Address Record,用来标识扩展段地址的记录,扩展段地址记录(HEX86),它包含4~19位数据地址段。由于普通的 Intel 的 HEX 记录文件只能记录 64K 的地址范围,所以大于 64K 的地址数据要靠扩展段地址记录0x03 - Start Segment Address Record,开始段地址记录0x04 - Extended Linear Address Record,用来标识扩展线性地址的记录,扩展线性地址记录也叫 32 位地址记录或者 HEX386 记录,这些记录包含了数据在存储器里真实地址的高 16 位。 当一个扩展线性地址记录被读取后,将一直保持有效,直到它被另一个扩展地址记录改变。因为它记录的是后面数据在存储器里存放的真实起始地址,所以它的起始地址偏移量 (Load offset) 总是 00000x05 - Start Linear Address Record,开始线性地址记录Info or Data:数据代表一个字节的数据,一个记录可以有许多数据字节,数据字节数量应等于 LengthChksum:一个字节,先将此字节前所有字节相加得到 sum,校验和=(0x100-sum & 0xFF) & 0xFF说了这么多,还是来实际验证一下,这里我贴出了的前3行和后3行,中间的其他内容省略::F8:(略n行):0CA2F000FFBF915AF0051D:620192:00000001FF:02|0000|04|0002|F8(头):02 - 本行数据长度为 20000 - 本行数据起始地址偏移为 0x000004 - 数据类型是标识扩展线性地址的记录0002 - 本行 2 个数据为0x00和0x02,(这里 0x0002<<16=0x00020000,为基地址)F8 - 校验和=0x100-(0x02+0x00+0x00+0x04+0x00+0x02)&0xFF=0xF8:10|6000|00|70F60320A1640200A96402008D640200|FE10 - 本行数据长度为166000 - 本行数据起始地址偏移为0x6000,所以这里记录的地址是0x000020000+0x6000=0x00026000(这个可以看上面J-Flash,的起始地址刚好是0x26000)00 - 数据类型是标识扩展线性地址的记录70F60320A1640200A96402008D640200 - 本行16个数据为0x02,0x00FE - 校验和=0x100-(0x10++0x02+0x00)&0xFF=0xFE:00|0000|01|FF(尾):00 - 本行数据长度为00000 - 本行数据起始地址偏移为0x000001 - 数据类型是标识文件结束FF - 校验和=0x100-(0x00+0x00+0x00+0x01)&0xFF=0xFF从上面这些可以验证出,hex 文件确实保存了 bin 文件的地址信息实际上 hex 文件会大于 2 倍的 bin 文件大小的,bin 文件一个 byte 在 hex 文件中用 Ascill 编码则需要用两个字符来表示一个字节,而且 hex又包括了其他信息,所以一般 hex > 2bin。imageaxf 文件axf,全称 ARM Executable File,它是由 ARM 编译器产生,除了包含 bin 的内容之外,还附加其他调试信息,这些调试信息加在可执行的二进制数据之前。调试时这些调试信息不会下载到 RAM 中,真正下载到 RAM 中的信息仅仅是可执行代码。调试信息作用:1. 可将源代码包括注释夹在反汇编代码中,这样我们可随时切换到源代码中进行调试2. 我们还可以对程序中的函数调用情况进行跟踪(Keil可以通过Watch & Call Stack Window查看)3. 对变量进行跟踪(Keil可以通过Watch & Call Stack Window查看)axf 文件转 bin 文件fromelf 格式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 (生成Plain Binary格式的文件) --m32 Motorola 32 bit Hex (生成Motorola 32位十六进制格式的文件) --i32 Intel 32 bit Hex (生成Intel 32位十六进制格式的文件) --vhx Byte Oriented Hex format (面向字节的位十六进制格式的文件t) --base addr Optionally set base address for m32,i32 (设置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 (打印代码和数据大小的信息)将 axf 文件转 bin 文件在 Keil 的安装目录下,我的装在了 C 盘,在

C:Keil_v5ARMARMCCbin 中,可以找到

这个可执行文件image使用命令行工具,例如 win10 自带的或者 git 等,这里考虑大家不一定有其他命令行工具,所以直接用 win10 自带的命令行。win+R 打开运行,输入

cmd 打开运行,进入

路径image这里我将要转化的文件放在

F 盘 test 文件夹中,image命令格式为:[文件路径] --bin -o [BIN路径] [AXF文件路径]命令行输入: --bin -o F:testtest_bin_ F:可以看到在输出目录 F:test 中,将 axf 文件转化生成为了 bin 文件,并且输出的 bin 文件和 keil 生成 bin 文件大小一致,说明是相同的文件imagehex 文件和bin文件相互转换srecord 软件在找软件时,在Keil官网看到 BINARY to Motorola S-Record Converter Utilityimage翻译一下: srec_ 应用程序是 HEX2BIN,BIN2HEX,BIN2MOT 和 MOT2BIN 的绝佳替代品,用途更广泛。该工具是在 上托管的 SRecord 项目的一部分。您可以从 这里 下载。后面我就下载下来研究了一下,发现可以替代 hex2bin 和 bin2hex,所以直接拿来使用。hex 转 bin命令:srec_ *.hex -intel -offset -0x00000 -o *.bin -binary*.hex指定要转换的hex文件*.bin指定要输出的文件名-offset -0x00000这个指定偏移要根据工程指定来image这里我的偏移是0x26000,所以我输入的命令是:srec_ -intel -offset -0x26000 -o test_ -binary上面转 axf 时,已经有了 test 文件,这里我把 srec_ 放在了 F:/test1/srecord 中,并且把要转换的 文件放在了 srecord文件夹中imageimage可以看到生成的 test_ 和 keil 生成的 文件大小一致。bin 转 hex参考Sorting Intel HEX Files,这里我的命令是:srec_ test_ -Binary -o test_hex_ -intel -Output-Block-Size=16 -Disable_Sequence_Warningsimage这里实际上和 keil 生的 hex 文件有稍稍区别,因为我试了多种方式,都没有办法和 Keil 生成文件一样,虽然 binw 文件存储的数据一样,但是crc 及偏移还是稍微有区别,图左边是生成的hex文件,图右边是原先的hex文件imagemapmap 文件map文件是我们通过编译器编译生成的映射文件,map文件包含了五个部分:A. Section Cross References(模块、段的交叉引用关系)B. Removing Unused input sections from the image(移除未使用的段)C. Image Symbol Table(映射符号表,列出了各个段所存储的对应地址)D. Memory Map of the image(内存(映射)分布)E. Image component sizes(映像组成大小)这里也提一下关于map的基本概念section(段):描述映像文件的代码和数据块RO:Read-Only的缩写,包括RO-data(只读数据)和RO-code(代码)RW:Read-Write的缩写,主要是RW-data,Rw-data由程序初始化初始值ZI:Zero-initialized的缩写,主要是ZI-data,由编程器初始化为:与RO-code同义.constdata:与RO-data同义.bss:与ZI-data同义,通常是指存放未初始化的全局变量的区域.data:与RW-data同义在Keil中,我们在

Project -> Options for Target -> Listing 中可以看到我们生成的map中链接的内容,image它包含了:Memory Map:内存映射Callgraph:图像映射Symbols:符号Cross Reference:交叉引用Size Info:大小信息Totals Info:统计信息Unused Section Info:未调用模块(段)信息Veneers Info:装饰信息Section Cross References模块、段的交叉引用关系:这里主要是各个源文件之间生成的模块、段之间相互引用的关系。这里我随便找了个简单的LED延时闪灯程序,编译后打开生成的 map 文件imageimage这里指出了 test.c 中模块调用其他模块之间的关系:test.o() refers to sys.o(32_Clock_Init) for Stm32_Clock_Inittest.o() refers to delay.o(_init) for delay_inittest.o() refers to led.o(_Init) for LED_Inittest.o() refers to delay.o(_ms) for delay_ms因为我的 main 写在 test.c 中,所以 test.c 中因为 main 函数调用了 sys.c 中的 Stm32_Clock_Init 函数、调用了 delay.c 中的 delay_init函数和 delay_ms 函数、调用了 led.c 的 LED_Init 函数,所以, 生成的 map 中 Section Cross References 指出了之前的相互引用关系。Removing Unused input sections from the image移除未使用的段:这个是我们工程中没有被调用的模块,会被编译器移除并标识出来。image图里列出了被移除的模块信息,例如Removing delay.o(_us), (60 bytes).Removing usart.o(_init), (220 bytes).13 unused section(s) (total 786 bytes) removed from the image.从上面 main 函数的执行中,可以看出我没有调用 delay_us 和 uart_init 串口的初始化函数,所以编译器把他们找出来并移除,共 13 个段没有被调用,大小为 786 字节。Image Symbol Table映射符号表,列出了各个段所存储的对应地址Image Symbol Table Local Symbols Symbol Name Value Ov Type Size Object(Section) ../clib/angel/boardlib.s 0x00000000 Number 0 boardinit3.o ABSOLUTE .... ..ProjectLEDled.c 0x00000000 Number 0 led.o ABSOLUTE ..SYSTEMdelaydelay.c 0x00000000 Number 0 delay.o ABSOLUTE ..SYSTEMsyssys.c 0x00000000 Number 0 sys.o ABSOLUTE ..SYSTEMusartusart.c 0x00000000 Number 0 usart.o ABSOLUTE ..SYSTEMsyssys.c 0x00000000 Number 0 sys.o ABSOLUTE .... RESET 0x08000000 Section 304 startup_stm32f10x_hd.o(RESET) main 0x08000130 Section 8 __main.o(main) .... fac_us 0x20000000 Data 1 delay.o(.data) fac_ms 0x20000002 Data 2 delay.o(.data) Global Symbols Symbol Name Value Ov Type Size Object(Section) .... .... delay_init 0x08000455 Thumb Code 56 delay.o(_init) delay_ms 0x08000495 Thumb Code 56 delay.o(_ms) main 0x080004d1 Thumb Code 114 test.o() LED_Init 0x08000269 Thumb Code 74 led.o(_Init) USART_RX_STA 0x20000008 Data 2 usart.o(.data) USART_RX_BUF 0x2000000c Data 200 usart.o(.bss)这里我截取了一些 map 文件中 Image Symbol Table 的内容出来分析Symbol 分为两类Local Symbols(局部)局部就是在函数内部用 static 声明的变量,还有用 static 声明的函数,基本上都是属于局部,汇编文件里面的变量如果作用域是本文件的就是局部。Global Symbols(全局)全局就是不是用 static 声明的变量和函数,是 auto 声明的全局变量和 C 文件函数就属于全局。汇编文件里面作用域是全工程的就是全局。Symbol 符号名称Symbol Name:符号名Value:存储对应的地址0x0800xxxx:指存储在 flash 中的代码和变量等0x2000xxxx:指存储在 RAM 中的变量 Data 等Ov Type:符号类型Number、Section、Thumb Code、Data等Size:大小,指当前Symbol占用的大小Object(Section):段目标,指当前段所在的模块及源文件Memory Map of the image内存(映射)分布:映像文件可以分为加载域(Load Region)和运行域(Execution Region):加载域反映了ARM可执行映像文件的各个段存放在存储器中的位置关系Memory Map of the image Image Entry point : 0x08000131 Load Region LR_IROM1 (Base: 0x08000000, Size: 0x00000578, Max: 0x00080000, ABSOLUTE) Execution Region ER_IROM1 (Base: 0x08000000, Size: 0x0000056c, Max: 0x00080000, ABSOLUTE) Base Addr Size Type Attr Idx E Section Name Object 0x08000000 0x00000130 Data RO 3 RESET startup_stm32f10x_hd.o 0x08000130 0x00000008 Code RO 213 * main c_w.l(__main.o) .... ....

Execution Region RW_IRAM1 (Base: 0x20000000, Size: 0x00000538, Max: 0x00010000, ABSOLUTE) Base Addr Size Type Attr Idx E Section Name Object 0x20000000 0x00000004 Data RW 49 .data delay.o 0x20000004 0x00000006 Data RW 159 .data usart.o .... ....Image Entry point : 0x08000131:指映射入口地址。Load Region LR_IROM1 (Base: 0x08000000, Size: 0x00000578, Max: 0x00080000, ABSOLUTE):指加载域LR_IROM1 起始地址为 0x08000000,大小是 0x00000578,加载域最大大小为 0x00080000Execution Region ER_IROM1 (Base: 0x08000000, Size: 0x0000056c, Max: 0x00080000, ABSOLUTE)Execution Region RW_IRAM1 (Base: 0x20000000, Size: 0x00000538, Max: 0x00010000, ABSOLUTE)运行域 ER_IROM1 起始地址 0x08000000,大小是 0x0000056c,加载域最大大小为 0x00080000运行域 RW_IRAM1 起始地址 0x20000000,大小是 0x00000538,加载域最大大小为 0x00010000对应Keil中设定的IROM1和IRAM1:imageImage component sizes映像组成大小:各个映像模块在各个文件中的代码大小,主要就是对模块进行汇总存储大小信息Image component sizes Code (inc. data) RO Data RW Data ZI Data Debug Object Name 124 12 0 4 0 1196 delay.o 84 10 0 0 0 435 led.o .... ....============================================================================== Code (inc. data) RO Data RW Data ZI Data Debug

1052 108 336 12 1324 215049 Grand Totals 1052 108 336 12 1324 215049 ELF Image Totals 1052 108 336 12 0 0 ROM Totals============================================================================== Total RO Size (Code + RO Data) 1388 ( 1.36kB) Total RW Size (RW Data + ZI Data) 1336 ( 1.30kB) Total ROM Size (Code + RO Data + RW Data) 1400 ( 1.37kB)==============================================================================Code指代码的大小RO Data指除了内联数据之外的常量数据RW Data指可读写、已初始化的变量数据ZI Data指未初始化的变量数据Debug显示调试数据占用了多少字节ObjectName目标名Total RW Size (RW Data + ZI Data) 1336 ( 1.30kB):是我们程序RAM所占的字节总数,Total ROM Size (Code + RO Data + RW Data) 1400 ( 1.37kB):是我们程序ROM所占的字节总数,也就是我们程序所下载到ROM Flash中的大小。结写的可能有点乱,因为我晚上才有空整理这些,上面这些是花了好几个晚上才整理出来,所以有点乱也希望大家理解。有什么错误也希望能在评论区指出,我会及时修改的!