将嵌入式程序拆分为内存中的多个部分
Splitting embedded program in multiple parts in memory
我正在开发嵌入式系统 (Stellaris Launchpad) 并编写一个简单的 OS(作为业余爱好项目)。使用的工具链是 gcc-none-eabi.
我的下一步是习惯 MPU 以允许内核防止用户程序更改特定数据。我有一堆 C 文件,我将它们分成两部分:内核和其他部分。
我有以下链接器脚本开始:
MEMORY
{
FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 0x00040000
SRAM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x00008000
}
SECTIONS
{
.text :
{
_text = .;
KEEP(*(.isr_vector))
*(.text*)
*(.rodata*)
_etext = .;
} > FLASH
.data : /*AT(ADDR(.text) + SIZEOF(.text))*/ /*contains initialized data*/
{
_data = .;
*(vtable)
*(.data*)
_edata = .;
} > SRAM AT > FLASH
.bss : AT (ADDR(.data) + SIZEOF(.data)) /*contains unitialized data (should be set to all zero's)*/
{
_bss = .;
*(.bss*)
*(COMMON)
_ebss = .;
_start_heap = .;
} > SRAM
_stack_top = ORIGIN(SRAM) + LENGTH(SRAM) - 1; /*The starting point of the stack, at the very bottom of the RAM*/
}
在阅读了链接描述文件后,我知道我可以用文件名替换星星,从而开始将闪存分成多个部分。例如,我会创建一个 .kernel.bss 部分,并将所有内核对象文件而不是该部分中的星星放在一起。
我剩下的唯一问题是内核不是一个文件,而是一大堆文件。并且文件可能会被添加、删除等。那么我该怎么做呢?如何更改我的链接描述文件,以便将动态的第一组文件映射到第一个位置,并将动态的第二组文件映射到第二个位置?
我找到了解决方案,虽然感觉有点老套。它确实有效:
我发现如果 .a 文件与 ar 静态链接,则链接器脚本可以处理这些文件。因此,假设您有一堆 .o 文件,它们一起构成了内核:a.o
、b.o
、c.o
。使用 ar rcs kernel.a a.o, b.o, c.o
。 kernel.a
现在是您的内核,您要将其单独存储在内存中。
接下来您需要知道的是,链接描述文件中的 *
实际上是所有尚未使用的内容的通配符。所以我们可以创建以下链接描述文件:
MEMORY
{
FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 0x00040000
SRAM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x00008000
}
SECTIONS
{
.kernel.text :
{
_kernel_text = .;
KEEP(kernel.a(.isr_vector))
KEEP(kernel.a(_sbrk))
kernel.a(.text*)
kernel.a(.rodata*)
_kernel_etext = .;
_kernel_flash_data = ALIGN(0x4);
} > FLASH
.kernel.data : /*AT(ADDR(.text) + SIZEOF(.text))*/ /*contains initialized data*/
{
_kernel_data = .;
kernel.a(vtable)
kernel.a(.data*)
_kernel_edata = .;
} > SRAM AT > FLASH
.kernel.bss :
{
_kernel_bss = .;
kernel.a(.bss*)
kernel.a(COMMON)
_kernel_ebss = .;
} > SRAM
.text : /*AT (ADDR(.core.text) + SIZEOF(.core.text) + SIZEOF(.core.data))*/
{
_text = .;
*(.text*)
*(.rodata*)
_etext = .;
_flash_data = ALIGN(0x4);
} > FLASH
.data :
{
_data = .;
*(vtable)
*(.data*)
_edata = .;
} > SRAM AT > FLASH
.bss : AT (ADDR(.data) + SIZEOF(.data)) /*contains unitialized data (should be set to all zero's)*/
{
_bss = .;
*(.bss*)
*(COMMON)
_ebss = .;
_start_heap = .;
} > SRAM
}
这可行,但可能会导致一个新问题:链接器将库视为……嗯,库。因此,如果它们包含程序开始(如我的情况),链接器实际上不会查找它,链接器只会在库中查找实际 o 文件引用的函数。我为此找到的解决方案是将 -u <name>
标志添加到链接器调用中。此标志导致符号变为未定义,因此链接器将查找此符号以及此符号所需的所有符号。
我的调用,为了参考:
arm-none-eabi-ld -Tlinker_script.ld -nostdlib --entry ResetISR
--gc-sections -u _sbrk -u .isr_vector
-L./lib//hardfp
-L/home/me/gcc-arm-none-eabi/gcc-arm-none-eabi-4_9-2015q1/arm-none-eabi/lib/armv7e-m/fpu
-L/home/me/gcc-arm-none-eabi/gcc-arm-none-eabi-4_9-2015q1/lib/gcc/arm-none-eabi/4.9.3/armv7e-m/fpu
-Lrelease/
-o release/os
./user/obj/release/ledsDance.c.o ./user/obj/release/main.c.o ./validation/obj/release/val_floattest.c.o ./validation/obj/release/val_genTest.c.o ./validation/obj/release/val_gpiotest.c.o ./validation/obj/release/val_iotest.c.o ./validation/obj/release/val_proctest.c.o ./validation/obj/release/val_schedTest.c.o release/kernel.a release/core.a
-ldriver-cm4f
-luartstdio
-lm
-lc
-lgcc
您知道可以指定将哪些文件用作某个部分的输入吗?
我们用它来将内核和应用程序代码分成快速的内部闪存和较慢的外部闪存,如下所示:
.kernel_text :
{
build/kernel/*.o (.text*) /*text section from files in build/kernel*/
} > INT_FLASH
.app_text:
{
build/app/*.o(.text*)
} > EXT_FLASH
第 4.6.4 节可能会有帮助,(更详细地描述了输入部分)
https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/4/html/Using_ld_the_GNU_Linker/sections.html
我正在开发嵌入式系统 (Stellaris Launchpad) 并编写一个简单的 OS(作为业余爱好项目)。使用的工具链是 gcc-none-eabi.
我的下一步是习惯 MPU 以允许内核防止用户程序更改特定数据。我有一堆 C 文件,我将它们分成两部分:内核和其他部分。 我有以下链接器脚本开始:
MEMORY
{
FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 0x00040000
SRAM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x00008000
}
SECTIONS
{
.text :
{
_text = .;
KEEP(*(.isr_vector))
*(.text*)
*(.rodata*)
_etext = .;
} > FLASH
.data : /*AT(ADDR(.text) + SIZEOF(.text))*/ /*contains initialized data*/
{
_data = .;
*(vtable)
*(.data*)
_edata = .;
} > SRAM AT > FLASH
.bss : AT (ADDR(.data) + SIZEOF(.data)) /*contains unitialized data (should be set to all zero's)*/
{
_bss = .;
*(.bss*)
*(COMMON)
_ebss = .;
_start_heap = .;
} > SRAM
_stack_top = ORIGIN(SRAM) + LENGTH(SRAM) - 1; /*The starting point of the stack, at the very bottom of the RAM*/
}
在阅读了链接描述文件后,我知道我可以用文件名替换星星,从而开始将闪存分成多个部分。例如,我会创建一个 .kernel.bss 部分,并将所有内核对象文件而不是该部分中的星星放在一起。 我剩下的唯一问题是内核不是一个文件,而是一大堆文件。并且文件可能会被添加、删除等。那么我该怎么做呢?如何更改我的链接描述文件,以便将动态的第一组文件映射到第一个位置,并将动态的第二组文件映射到第二个位置?
我找到了解决方案,虽然感觉有点老套。它确实有效:
我发现如果 .a 文件与 ar 静态链接,则链接器脚本可以处理这些文件。因此,假设您有一堆 .o 文件,它们一起构成了内核:a.o
、b.o
、c.o
。使用 ar rcs kernel.a a.o, b.o, c.o
。 kernel.a
现在是您的内核,您要将其单独存储在内存中。
接下来您需要知道的是,链接描述文件中的 *
实际上是所有尚未使用的内容的通配符。所以我们可以创建以下链接描述文件:
MEMORY
{
FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 0x00040000
SRAM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x00008000
}
SECTIONS
{
.kernel.text :
{
_kernel_text = .;
KEEP(kernel.a(.isr_vector))
KEEP(kernel.a(_sbrk))
kernel.a(.text*)
kernel.a(.rodata*)
_kernel_etext = .;
_kernel_flash_data = ALIGN(0x4);
} > FLASH
.kernel.data : /*AT(ADDR(.text) + SIZEOF(.text))*/ /*contains initialized data*/
{
_kernel_data = .;
kernel.a(vtable)
kernel.a(.data*)
_kernel_edata = .;
} > SRAM AT > FLASH
.kernel.bss :
{
_kernel_bss = .;
kernel.a(.bss*)
kernel.a(COMMON)
_kernel_ebss = .;
} > SRAM
.text : /*AT (ADDR(.core.text) + SIZEOF(.core.text) + SIZEOF(.core.data))*/
{
_text = .;
*(.text*)
*(.rodata*)
_etext = .;
_flash_data = ALIGN(0x4);
} > FLASH
.data :
{
_data = .;
*(vtable)
*(.data*)
_edata = .;
} > SRAM AT > FLASH
.bss : AT (ADDR(.data) + SIZEOF(.data)) /*contains unitialized data (should be set to all zero's)*/
{
_bss = .;
*(.bss*)
*(COMMON)
_ebss = .;
_start_heap = .;
} > SRAM
}
这可行,但可能会导致一个新问题:链接器将库视为……嗯,库。因此,如果它们包含程序开始(如我的情况),链接器实际上不会查找它,链接器只会在库中查找实际 o 文件引用的函数。我为此找到的解决方案是将 -u <name>
标志添加到链接器调用中。此标志导致符号变为未定义,因此链接器将查找此符号以及此符号所需的所有符号。
我的调用,为了参考:
arm-none-eabi-ld -Tlinker_script.ld -nostdlib --entry ResetISR
--gc-sections -u _sbrk -u .isr_vector
-L./lib//hardfp
-L/home/me/gcc-arm-none-eabi/gcc-arm-none-eabi-4_9-2015q1/arm-none-eabi/lib/armv7e-m/fpu
-L/home/me/gcc-arm-none-eabi/gcc-arm-none-eabi-4_9-2015q1/lib/gcc/arm-none-eabi/4.9.3/armv7e-m/fpu
-Lrelease/
-o release/os
./user/obj/release/ledsDance.c.o ./user/obj/release/main.c.o ./validation/obj/release/val_floattest.c.o ./validation/obj/release/val_genTest.c.o ./validation/obj/release/val_gpiotest.c.o ./validation/obj/release/val_iotest.c.o ./validation/obj/release/val_proctest.c.o ./validation/obj/release/val_schedTest.c.o release/kernel.a release/core.a
-ldriver-cm4f
-luartstdio
-lm
-lc
-lgcc
您知道可以指定将哪些文件用作某个部分的输入吗? 我们用它来将内核和应用程序代码分成快速的内部闪存和较慢的外部闪存,如下所示:
.kernel_text :
{
build/kernel/*.o (.text*) /*text section from files in build/kernel*/
} > INT_FLASH
.app_text:
{
build/app/*.o(.text*)
} > EXT_FLASH
第 4.6.4 节可能会有帮助,(更详细地描述了输入部分) https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/4/html/Using_ld_the_GNU_Linker/sections.html