Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Splitting embedded program in multiple parts in memory

I am working on an embedded system (Stellaris Launchpad) and writing a simple OS (as a hobby project). The used toolchain is gcc-none-eabi.

My next step is to get used to the MPU to allow the kernel to prevent user programs from altering specific data. I have a bunch of C files and I splitted them in two parts: kernel and other. I have the following linker script to start out with:

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*/

}

And after reading up on linker scripts I know that I can replace the stars with filenames, and thus start splitting the flash in multiple parts. I would for example create a .kernel.bss section and put all of the kernel object files instead of the stars in that section. My only problem left is that the kernel is not one file, it is a whole lot of files. And files might be added, removed etc. So how do I do this? How do I change my linker script so that a dynamic first group of files is mapped to the first place and a dynamic second group of files is mapped to a second place?

like image 310
Cheiron Avatar asked Apr 03 '15 18:04

Cheiron


2 Answers

you know that you can specify what files are used as input for a section? We use this for separating kernel and application code into fast internal flash, and slower external flash memory, like so:

.kernel_text :
{
     build/kernel/*.o (.text*) /*text section from files in build/kernel*/
} > INT_FLASH

.app_text:
{
    build/app/*.o(.text*)
} > EXT_FLASH

Section 4.6.4 might be helpful, (describes input sections in more detail) https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/4/html/Using_ld_the_GNU_Linker/sections.html

like image 70
Diederik Loos Avatar answered Nov 04 '22 01:11

Diederik Loos


I found a solution, allthough it feels a bit hacky. It does work though:

I found out that a linker script is OK with working on .a files if they are statically linked with ar. So lets say you have a buch of .o files that, together form the kernel: a.o, b.o, c.o. Use ar rcs kernel.a a.o, b.o, c.o. kernel.a is now your kernel, which you want to store seperately in memory.

The next thing you need to know is that the * in a linker script is actually a wildcard for everything not used yet. So we can create the following linker script:

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
}

This works but will probably lead to a new problem: the linker treats libraries as.. well, libraries. So if they contain the program start (as in my case) the linker does not actually look for it, the linker only looks trough the library for functions refered to by the actual o files. The solution I found for this is to add the -u <name> flag to the linker invocation. This flag causes a symbol to become undefined, so the linker will look for this symbol plus all symbols that are needed by this synbol. My invocation, for references sake:

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
like image 23
Cheiron Avatar answered Nov 04 '22 01:11

Cheiron