关于嵌入式的bin、hex、axf、map

别在注释里陷得太深 —— 注释很可能会误导你,你要调试的只是代码。

前言

个人博客

所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 nixgnauhcuy’s blog

如需转载,请标明出处!

记录工作中学习到的知识,在这里做些笔记,方便自己后面温习。


bin、hex、axf

bin、hex、axf 之间的关系

binhexaxf
数据数据数据
地址地址
调试信息

所以同一工程中 bin、hex、axf 的文件大小为 .bin < .hex < .axf

假设 bin 文件是一个三无产品,那么 hex 就是一个带有信息的产品,而 axf 文件则是带有信息并且附了一张使用说明的产品。(我也不知道这样举例合不合理,意思到位就行)

因为 bin 文件没有地址信息,而 hex 文件带了地址信息,所以实际上我们使用烧录软件 (J-Flash.exe) 是这样的:

导入 .bin 时,因为没有地址信息,所以我们需要为它指定烧写的起始地址

导入 .hex 时,因为包含了地址信息,所以我们不需要指定起始地址,烧录工具会自动读取要烧录的地址


bin 文件

上面已经说了 bin 文件起始不包含地址信息,所以 bin 文件只是单纯的二进制文件,是没有格式的程序文件,只是包含了程序数据。我们看到烧录到单片机的是 .hex 文件,但是实际上,烧录软件会帮我们将 hex 文件的地址解析提取,最后还是烧录的 .bin 文件

下面这张图可以看出,J-Flash 解析 test.hex 地址信息后,实际上要烧录的还是 test.bin 的内容:


hex 文件

Hex 是由 Intel 制定的一种十六进制标准文件格式,是由编译器转换而成的一种用于下载到处理器里面的文件。

Hex 文件格式是由一行一行的十六进制数据组成,每行包含:开始、长度、数据、类型、校验和等重要信息。

一般 Hex 文件的记录格式如下:

Record markLengthLoad offsetRecord typeInfo or DataChksum
冒号 byte(1)byte(2~3)byte(4)byte(5~n)byte(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) 总是 0000
0x05 - Start Linear Address Record,开始线性地址记录

  • Info or Data:数据代表一个字节的数据,一个记录可以有许多数据字节,数据字节数量应等于 Length
  • Chksum:一个字节,先将此字节前所有字节相加得到 sum,校验和 =(0x100-sum & 0xFF) & 0xFF

说了这么多,还是来实际验证一下,这里我贴出了 test.hex 的前 3 行和后 3 行,中间的其他内容省略:

:020000040002F8
:1060000070F60320A1640200A96402008D640200FE
:10601000AD640200AF640200B16402000000000041
...(略n行)
:0CA2F000FFBF915A010401789495F0051D
:040000050002620192
:00000001FF

:02|0000|04|0002|F8(头):

  • 02 - 本行数据长度为 2
  • 0000 - 本行数据起始地址偏移为 0x0000
  • 04 - 数据类型是标识扩展线性地址的记录
  • 0002 - 本行 2 个数据为 0x00 和 0x02,(这里 0x0002<<16=0x00020000,为基地址)
  • F8 - 校验和 = 0x100-(0x02+0x00+0x00+0x04+0x00+0x02)&0xFF=0xF8

:10|6000|00|70F60320A1640200A96402008D640200|FE

  • 10 - 本行数据长度为 16
  • 6000 - 本行数据起始地址偏移为 0x6000,所以这里记录的地址是 0x000020000+0x6000=0x00026000 (这个可以看上面 J-Flash,test.hex 的起始地址刚好是 0x26000)
  • 00 - 数据类型是标识扩展线性地址的记录
  • 70F60320A1640200A96402008D640200 - 本行 16 个数据为 0x70,0xF6…0x02,0x00
  • FE - 校验和 = 0x100-(0x10+0x60+0x00…+0x02+0x00)&0xFF=0xFE

:00|0000|01|FF(尾):

  • 00 - 本行数据长度为 0
  • 0000 - 本行数据起始地址偏移为 0x0000
  • 01 - 数据类型是标识文件结束
  • FF - 校验和 = 0x100-(0x00+0x00+0x00+0x01)&0xFF=0xFF

从上面这些可以验证出,hex 文件确实保存了 bin 文件的地址信息

实际上 hex 文件会大于 2 倍的 bin 文件大小的,bin 文件一个 byte 在 hex 文件中用 Ascill 编码则需要用两个字符来表示一个字节,而且 hex 又包括了其他信息,所以一般 hex > 2bin


axf 文件

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_v5\ARM\ARMCC\bin 中,可以找到 fromelf.exe 这个可执行文件

使用命令行工具,例如 win10 自带的或者 git 等,这里考虑大家不一定有其他命令行工具,所以直接用 win10 自带的命令行。

win+R 打开运行,输入 cmd 打开运行,进入 fromelf.exe 路径

这里我将要转化的文件放在 F 盘 test 文件夹中

命令格式为:

[fromelf.exe文件路径] --bin -o [BIN路径] [AXF文件路径]

命令行输入:

fromelf.exe --bin -o F:\test\test_bin_out.bin F:\test\test.axf

可以看到在输出目录 F:\test 中,将 axf 文件转化生成为了 bin 文件,并且输出的 bin 文件和 keil 生成 bin 文件大小一致,说明是相同的文件

hex 文件和bin文件相互转换

srecord 软件

在找软件时,在Keil官网看到 BINARY to Motorola S-Record Converter Utility

翻译一下: srec_cat.exe 应用程序是 HEX2BIN,BIN2HEX,BIN2MOT 和 MOT2BIN 的绝佳替代品,用途更广泛。该工具是在 sourceforge.net 上托管的 SRecord 项目的一部分。您可以从 https://sourceforge.net/projects/srecord/files/srecord-win32 下载。

后面我就下载下来研究了一下,发现可以替代 hex2bin 和 bin2hex,所以直接拿来使用。

hex 转 bin

命令:

srec_cat.exe *.hex -intel -offset -0x00000 -o *.bin -binary

*.hex 指定要转换的 hex 文件
*.bin 指定要输出的文件名
-offset -0x00000 这个指定偏移要根据工程指定来

这里我的偏移是 0x26000,所以我输入的命令是:

srec_cat.exe test.hex -intel -offset -0x26000 -o test_out.bin -binary

上面转 axf 时,已经有了 test 文件,这里我把 srec_cat.exe 放在了 F:/test1/srecord 中,并且把要转换的 test.hex 文件放在了 srecord 文件夹中

可以看到生成的 test_out.bin 和 keil 生成的 test.bin 文件大小一致。

bin 转 hex

参考Sorting Intel HEX Files,这里我的命令是:

srec_cat.exe test_out.bin -Binary -o test_hex_out.hex -intel -Output-Block-Size=16 -Disable_Sequence_Warnings

这里实际上和 keil 生的 hex 文件有稍稍区别,因为我试了多种方式,都没有办法和 Keil 生成文件一样,虽然 binw 文件存储的数据一样,但是 crc 及偏移还是稍微有区别,

图左边是生成的hex文件,图右边是原先的hex文件

map

map 文件

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,由编程器初始化为 0
.text:与 RO-code 同义
.constdata:与 RO-data 同义
.bss:与 ZI-data 同义,通常是指存放未初始化的全局变量的区域
.data:与 RW-data 同义

在Keil中,我们在 Project -> Options for Target -> Listing 中可以看到我们生成的map中链接的内容,

它包含了:

  • Memory Map:内存映射
  • Callgraph:图像映射
  • Symbols:符号
  • Cross Reference:交叉引用
  • Size Info:大小信息
  • Totals Info:统计信息
  • Unused Section Info:未调用模块 (段) 信息
  • Veneers Info:装饰信息

Section Cross References

模块、段的交叉引用关系:这里主要是各个源文件之间生成的模块、段之间相互引用的关系。

这里我随便找了个简单的 LED 延时闪灯程序,编译后打开生成的 map 文件

这里指出了 test.c 中模块调用其他模块之间的关系:

test.o(i.main) refers to sys.o(i.Stm32_Clock_Init) for Stm32_Clock_Init
test.o(i.main) refers to delay.o(i.delay_init) for delay_init
test.o(i.main) refers to led.o(i.LED_Init) for LED_Init
test.o(i.main) refers to delay.o(i.delay_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

移除未使用的段:这个是我们工程中没有被调用的模块,会被编译器移除并标识出来。

图里列出了被移除的模块信息,例如

Removing delay.o(i.delay_us), (60 bytes).
Removing usart.o(i.uart_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
    ....
    ..\Project\LED\led.c                     0x00000000   Number         0  led.o ABSOLUTE
    ..\SYSTEM\delay\delay.c                  0x00000000   Number         0  delay.o ABSOLUTE
    ..\SYSTEM\sys\sys.c                      0x00000000   Number         0  sys.o ABSOLUTE
    ..\SYSTEM\usart\usart.c                  0x00000000   Number         0  usart.o ABSOLUTE
    ..\\SYSTEM\\sys\\sys.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(i.delay_init)
    delay_ms                                 0x08000495   Thumb Code    56  delay.o(i.delay_ms)
    main                                     0x080004d1   Thumb Code   114  test.o(i.main)
    LED_Init                                 0x08000269   Thumb Code    74  led.o(i.LED_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, 加载域最大大小为 0x00080000

Execution 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:

Image 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)

==============================================================================
CodeRO DataRW DataZI DataDebugObject Name
指代码的大小指除了内联数据之外的常量数据指可读写、已初始化的变量数据指未初始化的变量数据显示调试数据占用了多少字节目标名

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 中的大小。

写的可能有点乱,因为我晚上才有空整理这些,上面这些是花了好几个晚上才整理出来,所以有点乱也希望大家理解。有什么错误也希望能在评论区指出,我会及时修改的!

  • 5
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 10
    评论
bin hex s19互转软件是一款可以将二进制(bin)文件、十六进制(hex)文件和S19文件相互转换的工具。 首先,二进制文件是计算机中最基本的文件格之一,它由一系列的0和1组成,代表着计算机的机器指令和数据。而十六进制文件是将二进制文件中的每个字节表示为16进制数的文件格,更加便于阅读和编辑。而S19文件则是 Motorola 公司在其 8位和16位CPU上使用的一种ASCII码格的文件,也是常用的一种固件格。 这款软件可以实现以下功能: 1. binhex:可以将二进制文件转换为十六进制文件。用户只需选择要转换的二进制文件,软件会自动将文件内容转换为相应的十六进制表示方,并保存为一个新的hex文件。 2. hexbin:可以将十六进制文件转换为二进制文件。用户选择要转换的hex文件,软件将文件中的十六进制数转换为二进制形,并保存为一个新的bin文件。 3. s19转binhex:可以将S19文件转换为二进制文件或十六进制文件。用户选择要转换的S19文件,软件将解析文件中的ASCII码,将其转换为二进制或十六进制形,并保存为相应的文件格。 此外,该软件还可能具有其他功能,如对转换后的文件进行校验和计算、文件的合并和分割、批量转换等。 通过使用bin hex s19互转软件,用户可以轻松地在不同的文件格之间进行转换,便于读取、编辑和应用不同类型的固件。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值