I'm working with an IAR project where there are ILINK Configuration Files (.icf) for both a bootloader and the main application. Each file defines the __ICFEDIT_intvec_start__ symbol and later places it referencing their respective .intvec sections (there are 2 cstartup.s files, each with their own .intvec section):
Bootloader .icf:
define symbol __ICFEDIT_intvec_start__ = 0x18000000;
...
place at address mem:__ICFEDIT_intvec_start__ { readonly section .intvec };
Application .icf:
define symbol __ICFEDIT_intvec_start__ = 0x18080000;
...
place at address mem:__ICFEDIT_intvec_start__ { readonly section .intvec };
From what I understand, each of these .icf files are used to generate their own .map files. Seeing as how each of these reference two different sections (that share the same name), I'm confused as to why only the .map file for the bootloader references the .intvec section:
Bootloader .map:
*******************************************************************************
*** PLACEMENT SUMMARY
***
"A1": place at 0x18000000 { ro section .intvec };
"P1": place in [from 0x18000040 to 0x1807ffff] { ro };
"P2": place in [from 0x20020000 to 0x209fffff] {
rw, block CSTACK, block SVC_STACK, block IRQ_STACK, block FIQ_STACK,
block UND_STACK, block ABT_STACK, block HEAP };
Section Kind Address Size Object
------- ---- ------- ---- ------
"A1": 0x3c
.intvec ro code 0x18000000 0x3c cstartup.o [1]
- 0x1800003c 0x3c
...
Application .map:
*******************************************************************************
*** PLACEMENT SUMMARY
***
"INT_VEC_RAM":
place at 0x20020000 { section .intvec_RAM };
"ROM": place in [from 0x18080040 to 0x1bffffff] {
ro section .cstartup, block ROM_CONTENT };
"RAM": place in [from 0x20020040 to 0x209fffff] { block RAM_CONTENT };
Section Kind Address Size Object
------- ---- ------- ---- ------
"ROM": 0x2405e0
ROM_CONTENT 0x18080040 0x2405e0 <Block>
.text ro code 0x18080040 0x104 access.o [8]
.text ro code 0x18080144 0x18c cstartup.o [1]
What's happening here? I'm just starting to understand the role of the linker, so I'm fairly new to all of this.
Also, to further clarify, the two .intvec sections comprise the same interrupt vector table:
SECTION .intvec:CODE:NOROOT(2)
...
__vector: ; Make this a DATA label, so that stack usage
; analysis doesn't consider it an uncalled fun
ARM
; All default exception handlers (except reset) are
; defined as weak symbol definitions.
; If a handler is defined by the application it will take precedence.
LDR PC,Reset_Addr ; Reset
LDR PC,Undefined_Addr ; Undefined instructions
LDR PC,SWI_Addr ; Software interrupt (SWI/SVC)
LDR PC,Prefetch_Addr ; Prefetch abort
LDR PC,Abort_Addr ; Data abort
DCD 0 ; RESERVED
LDR PC,IRQ_Addr ; IRQ
LDR PC,FIQ_Addr ; FIQ
It seems that the answer is a lot more obvious than I thought. According to the section "Linking—an overview" in "IAR C/C++ Development Guide", IAR's linker software ILINK ignores duplicate sections. Thus, if a section is already referenced in one binary object or ILINK configuration file (ICF), all other references to it are ignored.
In this project, since the bootloader takes precedence (is loaded and flashed before the application [defined in the project's .board files; more info here]), the application code's .intvec is seen as a duplicate and is thus ignored/discarded.
Related
I'm trying to write an mbr-manager in GNU-assembler, everything works great except that I'm doing an .include "print_routines" and I want that to change but I don't know how I could write a linker script to do the same thing as an include.
When writing my mbr-manager, I know that after the 0x1BE bytes there as the 4 partition entries, so basically all of my code needs to be from 0 --> 446 bytes. Also my code should be at 0x600, as the mbr reallocate itself
Here is what I have :
2 files named "mbr.s" and "print_16.s"
In mbr.s all of my code is under the section ".mbr"
In print_16.s all of my code is under the section ".utils"
My question is then the following:
How can I write a linker script to have the first 446 bytes of the .mbr section, followed by the .utils section and finally the rest of the .mbr, that is from 446 to 512 ?
Basically I should have something like this :
mbr.s
.intel_syntax noprefix
.code16
.section .mbr, "ax
_start:
init code
...
// Here, we are at the 0x1BEth byte
// !!! This is were I want to place my "print_routines.s" code, that is in between the .mbr section and before the 0x1BEth byte
. = _start + 0x1BE
/* Here are the partition entries, basically I souldn't touch this area*/
.word 0x55AA // Magic word, this is at the 512th byte
Here is what I tried so far but the linker says that my sections overlaps.. I'm out of ideas
SECTIONS
{
.mbr 0x0600 : AT(0)
{
mbr_start = .;
*(.mbr)
mbr_end = .;
}
.utils : AT (0x1BE)
{
*(.utils)
}
}
I am coding a bootloader for Nucleo-F429ZI. I have two different STM32 projects, one for the bootloader itself and an application to jump from the bootloader.
Linker script for bootloader
MEMORY
{
CCMRAM (xrw) : ORIGIN = 0x10000000, LENGTH = 64K
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 32K
FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 32K
}
Linker script for app
_estack = ORIGIN(RAM) + LENGTH(RAM);
MEMORY
{
CCMRAM (xrw) : ORIGIN = 0x10000000, LENGTH = 64K
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 192K
FLASH (rx) : ORIGIN = 0x8008000, LENGTH = 64K
}
I did not forget to set the flash offset of the app.
system_stm32f4xx.c (in the app project)
#define VECT_TAB_BASE_ADDRESS FLASH_BASE // 0x8000000
#define VECT_TAB_OFFSET 0x00008000U
The tutorial of STMicroelectronics about bootloaders has the following code to jump
main.c (in bootloader project)
#define FLASH_APP_ADDR 0x8008000
typedef void (*pFunction)(void);
uint32_t JumpAddress;
pFunction Jump_To_Application;
void go2APP(void)
{
JumpAddress = *(uint32_t*)(FLASH_APP_ADDR + 4);
Jump_To_Application = (pFunction) JumpAddress;
__set_MSP(*(uint32_t*)FLASH_APP_ADDR); // in cmsis_gcc.h
Jump_To_Application();
}
cmsis_gcc.h (in bootloader project)
__STATIC_FORCEINLINE void __set_MSP(uint32_t topOfMainStack)
{
__ASM volatile ("MSR msp, %0" : : "r" (topOfMainStack) : );
}
As you can see, __set_MSP function sets the main stack pointer before jumping to FLASH_APP_ADDR + 4.
I found the memory location of the target place by debugging. FLASH_APP_ADDR + 4 caused to run Reset_Handler function of app project. Lets see what will be executed.
startup_stm32f429zitx.c (in the app project)
.section .text.Reset_Handler
.weak Reset_Handler
.type Reset_Handler, %function
Reset_Handler:
ldr sp, =_estack /* set stack pointer */
/* Copy the data segment initializers from flash to SRAM */
ldr r0, =_sdata
ldr r1, =_edata
ldr r2, =_sidata
movs r3, #0
b LoopCopyDataInit
First thing of what Reset_Handler does is setting the stack pointer. _estack was defined in linker script.
If Reset_Handler is setting stack pointer, why did we call the __set_MSP function? I remove the function __set_MSP and bootloding process is still working. However I examined some other bootloader codes and found the exact same logic.
I tried what i have said and could not find an explanation.
Cortex-M core the loads SP register with initial value from address FLASH_BASE+0 during boot sequence. Then jumps to the code entry point (Reset vector) from address FLASH_BASE+4. Any bootloader code mimics core behaviour. Note, that FLASH_BASE here is not necessarily actual flash base, but an abstract value, that depends on the used processor, and it's settings.
Provided Reset_Handler code loads the sp register with __estack (Main stack top) value, but it doesn't have to! Bootloader can not expect the main program to do it, but has perform the same boot sequence as the core after reset. This way the main code doesn't have to rely on knowing, who started it - core, bootloader, jtag, or something else.
I've seen startup code, that doesn't load SP, but disables interrupts with the first instruction. Or startup code, written in C, which could use stack with the first instruction.
The real question here could be: Why this startup code loads SP if it is already loaded? But perhaps it should be forwarded to the original code author.
Let's see what's happening line by line.
JumpAddress = *(uint32_t*)(FLASH_APP_ADDR + 4);
Okay, so we take FLASH_APP_ADDR, add 1 word to it, call it a pointer to a word, dereference it. So it's the content of 0x8008004 (which is the one word after start of the vector table - list of interrupt handler pointers). You can find it in the vector table in reference manual. Here is reference manual for your MCU. Page 375
Next,
Jump_To_Application = (pFunction) JumpAddress;
Okay, so we treat reset handler address as a void function(void).
Eventually, you get to the stack
__set_MSP(*(uint32_t*)FLASH_APP_ADDR);
This function, as we see from its source code, simply sets main stack pointer to its argument. The argument is take vector table address, treat it as a pointer to a word, dereference it. So it's the first word of that vector table. And the first word of the vector table is the main stack pointer auto-loaded after power on. By definition of the vector table. You reset the stack to cold boot value, same value as the first word of your Flash. Your bootloader has used some stack until this point, but it won't be needed anymore, and the bootloader function will never return and free that stack, so you just reset stack to its initial value for your program. It will reuse all stack used by the bootloader.
So right now you've reset the stack pointer and you assigned reset handler to the function you call. And then you, well, call it.
Your vector table and the program that the bootloader starts are two different entities in memory. If you don't need to remap the interrupt handlers at runtime, don't move the vector table. It will stay at the beginning of the flash and will lead to the default interrupt handlers. Just make sure the address you execute from contains executable code and you run it from the start (well, if you don't, you will hardfault).
I have a new standard c++ project on an imx rt 1024 (an nxp chip), in which I try to move my vector table to SRAM. It fails, depending on a change I apply in the linker script.
The project is a new project from scratch created by MCUxpresso. I am not looking for answers MCUxpresso related, or c/c++/startup code related. I only want to properly understand the consequences of my changed linker script I show below.
The part that works
My starting point is a small program on my evk board, using a simple FreeRTOS task to blink a led. This works fine, when I put my vector table in flash.
linker script:
/* Not relevant for this question, other than showing there is something
written to flash before my vector table, harmless I think, but didn't want to leave
out of this question
*/
.boot_hdr : ALIGN(4)
{
__boot_hdr_start__ = ABSOLUTE(.) ;
KEEP(*(.boot_hdr.conf))
. = 0x1000 ;
KEEP(*(.boot_hdr.ivt))
. = 0x1020 ;
KEEP(*(.boot_hdr.boot_data))
. = 0x1030 ;
KEEP(*(.boot_hdr.dcd_data))
__boot_hdr_end__ = ABSOLUTE(.) ;
. = 0x2000 ;
} >PROGRAM_FLASH
/*
Here I write my vector table to flash
*/
.vector : ALIGN(4)
{
__vector_table_flash_start__ = ADDR(.vector) ;
__vector_table_itc_start__ = LOADADDR(.vector) ;
KEEP(*(.isr_vector))
__vector_table_flash_end__ = ABSOLUTE(.) ;
. = ALIGN(4) ;
} >PROGRAM_FLASH
Disassembled code for vector table
Disassembled code of reset handler
Note: 0x600022e5 corresponds to 0x600022e4, this has something to do with arm .thumb. I don't exactly know how that works tbh.
When I run this app, it runs fine. If I set a breakpoint in the ResetHandler it breaks and I can step through the startup code and jump to main. When I let the program run, my led will blink every second.
The part which fails
I changed my linker script to put my vector table in SRAM as follows
.vector : ALIGN(4)
{
__vector_table_flash_start__ = ADDR(.vector) ;
__vector_table_itc_start__ = LOADADDR(.vector) ;
KEEP(*(.isr_vector))
__vector_table_flash_end__ = ABSOLUTE(.) ;
. = ALIGN(4) ;
} >SRAM_ITC AT>PROGRAM_FLASH
For reference, the memory section:
MEMORY
{
PROGRAM_FLASH (rx) : ORIGIN = 0x60000000, LENGTH = 0x400000
SRAM_DTC (rwx) : ORIGIN = 0x20000000, LENGTH = 0x10000
SRAM_ITC (rwx) : ORIGIN = 0x0, LENGTH = 0x10000
SRAM_OC (rwx) : ORIGIN = 0x20200000, LENGTH = 0x20000
}
ENTRY(ResetISR)
When I upload, my program doesn't even reach the reset vector. It goes straight into the woods, and crashes somewhere outside program code.
The questions
What EXACTLY happens when I adjust my linker script with >SRAM_ITC AT>PROGRAM_FLASH?
I am pretty sure the produced elf file still contains the entire vector table starting from address 0x60002000. The >SRAM_ITC only tells the linker where certain parts of memory will end up AFTER the startup code copied all parts to their final ram location. Right? So how on earth can the initial jump to 0x60002004 (the address which holds the location of the reset handler) fail? The nxp bootloader always expects the reset vector on that location. I didn't change that. I only told the linker that the memory on that location will finally end up in SRAM. What am I misunderstanding here?
Maybe a stupid question: If I am completely wrong with my above assumptions, is there a way to see this from disassembly? I think objdump only shows the final addresses, but my debug probe will only write to flash as far as I know. So after uploading my code to my target, I still assume that stuff got written to flash, and after reset the built in bootloader will jump to 0x60002004 and set the PC to the address located at 0x60002000. Where can I see the actual blob of bytes which is programmed to flash memory?
Copying the vector table to sram from my custom bootloader solved the problem. That way the "on chip bootloader" from nxp can jump to my custom bootloader.
Before I just to my app from my custom bootloader, I copy the vector table to sram and set SCB->VTOR to the start of sram vector table.
Below is a portion of the C code I am using:
pushbutton_ISR()
{
int press;
int key_pressed;
press = *(KEYS_ptr + 3); // read the pushbutton Edge Det Register interrupt register
*(KEYS_ptr + 3) = 0; // Clear the Edge Det registers.
if (press & 0x1) { // KEY1
key_pressed = KEY1;
//sum = sum + *NEW_NUMBER;
}
else if (press & 0x2) { // KEY2
key_pressed = KEY2;
*GREEN_LEDS = *NEW_NUMBER;
sum = sum + *NEW_NUMBER;
*RED_LEDS = sum;
}
else // i.e. (press & 0x8), which is KEY3
sum = *(NEW_NUMBER); // Read the SW slider switch values; store in pattern
return;
}
The compiler compiles this fine and the code appears to run (on an Altera board) fine. However, when I change the first if statement to:
if (press & 0x1) { // KEY1
//key_pressed = KEY1;
sum = sum + *NEW_NUMBER;
}
the compiler gives the following error messages:
.../nios2-elf/bin/ld.exe: section .data loaded at [00000a00,00000e0f] overlaps section .text loaded at [00000500,00000a0f]
.../nios2-elf/bin/ld.exe: section .ctors loaded at [00000a10,00000a13] overlaps section .data loaded at [00000a00,00000e0f]
.../nios2-elf/bin/ld.exe: Z:/Projects/Altera/3215_W15_LabB/Part2/from_handout.elf: section .data vma 0xa00 overlaps previous sections
.../nios2-elf/bin/ld.exe: Z:/Projects/Altera/3215_W15_LabB/Part2/from_handout.elf: section .ctors vma 0xa10 overlaps previous sections
.../nios2-elf/bin/ld.exe: Z:/Projects/Altera/3215_W15_LabB/Part2/from_handout.elf: section .rodata vma 0xa14 overlaps previous sections
.../nios2-elf/bin/ld.exe: Z:/Projects/Altera/3215_W15_LabB/Part2/from_handout.elf: section .sdata vma 0xe10 overlaps previous sections
.../nios2-elf/bin/ld.exe: Z:/Projects/Altera/3215_W15_LabB/Part2/from_handout.elf: section .sbss vma 0xe18 overlaps previous sections
Could you please advise me about the reasons for these errors, and how to resolve them.
This has nothing to do with your code being incorrect.
These are linker errors (it even tells you ld.exe is the program complaining) about output sections overlapping. This probably means you just ran out of space, but could also mean the linker directive file your project is using has some problems.
When you add in this line, it causes the size of the compiled code to be too big for the memory area that you are loading the code into.
You can see from the first line of the linker error message that .text (the code) is loaded at 0x500, and .data (the non-zero static variables) is loaded at 0xa00. However, the .text section is so long that it is too big to fit in the space between 0x500 and 0xa00.
To fix this you will either need to:
Make your code smaller
Increase the amount of space available for .text
To do the first one, you could use -Os or similar compiler option to compile for minimum code size ; or manually rewrite your code to be smaller.
For the second one you really need to understand the hardware you are loading the code into. Is it a hardware requirement that code goes at 0x500 and data goes at 0xa00? If not, then you may be able to load the code and/or data into different addresses.
These addresses are configured in your linker script (this may be hardcoded into the makefile or it may be an actual file somewhere). Hopefully the hardware device came with documentation that explains how much memory it has and where you're allowed to load your code to.
I am writing the boot-up code for an ARM CPU. There is no internal RAM, but there is 1GB of DDRAM connected to the CPU, which is not directly accessible before initialisation. The code is stored in flash, initialises RAM, then copies itself and the data segment to RAM and continue execution there. My program is:
#define REG_BASE_BOOTUP 0xD0000000
#define INTER_REGS_BASE REG_BASE_BOOTUP
#define SDRAM_FTDLL_REG_DEFAULT_LEFT 0x887000
#define DRAM_BASE 0x0
#define SDRAM_FTDLL_CONFIG_LEFT_REG (DRAM_BASE+ 0x1484)
... //a lot of registers
void sdram_init() __attribute__((section(".text_sdram_init")));
void ram_init()
{
static volatile unsigned int* const sdram_ftdll_config_left_reg = (unsigned int*)(INTER_REGS_BASE + SDRAM_FTDLL_CONFIG_LEFT_REG);
... //a lot of registers assignments
*sdram_ftdll_config_left_reg = SDRAM_FTDLL_REG_DEFAULT_LEFT;
}
At the moment my program is not working correctly because the register values end up being linked to RAM, and at the moment the program tries to access them only the flash is usable.
How could I change my linker script or my program so that those values have their address in flash? Is there a way I can have those values in the text segment?
And actually are those defined values global or static data when they are declared at file scope?
Edit:
The object file is linked with the following linker script:
MEMORY
{
RAM (rw) : ORIGIN = 0x00001000, LENGTH = 12M-4K
ROM (rx) : ORIGIN = 0x007f1000, LENGTH = 60K
VECTOR (rx) : ORIGIN = 0x007f0000, LENGTH = 4K
}
SECTIONS
{
.startup :
{
KEEP((.text.vectors))
sdram_init.o(.sdram_init)
} > VECTOR
...
}
Disassembly from the register assignment:
*sdram_ftdll_config_left_reg = SDRAM_FTDLL_REG_DEFAULT_LEFT;
7f0068: e59f3204 ldr r3, [pc, #516] ; 7f0274 <sdram_init+0x254>
7f006c: e5932000 ldr r2, [r3]
7f0070: e59f3200 ldr r3, [pc, #512] ; 7f0278 <sdram_init+0x258>
7f0074: e5823000 str r3, [r2]
...
7f0274: 007f2304 .word 0x007f2304
7f0278: 00887000 .word 0x00887000
To answer your question directly -- #defined values are not stored in the program anywhere (besides possibly in debug sections). Macros are expanded at compile time as if you'd typed them out in the function, something like:
*((unsigned int *) 0xd0010000) = 0x800f800f;
The values do end up in the text segment, as part of your compiled code.
What's much more likely here is that there's something else you're doing wrong. Off the top of my head, my first guess would be that your stack isn't initialized properly, or is located in a memory region that isn't available yet.
There are a few options to solve this problem.
Use PC relative data access.
Use a custom linker script.
Use assembler.
Use PC relative data access
The trouble you have with this method is you must know details of how the compiler will generate code. #define register1 (volatile unsigned int *)0xd0010000UL is that this is being stored as a static variable which is loaded from the linked SDRAM address.
7f0068: ldr r3, [pc, #516] ; 7f0274 <sdram_init+0x254>
7f006c: ldr r2, [r3] ; !! This is a problem !!
7f0070: ldr r3, [pc, #512] ; 7f0278 <sdram_init+0x258>
7f0074: str r3, [r2]
...
7f0274: .word 0x007f2304 ; !! This memory doesn't exist.
7f0278: .word 0x00887000
You must do this,
void ram_init()
{
/* NO 'static', you can not do that. */
/* static */ volatile unsigned int* const sdram_reg =
(unsigned int*)(INTER_REGS_BASE + SDRAM_FTDLL_CONFIG_LEFT_REG);
*sdram_ftdll_config_left_reg = SDRAM_FTDLL_REG_DEFAULT_LEFT;
}
Or you may prefer to implement this in assembler as it is probably pretty obtuse as to what you can and can't do here. The main effect of the above C code is that every thing is calculated or PC relative. If you opt not to use a linker script, this must be the case. As Duskwuff points out, you also can have stack issues. If you have no ETB memory, etc, that you can use as a temporary stack then it probably best to code this in assembler.
Linker script
See gnu linker map... and many other question on using a linker script in this case. If you want specifics, you need to give actual addresses use by the processor. With this option you can annotate your function to specify which section it will live in. For instance,
void ram_init() __attribute__((section("FLASH")));
In this case, you would use the Gnu Linkers MEMORY statement and AT statements to put this code at the flash address where you desire it to run from.
Use assembler
Assembler gives you full control over memory use. You can garentee that no stack is used, that no non-PC relative code is generated and it will probably be faster to boot. Here is some table driven ARM assembler I have used for the case you describe, initializing an SDRAM controller.
/* Macro for table of register writes. */
.macro DCDGEN,type,addr,data
.long \type
.long \addr
.long \data
.endm
.set FTDLL_CONFIG_LEFT, 0xD0001484
sdram_init:
DCDGEN 4, FTDLL_CONFIG_LEFT, 0x887000
1:
init_sdram_bank:
adr r0,sdram_init
adr r1,1b
1:
/* Delay. */
mov r5,#0x100
2: subs r5,r5,#1
bne 2b
ldmia r0!, {r2,r3,r4} /* Load DCD entry. */
cmp r2,#1 /* byte? */
streqb r4,[r3] /* Store byte... */
strne r4,[r3] /* Store word. */
cmp r0,r1 /* table done? */
blo 1b
bx lr
/* Dump literal pool. */
.ltorg
Assembler has many benefits. You can also clear the bss section and setup the stack with simple routines. There are many on the Internet and I think you can probably code one yourself. The gnu ld script is also beneficial with assembler as you can ensure that sections like bss are aligned and a multiple of 4,8,etc. so that the clearing routine doesn't need special cases. Also, you will have to copy the code from flash to SDRAM after it is initialized. This is a fairly expensive/long running task and you can speed it up with some short assembler.