固件文件的常见结构是什么?

时间:2016-11-28 21:35:25

标签: embedded executable firmware

我在嵌入式编程中总共n00b。假设我正在使用编译器构建固件。此操作的结果是一个文件,它将闪存到(我猜)MCU(例如ARM或AVR)的闪存中。

我的问题是: 哪些常见结构(如果有)用于包含固件的此类生成文件?

我来自桌面开发,我理解,例如对于Windows,编译器很可能会生成PE或PE +,而类Unix系统我可能会得到ELF和{{3} },但不知道嵌入式系统。

我也明白这在很大程度上取决于很多因素(编译器,ISA,MCU供应商,操作系统等),所以我至少可以举一个例子。

更新:我将提供所有答案,提供已使用结构的示例,并将选择我认为最适合调查现有技术的一个。

4 个答案:

答案 0 :(得分:4)

固件文件是Executable and Linkable File,通常处理为二进制(.bin)或文本表示二进制(.hex)。

此二进制文件是写入嵌入式闪存的确切内存。首次为电路板供电时,内部引导加载程序会将执行重定向到固件入口点,通常位于地址0x0。

从那里开始,这是你的代码正在运行,这就是为什么你有一个启动代码(通常是startup.s文件),它将配置时钟,堆栈指针寄存器,向量表,将数据部分加载到RAM(你的初始化)变量),清除零初始化部分,也许你会想要将你的代码复制到RAM并跳转到副本以避免从FLASH运行代码(在某些平台上可以更快),等等。

当在操作系统上运行时,所有这些平台选择和资源都不受用户代码的控制,您只能链接到操作系统库并使用提供的API执行低级操作。在嵌入式中,它是100%的用户代码,您可以访问硬件并管理其资源。

毫不奇怪,操作系统的启动方式与固件类似,因为它们都与处理器,内存和I / O保持联系。

所有这些,要说:固件的结构类似于任何编译程序的结构。在操作系统加载期间,数据部分和代码部分在内存中组织,或者在嵌入式运行时由程序本身组织。

一个主要的区别是firwmare二进制文件中的内存寻址,通常地址是physical RAM address,因为在大多数微控制器上没有内存映射功能。这对用户是透明的,编译器将抽象它。

其他重要的区别是堆栈指针,在操作系统上,用户代码本身不会为堆栈保留内存,它会在操作系统上进行中继。在固件上,您必须在用户代码中执行此操作,原因与之前相同,没有中间人可以为您管理它。编译器的链接描述文件将保留相应配置的堆栈和堆内存,并在.map文件中有一个stack_pointer符号,让您知道它指向的位置。您将无法在OSs程序的地图文件中找到它。

答案 1 :(得分:4)

大多数工具输出ELF或COFF,或类似的东西,最终可归结为HEX / bin文件。

然而,这不一定是你的目标想要看到的东西。每个供应商都有自己的“固件”文件格式。有时它们是加密和签名的,有时是纯文本。有时会有压缩,有时它是原始的。它可能是一个简单的文件,或者复杂的东西,不仅仅是你的程序。

进行嵌入式工作的一个组成部分是构建流程和系统启动/启动过程,以及将代码放到部件上。不要低估这种努力。

答案 2 :(得分:3)

最终写入ROM的数据通常只是构成应用程序的代码和常量数据,因此除了可能被分割成代码和数据之外没有其他结构,如果你创建了它们,可能还有自定义段。这种意义上的结构由链接器脚本或用于构建代码的配置定义。包含此代码/数据的文件可以是原始二进制文件,也可以是编码的二进制格式,例如Intel Hex或Motorola S-Record。

通常,您的工具链也会生成一个目标代码文件,该文件不仅包含代码/数据,还包含调试器使用的符号和调试信息。在这种情况下,当调试器运行时,它会将代码加载到目标(如上面的二进制文件中),并将符号/调试信息加载到主机以允许源级调试。这些文件可以是特定于工具链的专有目标文件格式,但通常是标准的“开放”格式,例如ELF。但严格来说,目标文件的元数据组件不属于 firmware ,因为它们未加载到目标文件上。

答案 3 :(得分:1)

我最近遇到了此处未列出的另一种固件格式。它是一种二进制格式,可能被称为“.EEP”,但也可能不是。我认为它被 NXP 使用。我已经看到它用于 ARM THUMB2 和可能是 DSP/BSP 的神秘东西。

以下都是 32 位值,全部以逆序存储除了对于 CAFEBABE(所以...BEBAFECA?):

咖啡宝贝 length_in_16_bit_words(是的,16 位...?!) 基地址 CRC32 长度*2字节数据 FFFF(如果长度是奇数,则可选填充)

如果有更多的数据块: 长度 根据 校验和不是 CRC 而是奇怪的东西 数据 FFFF(如果长度为奇数,则为可选填充符) ...

当没有更多数据块剩余时: 长度 == 0 基数 == 0 校验和不是 CRC,而是奇怪的东西

然后对另一个存储库/设备重复所有这些操作。