5

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?

Cheiron
  • 3,620
  • 4
  • 32
  • 63
  • Most OSs discriminate by object file, not by program sections. Files incorporated into the boot image are treated as part of the kernel as well as modules loaded with a command specifically for adding to the kernel (like Linux's `insmod`). All other images are user space. Are you intending to support a single object to load partly into kernel space and the other part being in user space? It might help if you explained your OS's philosophy. – wallyk Apr 03 '15 at 18:49
  • The OS has to run in 32K with no external flashes avalable, so there is no loading after boot. There is also no memory mapping, so no virtual addressing. So the idea is to do all of the addressing work during linking and put the kernel .text, .bss and .data on known places, to be calculated by the linker. Then on boot the kernel knows exactly which address ranges are his and this which address ranges it needs to protect. The idea of my OS is that a user writes its own processes for it and then compiles, links and flashes the entire thing. – Cheiron Apr 03 '15 at 18:52
  • Very interesting. And as far as I know, quite unique! You might find it useful to use gcc extensions like `__attribute__ ((section (".my_custom_name")))`. See https://gcc.gnu.org/onlinedocs/gcc-3.2/gcc/Variable-Attributes.html – wallyk Apr 03 '15 at 20:40
  • I dont think its that unique, freeRTOS does the same thing on the Stellaris Launchpad. Thank you for the hint! – Cheiron Apr 03 '15 at 21:23

2 Answers2

5

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

Diederik Loos
  • 131
  • 1
  • 4
2

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
wallyk
  • 56,922
  • 16
  • 83
  • 148
Cheiron
  • 3,620
  • 4
  • 32
  • 63