10
votes

I hope the title is descriptive enough. So here is what I want to do and what I've toyed with.

Wait, first off, this is an embedded application. Atmel SAM4L microcontroller, using the Atmel Studio IDE and GCC compiler/linker.

Right, I am writing a bootloader, but want to save the bootloader version to the program memory to the end of the allocated space for the bootloader (let's say 0x3FF0). This way the app can also check the bootloader version by just looking at the specific address. At this moment I am busy with a utility for the app to update the bootloader itself, but I don't want the app or the bootloader to update the version at 0x3FF0 with a command or in code, I want it as part of the .bin/.hex file, so when I flash the bootloader on, the version is flashed on along with it. So currently I have a #define for the bootloader type, major version and minor version which are all in a file globals.h in the project. Basically I just want to write those 3 bytes to 0x3FF0 when I hit compile.

As I understand there's quite a lot of tricks I can pull with the linker, but have never before played with linker scripts until yesterday, and have been able to do some things with it, but not what I want yet. The project also creates quite an intense linker script, so I'm also a bit wary as to where to jump in and dump my three bytes. I know you're not allowed to move the address pointer back.

Here is the linker script generated by the project

/**
 * \file
 *
 * \brief Flash Linker script for SAM.
 *
 * Copyright (c) 2013 Atmel Corporation. All rights reserved.
 *
 * \asf_license_start
 *
 * \page License
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * 3. The name of Atmel may not be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * 4. This software may only be redistributed and used in connection with an
 *    Atmel microcontroller product.
 *
 * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
 * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * \asf_license_stop
 *
 */

OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
OUTPUT_ARCH(arm)
SEARCH_DIR(.)

/*
 * NOTE: to keep binary compatibility with SAM4L4 device on SAM4L Xplained Pro,
 * we use SAM4L4 memory space here instead SAM4L8. You may change it if you are
 * using SAM4L8 device.
 */
/* Memory Spaces Definitions */
MEMORY
{
  rom (rx)  : ORIGIN = 0x00000000, LENGTH = 0x00040000 /* flash, 256K */
  ram (rwx) : ORIGIN = 0x20000000, LENGTH = 0x00008000 /* sram, 32K */
  /* rom (rx)  : ORIGIN = 0x00000000, LENGTH = 0x00080000 */ /* flash, 512K */
  /* ram (rwx) : ORIGIN = 0x20000000, LENGTH = 0x00010000 */ /* sram, 64K */
}

/* The stack size used by the application. NOTE: you need to adjust according to your application. */
__stack_size__ = DEFINED(__stack_size__) ? __stack_size__ : 0x1000;
__ram_end__ = ORIGIN(ram) + LENGTH(ram) - 4;

/* Section Definitions */
SECTIONS
{
    .text :
    {
        . = ALIGN(4);
        _sfixed = .;
        KEEP(*(.vectors .vectors.*))
        *(.text .text.* .gnu.linkonce.t.*)
        *(.glue_7t) *(.glue_7)
        *(.rodata .rodata* .gnu.linkonce.r.*)
        *(.ARM.extab* .gnu.linkonce.armextab.*)

        /* Support C constructors, and C destructors in both user code
           and the C library. This also provides support for C++ code. */
        . = ALIGN(4);
        KEEP(*(.init))
        . = ALIGN(4);
        __preinit_array_start = .;
        KEEP (*(.preinit_array))
        __preinit_array_end = .;

        . = ALIGN(4);
        __init_array_start = .;
        KEEP (*(SORT(.init_array.*)))
        KEEP (*(.init_array))
        __init_array_end = .;

        . = ALIGN(0x4);
        KEEP (*crtbegin.o(.ctors))
        KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors))
        KEEP (*(SORT(.ctors.*)))
        KEEP (*crtend.o(.ctors))

        . = ALIGN(4);
        KEEP(*(.fini))

        . = ALIGN(4);
        __fini_array_start = .;
        KEEP (*(.fini_array))
        KEEP (*(SORT(.fini_array.*)))
        __fini_array_end = .;

        KEEP (*crtbegin.o(.dtors))
        KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors))
        KEEP (*(SORT(.dtors.*)))
        KEEP (*crtend.o(.dtors))

        . = ALIGN(4);
        _efixed = .;            /* End of text section */
    } > rom

    /* .ARM.exidx is sorted, so has to go in its own output section.  */
    PROVIDE_HIDDEN (__exidx_start = .);
    .ARM.exidx :
    {
      *(.ARM.exidx* .gnu.linkonce.armexidx.*)
    } > rom
    PROVIDE_HIDDEN (__exidx_end = .);

    . = ALIGN(4);
    _etext = .;

    .relocate : AT (_etext)
    {
        . = ALIGN(4);
        _srelocate = .;
        *(.ramfunc .ramfunc.*);
        *(.data .data.*);
        . = ALIGN(4);
        _erelocate = .;
    } > ram

    /* .bss section which is used for uninitialized data */
    .bss (NOLOAD) :
    {
        . = ALIGN(4);
        _sbss = . ;
        _szero = .;
        *(.bss .bss.*)
        *(COMMON)
        . = ALIGN(4);
        _ebss = . ;
        _ezero = .;
    } > ram

    /* stack section */
    .stack (NOLOAD):
    {
        . = ALIGN(8);
         _sstack = .;
        . = . + __stack_size__;
        . = ALIGN(8);
        _estack = .;
    } > ram

    . = ALIGN(4);
    _end = . ;
}

So as far as I understand, .ARM.exidx is the last section placed in ROM and .relocate will be placed in RAM (from 0x20000000 onward) based on the regions and MEMORY declarations, so my three bytes should be somewhere between those two.

Then, as to HOW, I've tried this example http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0803a/BABDJCAA.html but I don't see it reflected in my .bin or .hex files. I'm guessing it only allocates the memory and doesn't actually load anything as it's merely a variable. I've also found things like this gcc linker description file force symbol to be at specific address , but I don't since it's not actual code I'm trying to load to a specific address, I don't see how I can use this method.

I'm still playing around with manipulating the linker script and seeing what I can achieve, but any help would be greatly appreciated.

If any further information is required, please ask and I will provide. (Or if I need to change the title or tags for better hits.)

2
I don't know how gcc handles this, but with other toolchains there hasn't been much of a difference between functions and variables. So usually placement is done the same way.user694733
The best reference for this information is The ld Manual (sourceware). In your case with embedded systems or with assembly itself, knowing ld is every bit as important as knowing the compiler itself. Take a look and see if you can narrow down your question a bit.David C. Rankin
Thanks, I will have a look there.kowalski5233

2 Answers

1
votes

The address of things is determined at the linker stage and for the linker everything is just symbols. So while your gcc linker description file force symbol to be at specific address give an example for a function that equaly works for data. Define your version and put it in a seperate "version" section in the C code. Then the linker can place that section somewhere.

Now the hard part is to get it to the end of the rom, which means specifying a start address just low enough that the section will end at the end of rom. Making it a fixed size (your 3 Bytes) makes that simpler. Look how ram_end is defined in the linker script. Do the same for rom_version_start but with rom instead of ram and use that to set the start of the "version" section using '. = rom_version_start;'

1
votes

So, for those who land here looking for answers, this is the more detailed example of what I did.

In main.c I defined a prototype to first get the example working, after which I tried getting an array going:

void jver(void) __attribute__((section(".jver")));

static uint8_t jvar[10] __attribute__((section(".jvar"))) = {0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39};

void jver(void)
{
    //static uint8_t sbuf[10] = {0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39};
    jamba_TxBuff(jvar, 10);

}

Then, in the linker script, after the last section is placed in the 'rom' region, I add my things that I want to add in the 'rom' region:

_jverstart = 0x3FF0;
.jver : AT (_jverstart)
{
    *(.jver)
} > rom


_jvarstart = 0x4100;
.jvar : AT (_jvarstart)
{
    *(.jvar)
} > rom

Note here I had to tweak the example, just making the pointer equal to a value didn't work. I had to create a variable and use the AT function. During my testing I added the 'rom' region and haven't checked if it still works if I remove it.

When I compile, I get the following result:

:103FF00008B502480A21024B984708BDA02D0000D1
:04400000310D00007E
:0C410000303132333435363738390000A6
:0400000300001A657A
:00000001FF

From the last code to 0x3FF0 the .bin file fills with 0x00, then the couple of bytes of code for my jver(void) function (which is probably just a call to the jamba_TxBuff function), then fills 0x00's all the way to 0x4100 where "0123456789" is located. BOOM!!! All I'm going to add now is the 'FILL' function in the linker script to fill with 0xFF instead of 0x00.

Thanks again for the references and advice.