使用GCC工具链构建两部分固件映像 [英] Building a two-part firmware image using GCC toolchain

查看:398
本文介绍了使用GCC工具链构建两部分固件映像的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一些使用基于ARM Cortex M0的微控制器运行的GCC固件。该构建目前生成可以写入微控制器的程序存储器的单个二进制映像。



为了更新现场的原因,我需要将此映像分成两个部分可以单独更新。我会将这些核心应用称为。




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


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




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




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


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


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




所有这一切都可以。



我的问题只是我如何构建这些图片使用GCC和GNU binutils?



本质上,我想像正常的固件映像一样构建核心,然后构建应用程序映像,核心就像一个图书馆。但是,这里不适用共享链接(这将需要动态链接机制)或静态链接(将所使用的核心函数复制到应用程序二进制文件中)。我想要做的实际上是更简单:使用已知的固定地址链接现有的二进制文件。对于我来说,如何使用这些工具,这并不清楚。

解决方案

我们现在有这个工作,所以我要回答我自己的问题这是需要做的,从正常的单一图像构建开始,将其转变为核心,然后设置应用程序的构建。


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


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


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


void app_init(void);


  1. 从核心C代码中加入这个头文件,核心调用 app_init()启动应用程序。


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


app_init = 0x00010000; code>


  1. 使用核心链接描述文件构建核心,并添加 - just-symbols = app.sym 到链接器参数,以给出 app_init 的地址。保留ELF文件,我将调用 core.elf


  2. 创建一个该应用的链接描述文件。这将再次基于平台的标准链接描述文件,但是使用Flash& RAM内存范围更改为应用程序保留的内存范围。此外,它需要一个特殊的部分,以确保 app_init 被放置在应用程序闪存区域的开头,而 。文本部分:




 
SECTIONS
{
.text:
{
KEEP(*(。app_init))
*(。text *)




  1. 编写 app_init 函数。这将需要在装配中,因为它必须在应用程序中的任何C代码被调用之前做一些低级别的工作。它将需要标记为 .section .app_init ,以便链接器将其放置在应用程序闪存区域开始处的正确位置。 app_init 函数需要:


    1. 在应用程序的<$ c $中填充变量c> .data 部分,其中包含Flash的初始值。

    2. 在应用程序的 .bss 部分中设置变量调整为$。
    3. 调用应用程序的C入口点,我将调用 app_start()


  2. 编写启动应用程序的 app_start()函数。

    / li>
  3. 使用应用程序链接描述构建应用程序。该链接步骤应该传递包含 app_init app_start 的目标文件,以及 app_start 那个还没有在核心。链接器参数 - just-symbols = core.elf 应该被传递到核心的链接函数的地址。另外,应该传递 -nostartfiles ,以省去正常的C运行时启动代码。


花了一段时间来计算所有这些,但现在正在很好地工作。


I have some firmware built with GCC that runs on an ARM Cortex M0 based microcontroller. The build currently generates a single binary image that can be written into the program memory of the microcontroller.

For reasons to do with field update, I need to split this image into two parts that can be updated separately. I'll call these Core and App.

  • Core: contains the interrupt vector table, main() routine, and various drivers and library routines. It will be located in the first half of the program memory.

  • App: contains application-specific code. It will be located in the second half of the program memory. It will have a single entry point, at a known address, which is called by the core to start the application. It will access functions and data in the core via known addresses.

There are some obvious limitations here, which I'm well aware of:

  • When building the app, the addresses of symbols in the core will need to be known. So the core must be built first, and must be available when linking the app.

  • An app image will only be compatible with the specific core image it was built against.

  • It will be possible to update the app without updating the core, but not vice versa.

All of that is OK.

My question is simply, how can I build these images using GCC and the GNU binutils?

Essentially I want to build the core like a normal firmware image, and then build the app image, with the app treating the core like a library. But neither shared linking (which would require a dynamic linking mechanism) or static linking (which would copy the core functions used into the app binary) are applicable here. What I'm trying to do is actually a lot simpler: link against an existing binary using its known, fixed addresses. It's just not clear to me how to do so with the tools.

解决方案

We have this working now so I am going to answer my own question. Here is what was necessary to do this, starting from a normal single image build, turning that into the "core" and then setting up the build for the "app".

  1. Decide how to split up both the flash and the RAM into separate areas for the core and the app. Define the start address and size of each area.

  2. Create a linker script for the core. This will be the same as the standard linker script for the platform except that it must only use the areas reserved for the core. This can be done by changing the ORIGIN and LENGTH of the flash & RAM entries in the MEMORY section of the linker script.

  3. Create a header file declaring the entry point for the app. This just needs a prototype e.g.:

void app_init(void);.

  1. Include this header from the core C code and have the core call app_init() to start the app.

  2. Create a symbol file declaring the address of the entry point, which will be the start address of the flash area for the app. I'll call this app.sym. It can just be one line in the following format:

app_init = 0x00010000;

  1. Build the core, using the core linker script and adding --just-symbols=app.sym to the linker parameters to give the address of app_init. Retain the ELF file from the build, which I'll call core.elf.

  2. Create a linker script for the app. This will again be based on the standard linker script for the platform, but with the flash & RAM memory ranges changed to those reserved for the app. Additionally, it will need a special section to ensure that app_init is placed at the start of the app flash area, before the rest of the code in the .text section:

SECTIONS
{
    .text :
    {
        KEEP(*(.app_init))
        *(.text*)

  1. Write the app_init function. This will need to be in assembly, as it must do some low level work before any C code in the app can be called. It will need to be marked with .section .app_init so that the linker puts it in the correct place at the start of the app flash area. The app_init function needs to:

    1. Populate variables in the app's .data section with initial values from flash.
    2. Set variables in the app's .bss section to zero.
    3. Call the C entry point for the app, which I'll call app_start().

  2. Write the app_start() function that starts the app.

  3. Build the app, using the app linker script. This link step should be passed the object files containing app_init, app_start, and any code called by app_start that is not already in the core. The linker parameter --just-symbols=core.elf should be passed to link functions in the core by their addresses. Additionally, -nostartfiles should be passed to leave out the normal C runtime startup code.

It took a while to figure all this out but it is now working nicely.

这篇关于使用GCC工具链构建两部分固件映像的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆