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

Linker 将嵌入式程序拆分为内存中的多个部分,linker,embedded,linker-scripts,Linker,Embedded,Linker Scripts,我正在开发一个嵌入式系统(StellarisLaunchpad)并编写一个简单的操作系统(作为一个爱好项目)。使用的工具链是gcc none eabi 我的下一步是习惯MPU,允许内核阻止用户程序更改特定数据。我有一堆C文件,我把它们分成两部分:内核和其他。 我有以下链接器脚本开始: MEMORY { FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 0x00040000 SRAM (rwx) : ORIGIN = 0x20000000, LE

我正在开发一个嵌入式系统(StellarisLaunchpad)并编写一个简单的操作系统(作为一个爱好项目)。使用的工具链是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*/
}

在阅读了链接器脚本之后,我知道我可以用文件名替换星星,从而开始将flash拆分为多个部分。例如,我将创建一个.kernel.bss节,并将所有内核对象文件而不是星号放在该节中。
我唯一剩下的问题是内核不是一个文件,而是一大堆文件。文件可能会被添加、删除等。那么我该如何做呢?如何更改我的链接器脚本,以便将动态第一组文件映射到第一个位置,并将动态第二组文件映射到第二个位置?

我找到了一个解决方案,尽管它感觉有点粗糙。但它确实起作用:

我发现链接器脚本可以处理与ar静态链接的.a文件。因此,假设您有大量的.o文件,这些文件一起构成内核:
a.o
b.o
c.o
。使用ar rcs内核。a.o、b.o、c.o<代码>内核。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
}
这是可行的,但可能会导致一个新问题:链接器将库视为。。嗯,图书馆。因此,如果它们包含程序start(在我的例子中),链接器实际上不会查找它,链接器只会通过库查找实际o文件引用的函数。我找到的解决方案是将
-u
标志添加到链接器调用中。此标志导致符号未定义,因此链接器将查找此符号以及此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

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

.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节可能会有所帮助,(更详细地描述了输入部分)

大多数OSs是根据对象文件而不是程序段来区分的。集成到引导映像中的文件被视为内核的一部分,以及加载了专门用于添加到内核的命令的模块(如Linux的
insmod
)。所有其他图像都是用户空间。您是否打算支持单个对象部分加载到内核空间,另一部分加载到用户空间?如果你解释一下你的操作系统的原理,可能会有所帮助。操作系统必须在32K下运行,没有可用的外部闪存,因此引导后没有加载。也没有内存映射,因此没有虚拟寻址。因此,我们的想法是在链接过程中完成所有寻址工作,并将kernel.text、.bss和.data放在已知位置,由链接器计算。然后在引导时,内核确切地知道哪些地址范围是他的,以及它需要保护哪些地址范围。我的操作系统的想法是,用户为它编写自己的进程,然后编译、链接和刷新整个过程。非常有趣。据我所知,非常独特!您可能会发现使用诸如
\uuuuu属性(section(“.my\u custom\u name”))
之类的gcc扩展非常有用。看,我不认为这是独一无二的,freeRTOS在Stellaris发射台上做同样的事情。谢谢你的提示!不,我不知道!非常感谢:)需要注意的是,/*将成为评论。所以你的脚本不会工作,对吧?你能给出一个有效的例子吗?在斜线和星形之间打一个问号