Mar*_*n L 6 embedded microcontroller linker gcc binutils
我有一些使用GCC构建的固件,它运行在基于ARM Cortex M0的微控制器上.构建当前生成单个二进制映像,可以将其写入微控制器的程序存储器中.
由于与字段更新有关的原因,我需要将此图像分成两个部分,可以单独更新.我将这些称为Core和App.
核心:包含中断向量表,main()例程以及各种驱动程序和库例程.它将位于程序存储器的前半部分.
应用:包含特定于应用程序的代码.它将位于程序存储器的后半部分.它将在一个已知地址处有一个入口点,由核心调用以启动应用程序.它将通过已知地址访问核心中的功能和数据.
这里有一些明显的限制,我很清楚:
构建应用程序时,需要知道核心中符号的地址.因此必须首先构建核心,并且在链接应用程序时必须可用.
应用程序映像仅与其构建的特定核心映像兼容.
可以在不更新核心的情况下更新应用程序,但反之亦然.
所有这一切都没问题.
我的问题很简单,我如何使用GCC和GNU binutils构建这些图像?
基本上我想像正常的固件映像一样构建核心,然后构建应用程序映像,应用程序将核心视为库.但是,共享链接(这将需要动态链接机制)或静态链接(将复制用于应用程序二进制文件的核心功能)都不适用于此处.我正在尝试做的事情实际上要简单得多:使用已知的固定地址链接现有的二进制文件.我不清楚如何用这些工具做到这一点.
我们现在有这个工作,所以我要回答我自己的问题。这是执行此操作所必需的,从普通的单个图像构建开始,将其转换为“核心”,然后为“应用程序”设置构建。
决定如何将闪存和 RAM 拆分为内核和应用程序的单独区域。定义每个区域的起始地址和大小。
为内核创建链接描述文件。这将与平台的标准链接描述文件相同,只是它必须仅使用为内核保留的区域。这可以通过更改链接描述文件部分中闪存和 RAM 条目的ORIGIN和来完成。LENGTHMEMORY
创建一个头文件,声明应用程序的入口点。这只需要一个原型,例如:
void app_init(void);.
在核心 C 代码中包含此标头,并使用核心调用app_init()来启动应用程序。
创建一个符号文件,声明入口点的地址,这将是应用程序闪存区域的起始地址。我会打电话给这个app.sym。它可以是以下格式的一行:
app_init = 0x00010000;
构建核心,使用核心链接器脚本并添加--just-symbols=app.sym到链接器参数以给出app_init. 保留构建中的 ELF 文件,我将其称为core.elf.
为应用程序创建链接器脚本。这将再次基于平台的标准链接器脚本,但闪存和 RAM 内存范围更改为为应用程序保留的范围。此外,它还需要一个特殊的部分,以确保将app_init其放置在应用程序闪存区域的开头,在该部分中的其余代码之前.text:
部分
{
    。文本 :
    {
        保持(*(.app_init))
        *(。文本*)
编写app_init函数。这将需要在汇编中,因为它必须在调用应用程序中的任何 C 代码之前执行一些低级工作。它需要被标记,.section .app_init以便链接器将它放在应用程序闪存区域开头的正确位置。该app_init函数需要:
.data使用 Flash 中的初始值填充应用程序部分中的变量。.bss部分中的变量设置为零。app_start().编写app_start()启动应用程序的函数。
使用应用程序链接器脚本构建应用程序。此链接步骤应传递包含app_init、app_start和任何由app_start它调用的代码不在核心中的目标文件。链接器参数--just-symbols=core.elf应该通过地址传递给内核中的链接函数。此外,-nostartfiles应传递以省略正常的 C 运行时启动代码。
花了一段时间才弄清楚这一切,但现在运行良好。