El *_*rce 5 embedded executable firmware
我是嵌入式编程的总n00b。假设我正在使用编译器构建固件。此操作的结果是一个文件,该文件将被闪存到(我猜)诸如 ARM 或 AVR 之类的 MCU 的闪存中。
我的问题是: 此类生成的包含固件的文件使用哪些通用结构(如果有)?
我来自桌面开发,我知道例如对于 Windows,编译器很可能会生成PE或 PE+,而类 Unix 系统我可能会得到ELF和COFF,但不知道嵌入式系统。
我也明白这在很大程度上取决于许多因素(编译器、ISA、MCU 供应商、操作系统等),所以我至少可以举一个例子。
更新:我将对所有提供使用结构示例的答案进行投票,并将选择我认为最能调查最新技术的答案。
固件文件是可执行和可链接文件,通常处理为二进制文件(.bin)或文本表示的二进制文件(.hex)。
该二进制文件正是写入嵌入式闪存的内存。当您首次为开发板供电时,内部引导加载程序会将执行重定向到固件入口点,通常位于地址 0x0。
从那里开始,你的代码正在运行,这就是为什么你有一个启动代码(通常是startup.s文件),它将配置时钟、堆栈指针寄存器、向量表、将数据部分加载到RAM(你的初始化变量),清除零初始化部分,也许您需要将代码复制到 RAM 并跳转到副本以避免从 FLASH 运行代码(在某些平台上可能会更快),等等。
当在操作系统上运行时,所有这些平台选择和资源都不受用户代码的控制,您只能链接到操作系统库并使用提供的 API 来执行低级操作。在嵌入式中,它是 100% 用户代码,您可以访问硬件并管理其资源。
毫不奇怪,操作系统的启动方式与固件类似,因为两者都与处理器、内存和 I/O 相关。
所有这些,就是说:固件的结构类似于任何编译程序的结构。数据部分和代码部分在操作系统加载期间组织在内存中,或者在嵌入式运行时由程序本身组织。
一个主要区别是固件二进制文件中的内存寻址,通常地址是物理 RAM 地址,因为大多数微控制器没有内存映射功能。这对用户来说是透明的,编译器会抽象它。
其他显着的区别是堆栈指针,在操作系统上,用户代码本身不会为堆栈保留内存,而是依赖于操作系统。在固件上,您必须在用户代码中执行此操作,原因与以前相同,没有中间人为您管理它。编译器的链接器脚本将保留相应配置的堆栈和堆内存,并且 .map 文件上会有一个stack_pointer符号让您知道它指向哪里。您不会在操作系统程序的映射文件中找到它。
大多数工具输出 ELF、COFF 或类似的东西,最终可以归结为 HEX/bin 文件。
然而,这不一定是你的目标想要看到的。每个供应商都有自己的“固件”文件格式。有时它们是加密和签名的,有时是纯文本。有时是压缩的,有时是原始的。它可能是一个简单的文件,也可能是不仅仅是您的程序的复杂文件。
进行嵌入式工作的一个组成部分是构建流程和系统启动/引导过程,以及将代码添加到部件上。不要低估努力。