将嵌入式程序拆分为内存中的多个部分

时间:2015-04-03 18:40:46

标签: linker embedded linker-scripts

我正在开发一个嵌入式系统(Stellaris Launchpad)并编写一个简单的操作系统(作为一个爱好项目)。使用过的工具链是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部分并将所有内核对象文件放在该部分中而不是星号。 我唯一的问题是内核不是一个文件,它是一大堆文件。文件可能会被添加,删除等。所以我该怎么做?如何更改链接描述文件,以便将动态第一组文件映射到第一个位置,将动态第二组文件映射到第二个位置?

2 个答案:

答案 0 :(得分:4)

您知道可以指定哪些文件用作某个部分的输入吗? 我们使用它将内核和应用程序代码分成快速内部闪存和较慢的外部闪存,如下所示:

.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

答案 1 :(得分:1)

我找到了一个解决方案,虽然感觉有点笨拙。它确实有效:

我发现链接器脚本可以处理.a文件,如果它们与ar静态链接。因此,假设您有一大堆.o文件,这些文件一起组成了内核:a.ob.oc.o。使用ar rcs kernel.a a.o, b.o, c.okernel.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>标志添加到链接器调用。此标志导致符号变为未定义,因此链接器将查找此符号以及此synbol所需的所有符号。 我的调用,为了参考:

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