Reputation: 199
For my embedded application on Atmel SAM4E16C i need to place an array with firmware information at the end of the .hex file. I'm using Atmel Studio 7 with GCC.
I've already done this for Atmega168PB but somehow it doesn't work for this project.
OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
OUTPUT_ARCH(arm)
SEARCH_DIR(.)
/* Memory Spaces Definitions */
MEMORY
{
rom (rx) : ORIGIN = 0x00420000, LENGTH = 0x000E0000 /* changed to leave space for 128KB Bootloader -> was ORIGIN = 0x00400000, LENGTH = 0x00100000 */
ram (rwx) : ORIGIN = 0x20000000, LENGTH = 0x00020000
}
/* The stack size used by the application. NOTE: you need to adjust according to your application. */
__stack_size__ = DEFINED(__stack_size__) ? __stack_size__ : 0x3000;
__ram_end__ = ORIGIN(ram) + LENGTH(ram) - 4;
/* Firmware Info - 8 Bytes long at the end of ROM */
__FWInfo_start__ = ORIGIN(rom) + LENGTH(rom) - 8;
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 = . ;
/* 8 Byte Firmware Info Section */
.FWInfo : AT (__FWInfo_start__)
{
*(.FWInfo)
} > rom
}
This is the linker script i'm using. I've added the __FWInfo_start__ and the .FWInfo section.
In my application i tried to define the firmware info block with attribute section .FWInfo but i'm unable to locate the data in my .hex file.
#define SIZE_OF_FWINFO 8
const uint8_t nFirmwareInfoBlock[SIZE_OF_FWINFO] __attribute__((section(".FWInfo"))) = {
0xff, // reserved for future
0xff, // reserved for future
DEVICE_TYPE, // DeviceType
BUILD_NR, // BuildNr of Firmware
VERSION_MINOR, // VersionMinor of Firmware
VERSION_MAJOR, // VersionMajor of Firmware
0xFF, // Checksum
0xFF // Checksum
};
I hope someone can help me why this isn't working. Thanks in advance.
EDIT: Here are the entries in the .map file:
.data 0x00000000 0x0 src/main.o
.FWInfo 0x00000000 0x8 src/main.o
.debug_macro 0x00000000 0x8b0 src/main.o
and..
*fill* 0x200133b0 0x3000
0x200163b0 . = ALIGN (0x8)
0x200163b0 _estack = .
0x200163b0 . = ALIGN (0x4)
0x200163b0 _end = .
.FWInfo
*(.FWInfo)
OUTPUT(Dali4Net.elf elf32-littlearm)
as far as i can read from the context in the second block there should be an address written after the .FWInfo or?
Upvotes: 4
Views: 1968
Reputation: 286
I know this post is pretty old, but anyways...
As already stated, the use of KEEP directive to keep the section even if unused may be needed, or just ensuring it will always be kept no matter what.
More than that, I wanted to do something similar for a STM32. It seems that the order of sections is important too (even when an address is explicitly given). I had to place my extra section just before the start of RAM sections.
Here, you have sections that goes to ROM > rom
and RAM > ram
. You may also have some other section of initialized datas which are denoted >RAM AT> FLASH
(for STM32 gcc). In this example, it seems that it's denoted as .relocate : AT (_etext)
.
Anyways, if it works the same way, you should place your own section just before the .bss
which is the start of uninitialized datas (first section related to RAM only):
...
_erelocate = .;
} > ram
/* 8 Byte Firmware Info Section */
.FWInfo : AT (__FWInfo_start__)
{
KEEP(*(.FWInfo))
} > rom
/* .bss section which is used for uninitialized data */
.bss (NOLOAD) :
...
If it still doesn't work as expected, you may try to place this section before the .relocate : AT (_etext)
section; which is located after the latest mentioned rom section, but as this section is relocated from ROM to RAM even if ambigous in the notation, I think the first guess should work.
Upvotes: 0
Reputation: 93476
From GCC variable attribute documentation:
used
This attribute, attached to a variable with static storage, means that the variable must be emitted even if it appears that the variable is not referenced.
So to prevent the unused data being removed by the linker, use:
const uint8_t nFirmwareInfoBlock[SIZE_OF_FWINFO]
__attribute__((used,section(".FWInfo"))) =
{ ... } ;
An alternative but perhaps less attractive solution is to declare the array volatile
and then perform a dummy read i.e.:
const uint8_t nFirmwareInfoBlock[SIZE_OF_FWINFO]
__attribute__((section(".FWInfo"))) =
{ ... } ;
int main()
{
uint8_t dummy = nFirmwareInfoBlock[0] ;
...
}
The third method is to avoid toolchain dependencies altogether and to use the excellent if somewhat arcane SRecord utility to patch the data directly to the hex file as a post-build operation. This has the advantage of being tool-chain independent, but you will need a step perhaps to generate the data to be patched in, but writing a generator for that is probably trivial.
Upvotes: 1
Reputation: 1910
From my experience in Atmel studio 6 and 8bit AVR core:
define the section .FWInfo
(I do it without makefile sorry)
And doing
const uint8_t nFirmwareInfoBlock[SIZE_OF_FWINFO]
__attribute__((used,section(".FWInfo"))) =
{ ... } ;
is not enough, because only the compiler will keep the variable. Then the linker may discard it if it is not used. Of course a simple reference is enough to keep it but if it only used in the bootloader for example you can define a linker flag.
Again I did it graphically (sorry).
Upvotes: 0