使用GCC工具链构建两部分固件映像

Mar*_*n L 6 embedded microcontroller linker gcc binutils

我有一些使用GCC构建的固件,它运行在基于ARM Cortex M0的微控制器上.构建当前生成单个二进制映像,可以将其写入微控制器的程序存储器中.

由于与字段更新有关的原因,我需要将此图像分成两个部分,可以单独更新.我将这些称为CoreApp.

  • 核心:包含中断向量表,main()例程以及各种驱动程序和库例程.它将位于程序存储器的前半部分.

  • 应用:包含特定于应用程序的代码.它将位于程序存储器的后半部分.它将在一个已知地址处有一个入口点,由核心调用以启动应用程序.它将通过已知地址访问核心中的功能和数据.

这里有一些明显的限制,我很清楚:

  • 构建应用程序时,需要知道核心中符号的地址.因此必须首先构建核心,并且在链接应用程序时必须可用.

  • 应用程序映像仅与其构建的特定核心映像兼容.

  • 可以在不更新核心的情况下更新应用程序,但反之亦然.

所有这一切都没问题.

我的问题很简单,我如何使用GCC和GNU binutils构建这些图像?

基本上我想像正常的固件映像一样构建核心,然后构建应用程序映像,应用程序将核心视为库.但是,共享链接(这将需要动态链接机制)或静态链接(将复制用于应用程序二进制文件的核心功能)都不适用于此处.我正在尝试做的事情实际上要简单得多:使用已知的固定地址链接现有的二进制文件.我不清楚如何用这些工具做到这一点.

Mar*_*n L 6

我们现在有这个工作,所以我要回答我自己的问题。这是执行此操作所必需的,从普通的单个图像构建开始,将其转换为“核心”,然后为“应用程序”设置构建。

  1. 决定如何将闪存和 RAM 拆分为内核和应用程序的单独区域。定义每个区域的起始地址和大小。

  2. 为内核创建链接描述文件。这将与平台的标准链接描述文件相同,只是它必须仅使用为内核保留的区域。这可以通过更改链接描述文件部分中闪存和 RAM 条目的ORIGIN和来完成。LENGTHMEMORY

  3. 创建一个头文件,声明应用程序的入口点。这只需要一个原型,例如:

void app_init(void);.

  1. 在核心 C 代码中包含此标头,并使用核心调用app_init()来启动应用程序。

  2. 创建一个符号文件,声明入口点的地址,这将是应用程序闪存区域的起始地址。我会打电话给这个app.sym。它可以是以下格式的一行:

app_init = 0x00010000;

  1. 构建核心,使用核心链接器脚本并添加--just-symbols=app.sym到链接器参数以给出app_init. 保留构建中的 ELF 文件,我将其称为core.elf.

  2. 为应用程序创建链接器脚本。这将再次基于平台的标准链接器脚本,但闪存和 RAM 内存范围更改为为应用程序保留的范围。此外,它还需要一个特殊的部分,以确保将app_init其放置在应用程序闪存区域的开头,在该部分中的其余代码之前.text

部分
{
    。文本 :
    {
        保持(*(.app_init))
        *(。文本*)
  1. 编写app_init函数。这将需要在汇编中,因为它必须在调用应用程序中的任何 C 代码之前执行一些低级工作。它需要被标记,.section .app_init以便链接器将它放在应用程序闪存区域开头的正确位置。该app_init函数需要:

    1. .data使用 Flash 中的初始值填充应用程序部分中的变量。
    2. 将应用程序.bss部分中的变量设置为零。
    3. 调用应用程序的 C 入口点,我将调用app_start().
  2. 编写app_start()启动应用程序的函数。

  3. 使用应用程序链接器脚本构建应用程序。此链接步骤应传递包含app_initapp_start和任何由app_start它调用的代码不在核心中的目标文件。链接器参数--just-symbols=core.elf应该通过地址传递给内核中的链接函数。此外,-nostartfiles应传递以省略正常的 C 运行时启动代码。

花了一段时间才弄清楚这一切,但现在运行良好。