使用GCC工具链构建两部分固件映像 [英] Building a two-part firmware image using GCC toolchain
问题描述
为了更新现场的原因,我需要将此映像分成两个部分可以单独更新。我会将这些核心和应用称为。
-
Core :包含中断向量表,
main()
例程,以及各种驱动程序和库例程。它将位于程序存储器的前半部分。 -
应用:包含应用程序特定的代码。它将位于程序存储器的后半部分。它将具有一个单一的入口点,在已知的地址处,由核心调用以启动应用程序。它将通过已知地址访问核心中的功能和数据。
这里有一些明显的限制,很清楚:
-
在构建应用程序时,核心中的符号地址将需要知道。因此,核心必须首先构建,并且在链接应用程序时必须可用。
-
应用程序图像将仅与其构建的特定核心映像兼容
-
可以在不更新内核的情况下更新应用程序,但反之亦然。
所有这一切都可以。
我的问题只是我如何构建这些图片使用GCC和GNU binutils?
本质上,我想像正常的固件映像一样构建核心,然后构建应用程序映像,核心就像一个图书馆。但是,这里不适用共享链接(这将需要动态链接机制)或静态链接(将所使用的核心函数复制到应用程序二进制文件中)。我想要做的实际上是更简单:使用已知的固定地址链接现有的二进制文件。对于我来说,如何使用这些工具,这并不清楚。
我们现在有这个工作,所以我要回答我自己的问题这是需要做的,从正常的单一图像构建开始,将其转变为核心,然后设置应用程序的构建。
-
决定如何将闪存和内存分为不同的核心和应用程序区域。定义每个区域的起始地址和大小。
-
为核心创建链接描述文件。这将与平台的标准链接描述文件相同,只不过它只能使用为核心保留的区域。这可以通过更改闪存和闪存的
ORIGIN
和LENGTH
来完成。链接描述文件的MEMORY
部分中的RAM条目。 -
创建一个声明入口点的头文件为应用程序这只需要一个原型,例如:
void app_init(void);
-
从核心C代码中加入这个头文件,核心调用
app_init()
启动应用程序。 -
创建一个声明入口点地址的符号文件,作为应用程序的闪存区域的起始地址。我会把它称为
app.sym
。它可以是以下格式的一行:
app_init = 0x00010000; code>
-
使用核心链接描述文件构建核心,并添加
- just-symbols = app.sym
到链接器参数,以给出app_init
的地址。保留ELF文件,我将调用core.elf
。 -
创建一个该应用的链接描述文件。这将再次基于平台的标准链接描述文件,但是使用Flash& RAM内存范围更改为应用程序保留的内存范围。此外,它需要一个特殊的部分,以确保
app_init
被放置在应用程序闪存区域的开头,而。文本
部分:
SECTIONS
{
.text:
{
KEEP(*(。app_init))
*(。text *)
-
编写
app_init
函数。这将需要在装配中,因为它必须在应用程序中的任何C代码被调用之前做一些低级别的工作。它将需要标记为.section .app_init
,以便链接器将其放置在应用程序闪存区域开始处的正确位置。app_init
函数需要:- 在应用程序的<$ c $中填充变量c> .data 部分,其中包含Flash的初始值。
- 在应用程序的
.bss
部分中设置变量调整为$。
- 调用应用程序的C入口点,我将调用
app_start()
。
-
编写启动应用程序的
/ li>app_start()
函数。
-
使用应用程序链接描述构建应用程序。该链接步骤应该传递包含
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".
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.
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
andLENGTH
of the flash & RAM entries in theMEMORY
section of the linker script.Create a header file declaring the entry point for the app. This just needs a prototype e.g.:
void app_init(void);
.
Include this header from the core C code and have the core call
app_init()
to start the app.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;
Build the core, using the core linker script and adding
--just-symbols=app.sym
to the linker parameters to give the address ofapp_init
. Retain the ELF file from the build, which I'll callcore.elf
.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*)
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. Theapp_init
function needs to:- Populate variables in the app's
.data
section with initial values from flash. - Set variables in the app's
.bss
section to zero. - Call the C entry point for the app, which I'll call
app_start()
.
- Populate variables in the app's
Write the
app_start()
function that starts the app.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 byapp_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屋!