草庐IT

hex文件格式详解

a只如初见 2023-05-25 原文

什么是hex文件

以*.hex为后缀的文件我们称之为HEX文件。hex是intel规定的标准,hex的全称是Intel HEX,此类文件通常用于传输将被存于ROM或EEPROM中的程序和数据。是由一行行符合Intel HEX文件格式的文本所构成的ASCII文本文件。
HEX的英语原始意思是16进制。这种文件格式主要用于保存单片机固件。
整个文件以行为单位,每行以冒号开头,内容全部为16进制码,2个ASCII码字符表示1个Hex字节

hex文件格式

先上图:这是我用Notepad++ 直接打开的一个32bit的单片机编译器编译的一个hex文件(中间省略了部分):

通过上面的文件,我们不难发现,hex文件每行都由一个冒号:作为起始码。
每行都符合以下图示的格式:
(注意:下图中一格代表一个ASC码,用一个字节表示)
起始码:每行数据作为一帧,并由:作为起始码;

字节长度:两个十六进制数字(一对十六进制数字),指示数据字段中的字节数(十六进制数字对)。最大字节数为255(0xFF)。16(0x10)和32(0x20)是常用的字节数;

地址:四个十六进制数字,代表数据的16位起始存储器地址偏移量。数据的物理地址是通过将此偏移量添加到先前建立的基地址来计算的,因此允许内存寻址超过16位地址的64 KB限制。基地址默认为零,可以通过各种类型的记录进行更改。基地址和地址偏移量始终表示为大端值。

指令类型:两个十六进制数字00到05,定义了这行数据的具体含义;

数据类型作用
‘00’ Data Rrecord用来记录数据,HEX文件的大部分记录都是数据记录
‘01’ End of File Record用来标识文件结束,放在文件的最后,标识HEX文件的结尾。数据字段为空(因此字节数为00),并且地址字段通常为 0000。
‘02’ Extended Segment Address Record用来标识扩展段地址的记录,数据字段包含一个16位的段基址(因此字节数始终为02)与80x86实模式寻址兼容。地址字段(通常为0000)被忽略。最近的段地址02记录乘以16,然后加到每个后续数据记录地址,以形成数据的物理起始地址。这允许寻址多达1 MB的地址空间。
‘03’ Start Segment Address Record开始段地址记录,对于80x86处理器,请指定CS:IP寄存器的初始内容(即起始执行地址)。地址字段是0000,字节数始终为04,前两个数据字节是CS值,后两个是IP值。
‘04’ Extended Linear Address Record用来标识扩展线性地址的记录,允许32位寻址(最大4GiB)。记录的地址字段将被忽略(通常是0000),其字节数始终为02。两个数据字节(大字节序)为所有后续类型指定32位绝对地址的高16位00记录; 这些高位地址位适用于下一个04记录。类型的绝对地址00 通过组合最近的高16位地址位形成记录 04 用低16位的地址记录 00记录。如果是类型00 记录之前没有任何类型 04 记录,然后其高16位地址位默认为0000。
‘05’ Start Linear Address Record开始线性地址记录,地址字段是 0000(未使用),字节数始终为04。四个数据字节代表一个32位地址值(big-endian)。对于80386和更高版本的CPU,此地址将加载到EIP寄存器中。

数据:n字节数据序列,由2个n十六进制数字表示;

校验码:(两个十六进制数字),可以用来验证记录没有错误的计算值;
注意校验和算法:累加和=校验码之前所有16进制,校验和=0x100-累加和。

如之前的hex文件图
第一行020000040804EE中,可以看做是0x02 0x00 0x00 0x04 0x08 0x04 0xEE,
02:代表本行有2个字节数据
0000:表示偏移地址或无用填0
04:扩展线性地址标识,表面后面2个字节数据是后面数据的基地址
注:由于每行标识数据地址的只有2Byte,所以最大只能到64K,为了可以保存高地址的数据,故有了扩展线性地址记录也叫作32位地址记录或HEX386记录.这些记录含数据的高16位,扩展线性地址记录总是有两个数据字节。
0804:是扩展地址 (0x0804 << 16) = 0x08040000后面的数据记录都以这个地址为基地址。
EE: 记录本行校验和 EE=0x100-(0x02+0x04+0x08+0x04)

第二行也按格式切分开为 10 0000 00 A8990020C1010408830B040839080408 DA
10:本行有0x10个Byte数据(即16个Byte数据)
0000:表示本行数据起始地址为0x08040000。下一行则是0x08040010依次类推
00:数据类型,表示后面字节为记录的数据
A8990020C1010408830B040839080408:16个byte的数据
DA: 校验和
00类型的行都大差不差,只是地址和数据不同而已,就不多赘述,以此类推即可。

倒数第二行也按格式切分开为 04 0000 05 080401AD 3D
04:表示本行有4个Byte数据
0000:无用填0
05:数据类型,表示开始线性地址记录
080401AD:表示要执行的下一个指令的地址(main函数地址)
3D:校验和

位置只有四个数据,但是 标注类型是 05, 即他是一个 “开始线性地址记录”。
说那么多,什么是开始线性地址记录呢?其实他就是函数入口地址
例如在该工程的 .map 文件中可以看到以下信息

Memory Map of the image

Image Entry point : 0x080401AD

但该行对于普通的MCU烧写程序来说没有什么作用,参考以下资料:
05 类型: Start Linear Address Record. The address field is 0000, the byte count is 04. The 4 data
bytes represent the 32-bit value loaded into the EIP register of the 80386 and higher CPU.

最后一行 00000001FF
00:代表本行有0个字节数据
0000:无用填0
01:标识hex文件结束
FF:校验和
注:每一个hex文件都以这个结尾。

hex文件和bin文件有什么不同

从几个方面来看待它们的不同。
1、格式的不同
HEX文件格式,看文章前一节已经详细讲解了。
BIN文件格式,对二进制文件而言,其实没有”格式”。文件只是包括了纯粹的二进制数据。
Bin文件是MCU固件烧写的最终形式,也就是说芯片ROM中烧写的内容完全就是Bin文件的内容。

2、使用的不同
HEX文件是包括地址信息的,而BIN文件格式只包括了数据本身。在烧写或下载HEX文件的时候,一般都不需要用户指定地址,因为HEX文件内部的信息已经包括了地址。而烧写BIN文件的时候,用户是一定需要指定烧录的起始地址信息的。

3、HEX文件和BIN文件大小的区别
HEX文件是用ASCII来表示二进制的数值。例如一般8BIT的二进制数值0x3F,用ASCII来表示就需要分别表示字符’3’和字符’F’,每个字符需要一个BYTE,所以对比同一个工程编译出来的HEX文件和BIN文件,HEX文件至少是BIN文件大小的两倍以上。


上面两张图分别以文本形式查看一个hex文件,以及使用十六进制形式查看该hex文件,可以更明显得看出:
hex文件是以ASCII码形式保存下来的。比如0x3a对应字符’:',0x30 0x32 0x30 0x30 对应字符0200,并且每行的末尾用了0D 0A进行回车换行。

对一个BIN文件而言,你查看文件的大小就可以知道文件包括的数据的实际大小。而对HEX文件而言,你看到的文件大小并不是实际的数据的大小。一是因为HEX文件是用ASCII来表示数据,二是因为HEX文件本身还包括别的附加信息。

4、Hex文件和Bin文件的存在价值
从上面的介绍中,我们发现Hex文件中每行的内容,有开始行,有结束行,并且每行还有校验码。我们得出hex文件的两个优点:

  1. 使用ASCII文本保存固件信息,方便查看一些固件内容;
  2. 通过文件每行的校验和与最后一行的文件结束标志,在文件的传输与保存过程中能够发现固件是否完整。

Hex文件有更好的可读性,最重要的是hex文件能够保证固件在保存与传输时的完整性。因此hex文件更适用于保存与传输。而Bin文件是纯二进制文件,内部只包含程序编译后的机器码和变量数据。当文件损坏时,我们也无法知道文件已损坏。不过Bin文件作为固件的最终形式,在使用串口下载程序或者远程升级时,是不可替代的,因为bin文件相对hex文件更小巧。

小知识

稍微高一些的版本的Notepad++才能在打开hex文件时有彩色区分数据段,如果你的Notepad++打开hex文件没有颜色,那么很可能是版本比较低。
通常打开hex文件后会自动识别为hex文件,查看语言可以看到是自动识别了Intel HEX(前面提过了,HEX全称是Intel HEX)

正常情况下,校验码都是正确的,校验码部分会显示绿色

如果有校验码出错,Notepad++会显示成黄色

有关hex文件格式详解的更多相关文章

  1. ruby - 使用 RubyZip 生成 ZIP 文件时设置压缩级别 - 2

    我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看ruby​​zip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d

  2. ruby - 其他文件中的 Rake 任务 - 2

    我试图在一个项目中使用rake,如果我把所有东西都放到Rakefile中,它会很大并且很难读取/找到东西,所以我试着将每个命名空间放在lib/rake中它自己的文件中,我添加了这个到我的rake文件的顶部:Dir['#{File.dirname(__FILE__)}/lib/rake/*.rake'].map{|f|requiref}它加载文件没问题,但没有任务。我现在只有一个.rake文件作为测试,名为“servers.rake”,它看起来像这样:namespace:serverdotask:testdoputs"test"endend所以当我运行rakeserver:testid时

  3. ruby-on-rails - 在 Rails 中将文件大小字符串转换为等效千字节 - 2

    我的目标是转换表单输入,例如“100兆字节”或“1GB”,并将其转换为我可以存储在数据库中的文件大小(以千字节为单位)。目前,我有这个:defquota_convert@regex=/([0-9]+)(.*)s/@sizes=%w{kilobytemegabytegigabyte}m=self.quota.match(@regex)if@sizes.include?m[2]eval("self.quota=#{m[1]}.#{m[2]}")endend这有效,但前提是输入是倍数(“gigabytes”,而不是“gigabyte”)并且由于使用了eval看起来疯狂不安全。所以,功能正常,

  4. ruby-on-rails - Rails 3 中的多个路由文件 - 2

    Rails2.3可以选择随时使用RouteSet#add_configuration_file添加更多路由。是否可以在Rails3项目中做同样的事情? 最佳答案 在config/application.rb中:config.paths.config.routes在Rails3.2(也可能是Rails3.1)中,使用:config.paths["config/routes"] 关于ruby-on-rails-Rails3中的多个路由文件,我们在StackOverflow上找到一个类似的问题

  5. ruby - 将差异补丁应用于字符串/文件 - 2

    对于具有离线功能的智能手机应用程序,我正在为Xml文件创建单向文本同步。我希望我的服务器将增量/差异(例如GNU差异补丁)发送到目标设备。这是计划:Time=0Server:hasversion_1ofXmlfile(~800kiB)Client:hasversion_1ofXmlfile(~800kiB)Time=1Server:hasversion_1andversion_2ofXmlfile(each~800kiB)computesdeltaoftheseversions(=patch)(~10kiB)sendspatchtoClient(~10kiBtransferred)Cl

  6. ruby - 使用 ruby​​ 将 HTML 转换为纯文本并维护结构/格式 - 2

    我想将html转换为纯文本。不过,我不想只删除标签,我想智能地保留尽可能多的格式。为插入换行符标签,检测段落并格式化它们等。输入非常简单,通常是格式良好的html(不是整个文档,只是一堆内容,通常没有anchor或图像)。我可以将几个正则表达式放在一起,让我达到80%,但我认为可能有一些现有的解决方案更智能。 最佳答案 首先,不要尝试为此使用正则表达式。很有可能你会想出一个脆弱/脆弱的解决方案,它会随着HTML的变化而崩溃,或者很难管理和维护。您可以使用Nokogiri快速解析HTML并提取文本:require'nokogiri'h

  7. ruby - 如何将脚本文件的末尾读取为数据文件(Perl 或任何其他语言) - 2

    我正在寻找执行以下操作的正确语法(在Perl、Shell或Ruby中):#variabletoaccessthedatalinesappendedasafileEND_OF_SCRIPT_MARKERrawdatastartshereanditcontinues. 最佳答案 Perl用__DATA__做这个:#!/usr/bin/perlusestrict;usewarnings;while(){print;}__DATA__Texttoprintgoeshere 关于ruby-如何将脚

  8. ruby - 使用 Vim Rails,您可以创建一个新的迁移文件并一次性打开它吗? - 2

    使用带有Rails插件的vim,您可以创建一个迁移文件,然后一次性打开该文件吗?textmate也可以这样吗? 最佳答案 你可以使用rails.vim然后做类似的事情::Rgeneratemigratonadd_foo_to_bar插件将打开迁移生成的文件,这正是您想要的。我不能代表textmate。 关于ruby-使用VimRails,您可以创建一个新的迁移文件并一次性打开它吗?,我们在StackOverflow上找到一个类似的问题: https://sta

  9. Ruby 写入和读取对象到文件 - 2

    好的,所以我的目标是轻松地将一些数据保存到磁盘以备后用。您如何简单地写入然后读取一个对象?所以如果我有一个简单的类classCattr_accessor:a,:bdefinitialize(a,b)@a,@b=a,bendend所以如果我从中非常快地制作一个objobj=C.new("foo","bar")#justgaveitsomerandomvalues然后我可以把它变成一个kindaidstring=obj.to_s#whichreturns""我终于可以将此字符串打印到文件或其他内容中。我的问题是,我该如何再次将这个id变回一个对象?我知道我可以自己挑选信息并制作一个接受该信

  10. ruby - 如何使用 Ruby aws/s3 Gem 生成安全 URL 以从 s3 下载文件 - 2

    我正在编写一个小脚本来定位aws存储桶中的特定文件,并创建一个临时验证的url以发送给同事。(理想情况下,这将创建类似于在控制台上右键单击存储桶中的文件并复制链接地址的结果)。我研究过回形针,它似乎不符合这个标准,但我可能只是不知道它的全部功能。我尝试了以下方法:defauthenticated_url(file_name,bucket)AWS::S3::S3Object.url_for(file_name,bucket,:secure=>true,:expires=>20*60)end产生这种类型的结果:...-1.amazonaws.com/file_path/file.zip.A

随机推荐