Linker script .relocate section's first symbol, _srelocate, is incorrect (GCC Bug?)
c, ld, linker, linker-scripts
Solution
I had long ago discovered the answer to my own question but it seems that it is still a little unclear in my edits so allow me to formalize it here.
The issue is that due to a bug in GCC the linker inserts an additional TMC_END symbol taking up 4 bytes in between my `_etext` and `_srelocate` symbols. This is incorrect because this symbol has nothing to do with the code that I'm writing and I'm not even using this functionality. I had discovered and explained this in my other SO question.
Workaround:
Atmel's sample code, rightly, assumes that if your code is running out of Flash memory your `_srelocate` symbol's address and `_etext` symbol's address will be different, one having an address starting with `0x004...` and the other with `0x20...`.
If your code is running out of RAM, this means that you have a bootloader that copies the relevant sections into the correct memory location, hence the `_etext` symbol's address will also reside in RAM and be equal to the `_srelocate` symbol's address.
Therefore Atmel is trying to make sure that the `_srelocate` section always resides in RAM.
Due to the bug in GCC the solution was to change the code to check if the `_srelocate` symbol's address is already in RAM, like this:
// I can't remember what #define Atmel has for SRAM so I'm just going to
// use this one for the purposes of my example.
#define SRAM_START_ADDRESS 0x20000000
void Reset_Handler(void)
{
uint32_t *pSrc, *pDest, Size;
/* Initialize the relocate segment */
pSrc = &_etext; // Could either be in Flash or SRAM depending on your configuration.
pDest = &_srelocate; // Always in SRAM or there's something strange going on...
if ((intptr_t)pSrc < (intptr_t)SRAM_START_ADDRESS) { // <<<< Changed code here <<<<
// We enter here only if pSrc is pointing to a location in Flash.
// If that's the case, we need to copy the memory from Flash to SRAM.
for (; pDest < &_erelocate;) {
*pDest++ = *pSrc++;
}
}
/* Clear the zero segment */
for (pDest = &_szero; pDest < &_ezero;) {
*pDest++ = 0;
}
/* Set the vector table base address */
pSrc = (uint32_t *) & _sfixed;
SCB->VTOR = ((uint32_t) pSrc & SCB_VTOR_TBLOFF_Msk);
if (((uint32_t) pSrc >= IRAM_ADDR) && ((uint32_t) pSrc < IRAM_ADDR + IRAM_SIZE)) {
SCB->VTOR |= (1UL) << SCB_VTOR_TBLBASE_Pos;
}
/* Initialize the C library */
__libc_init_array();
/* Branch to main function */
main();
/* Infinite loop */
while (1);
}
And that worked for my situation. From what I can see this bug was fixed and the fix has since been reverted so it is unlikely that you will have it fixed in your Atmel Studio.
Problem
Problem My problem is that when I use the following script which is intended to place the Code into RAM the relocate section gets filled with bogus data. My question is: Why is the `_srelocate` symbol 4 bytes greater than the `_etext` symbol? Shouldn't they be the same? Also, if the answer to 1. is NO, shouldn't I be copying from `_etext + 4` to `_srelocate`? Background and relevant code I'm working with an Atmel ATSAM3N4X series processor (an ARM Cortex M3) and would like a little help with my linker script and `.relocate` section initialization. The reason for this is that the `_etext` symbol is 4 bytes less than the `_srelocate` symbol. The following linker script is the default script generated by Atmel Studio 6 (if you want the `ram`/`rom` symbol locations see the Apendix to the question). ``` /* ---------------------------------------------------------------------------- * SAM Software Package License * ---------------------------------------------------------------------------- * Copyright (c) 2012, Atmel Corporation * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the disclaimer below. * * Atmel's name may not be used to endorse or promote products derived from * this software without specific prior written permission. * * DISCLAIMER: 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 * 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. * ---------------------------------------------------------------------------- */ /* 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 */ } > ram . = 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 /* .ARM.exidx is sorted, so has to go in its own output section. */ PROVIDE_HIDDEN (__exidx_start = .); .ARM.exidx : { *(.ARM.exidx* .gnu.linkonce.armexidx.*) } > ram PROVIDE_HIDDEN (__exidx_end = .); . = ALIGN(4); _end = . ; } ``` I used `nm` to see what the symbol values were and I've included them below: ``` 2000115c A _etext 20001160 D _srelocate 200015c8 D _erelocate ``` Now, Atmel Studio 6 auto-generated my project and gave me a `Reset_Handler()` that copies the `.relocate` section from `_etext` to `_srelocate` and clears the `.bss` section. The project came with two linker scripts, one for FLASH based execution and one for RAM based code. The default is the FLASH based code but I changed that to the RAM based linker script (provided above), and ran into the problem. The initialization code that copies the `.relocate` section from FLASH to RAM is also auto-generated and I haven't changed it when I changed linker scripts. It looks like this: ``` void Reset_Handler(void) { uint32_t *pSrc, *pDest, Size; /* Initialize the relocate segment */ pSrc = &_etext; pDest = &_srelocate; if (pSrc != pDest) { for (; pDest < &_erelocate;) { *pDest++ = *pSrc++; } } /* Clear the zero segment */ for (pDest = &_szero; pDest < &_ezero;) { *pDest++ = 0; } /* Set the vector table base address */ pSrc = (uint32_t *) & _sfixed; SCB->VTOR = ((uint32_t) pSrc & SCB_VTOR_TBLOFF_Msk); if (((uint32_t) pSrc >= IRAM_ADDR) && ((uint32_t) pSrc < IRAM_ADDR + IRAM_SIZE)) { SCB->VTOR |= (1UL) << SCB_VTOR_TBLBASE_Pos; } /* Initialize the C library */ __libc_init_array(); /* Branch to main function */ main(); /* Infinite loop */ while (1); } ``` Edit 1: Using `objdump -t CodeFile.elf > CodeFile.symbols`, I have found this symbol at the start of my `.relocate` section which seems to suggest that `_srelocate` doesn't truly point to the beginning of `.relocate`. ``` 2000115c g O .relocate 00000000 .hidden __TMC_END__ ``` What is this symbol? I've looked it up and found this bug in GCC 4.7 but can't figure out if it is fixed or not in my version. My compiler is `arm-none-eabi-gcc` and states its version as `4.7.0`... The linker is `arm-none-eabi-ld` and its version is `2.22`. Edit 2: I have done some research about this and it is recorded in this related SO question that I asked. Please look at it as well, as it explains that the problem is a GCC bug. Apendix Memory Layout for Linker Scripts ``` OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm") OUTPUT_ARCH(arm) SEARCH_DIR(.) /* Memory Spaces Definitions */ MEMORY { rom (rx) : ORIGIN = 0x00400000, LENGTH = 0x00040000 /* flash, 256K */ ram (rwx) : ORIGIN = 0x20000000, LENGTH = 0x00006000 /* sram, 24K */ } /* The stack size used by the application. NOTE: you need to adjust */ STACK_SIZE = DEFINED(STACK_SIZE) ? STACK_SIZE : 0x800 ; ```