Tell the linker to avoid a region - linker

Esteemed Colleagues!
If I want the linker to not place anything into an area of memory, I can simply omit the region from the "MEMORY" section in the linker script file. I could also put a comment in there to make it clear what I'm doing. Perhaps like this:
MEMORY
{
...
good_one : ORIGIN = 0x00400000, LENGTH = 0x00010000 /* 64 kiB */
/* Avoid the "special" area 0x00410000 -> 0x00480000 */
good_two : ORIGIN = 0x00480000, LENGTH = 0x00010000 /* 64 kiB */
...
}
Instead, is there a way that I could define a memory region, but attach attributes to it to keep the linker from placing anything in there? Perhaps like this:
MEMORY
{
...
good_one : ORIGIN = 0x00400000, LENGTH = 0x00010000 /* 64 kiB */
avoid (!rwiax) : ORIGIN = 0x00410000, LENGTH = 0x00070000 /* 448 kiB */
good_two : ORIGIN = 0x00480000, LENGTH = 0x00010000 /* 64 kiB */
...
}
Could I also create some symbols (and "KEEP" them) in the linker script file that would show up in the ELF to clearly indicate what is going on? Perhaps like this:
SECTIONS
{
...
avoid 0x00410000 (NOLOAD) :
{
_avoid_region_start = . ; /* KEEP() ??? */
. = . + 0x00070000 ;
_avoid_region_end = . ; /* KEEP() ??? */
} > avoid ;
...
I'm using a wide variety of GNU "ld" versions (from 2.20.51 through 2.36.1), cross-compiling to a broad set of target architectures (riscv32/64, aarch32/64, etc...), if that matters...

Related

ARM GCC force variable to be loaded in flash even if unused

I have a AES variable that would like to force-load it to specific address in the bootloader application (Cortex-M7 based app). ARM GCC 10.3.1 compiler. it will be used in the later stage, by the main app. It is considered UNUSED in the bootloader app.
Linker script memory definition
/* Specify the memory areas */
MEMORY
{
FLASH_FSBL (rx) : ORIGIN = 0x08000000, LENGTH = 126K
FLASH_FSBL_AESKEY (rx) : ORIGIN = 0x0801F900, LENGTH = 256
}
I defined my own region for AES key
/* Section for AES key */
.aes_key_sect :
{
PROVIDE(region_aes_key_start = .);
KEEP (*(.aes_key_sect))
PROVIDE(region_aes_key_end = .);
} >FLASH_FSBL_AESKEY
In boot code, I have:
#define LOCATION_AES_KEY __attribute__((section(".aes_key_sect")))
LOCATION_AES_KEY static uint8_t aeskey_const[] = {
#include "../../../aes256_key.txt"
};
When compiled, I see that it was not included in the build
FLASH_FSBL: 24444 B 126 KB 18.95%
FLASH_FSBL_AESKEY: 0 GB 0 GB
The only way to do it so far, is to dummy-access it in the init function, like so:
void init() {
const volatile uint8_t* k1 = aeskey_const;
(void)*k1;
}
When compiled, I see it is part of build:
Memory region Used Size Region Size %age Used
FLASH_FSBL: 24452 B 126 KB 18.95%
FLASH_FSBL_AESKEY: 32 B 0 GB
is there no better way than this? if I change definition to const, there is no effect.
GCC provides special attributes not defined in the C standards, as you apparently know. One such attribute is "used" that tells the compiler the object is in fact needed and prevents it from removing it. The usage in your case would be :
__attribute__((__used__)) LOCATION_AES_KEY static uint8_t aeskey_const[] = {
#include "../../../aes256_key.txt"
};

Is writing to a section not defined in linker file allowed?

In my linker file, I have the following memory sections.
MEMORY
{
ram (rwx) : ORIGIN = 0x20000000, LENGTH = 64k
mram (rwx) : ORIGIN = 0xA0000010, LENGTH = 1M
}
The actual address of the mram peripheral starts at 0xA0000000. In a C file I can write to a specific memory address as
(*(uint32_t *)(void *)0xA0000000) = 0xaabbccdd;
Will this cause any problems?
The space specified in the linker script defines where and what the linker can locate in the defined memory regions. It will not locate anything to the "hole" you have left at 0xA0000000 to 0xA000000F because it is not aware of it.
In that sense it is "safe" in that the linker will not attempt to use that space. It is entirely in the control of your code - you have taken responsibility for that region by not giving it to the linker. And indeed the statement:
(*(uint32_t *)(void *)0xA0000000) = 0xaabbccdd;
will write a 32 bit value to that location. The point is neither the compiler nor the linker will prevent you from doing what you will in that region.
What is less plausible is LENGTH = 1M. That would make your mram 0x100010 bytes long (i.e. 1M + 0x10). That is a problem because the linker is free to locate objects in the region 0x100000 to 0x10000F. The consequences of that depend on your hardware, but quite possibly it will wrap into the region 0x100000 to 0x10000F that you have attempted to hide from the linker. I would imagine that you need LENGTH = 1M - 0x10 or LENGTH = 0x0FFFF0.
Now while you can absolve the linker from managing that region in order to manage it in your code, it may not be the best approach. It would be better to create a linker symbol at the required absolute address.
So given:
MEMORY
{
ram (rwx) : ORIGIN = 0x20000000, LENGTH = 64k
mram (rwx) : ORIGIN = 0xA0000000, LENGTH = 1M
}
Your would create a linker symbol in mram:
SECTIONS
{
...
.mram :
{
reserved_mram = 0 ; /* address is zero offset from .mram1 */
. += 0x10 ; /* create 16 byte "hole" at address reserved_mram */
... /* other mram linker assignment follows */
} < mram
...
}
Then in your code you should be able to declare:
extern uint32_t reserved_mram[] ; // 4 x 32-bit word array.
And through reserved_mram you can access the memory at 0xA00000000 symbolically and the code is always in sync with the linker script so you can relocate the space easily without introducing a conflict.
Of course there is no bounds checking and no size information - you still need to confine your access to reserved_mram[0] to reserved_mram[3].
You might alternatively create a separate symbol for each location (with meaningful names specific to your application):
.mram :
{
reserved_mram1 = 0 ;
. += 4 ;
reserved_mram2 = . ;
. += 4 ;
reserved_mram3 = . ;
. += 4 ;
reserved_mram4 = . ;
. += 4 ;
... /* other mram linker usage follows */
} < mram
Then in your code:
extern uint32_t reserved_mram1 ;
extern uint32_t reserved_mram2 ;
extern uint32_t reserved_mram3 ;
extern uint32_t reserved_mram4 ;
Another alternative; you might create an independent section for the region, then create variables within it using __attribute__((section(.xxx))) directives in the code. e.g:
MEMORY
{
ram (rwx) : ORIGIN = 0x20000000, LENGTH = 64k
mram1 (rwx) : ORIGIN = 0xA0000000, LENGTH = 0x10
mram2 (rwx) : ORIGIN = 0xA0000010, LENGTH = 0xFFFF0
}
SECTIONS
{
...
.mram1 :
{
*(.mram1)
} < mram1
...
}
Then in your code:
uint32_t my_mram_data __attribute__ ((section (".mram1"))) ;
The variable my_mram_data will be created somewhare in .mram1, but the linker decides where. The advantage here is that you can create arbitrary variables in the code without modifying the linker script, and if you attempt to allocate to .mram1 more data than is available you will get a linker error.
Note that linker script syntax is arcane and varies between linkers - I am unassuming this relates to the GNU linker? But my linker foo is strictly on demand (i.e. I figure it out when I need to) and I make no claim that any of the above is complete or correct, or even the only possible solutions - regard it as illustrative, and refer to the linker documentation for accurate information.

using the same linker ld script file target for two files

lets assume I have a memory allocation that looks like this:
MEMORY
{
firstfile : ORIGIN = 0x00000000, LENGTH = 0x2000
secondfile : ORIGIN = 0x00002000, LENGTH = 0x6000
}
now I want to use the same ld script for two different files. 'firstfile.c' and 'secondfile.c'
how to I make firstfile entire allocation go under 'firstfile' section, and the second file under 'secondfile' section?
currently .text all goes under secondfile section.
using special attribute section on each of the functions in firstfile.c doesnt help
In your linker script fragment firstfile and secondfile are MEMORY regions not SECTIONS, so the section attributes will (I guess) be ignored because the sections do not exist.
You must create the MEMORY regions, in which you place SECTIONS, then you assign sections defined in the object code to sections declared in the linker script. Note that it is the object code that is located, not the source file - the linker knows nothing about source files:
Something like:
MEMORY
{
FIRST_MEMORY : ORIGIN = 0x00000000, LENGTH = 0x2000
SECOND_MEMORY : ORIGIN = 0x00002000, LENGTH = 0x6000
}
SECTIONS
{
.firstsection :
{
. = ALIGN(4);
*firstfile.o (.text .text*) /* Locate firstfile text sections here */
} > FIRST_MEMORY
.secondsection :
{
. = ALIGN(4);
*secondfile.o (.text .text*) /* Locate secondfile text sections here */
} > SECOND_MEMORY
}
You can then locate any number of modules explicitly to each section.
You might want a default location to place modules not explicitly located. In which case you should add:
*(.text) /* .text sections (code) */
*(.text*) /* .text* sections (code) */
to one of the sections (or create a separate default .text section).
Also if you add:
*(.firstsection*) /* Locate anything with firstsection attribute here */
or
*(.secondsection*) /* Locate anything with secondsection attribute here */
to the respective sections you can use __section__ attributes in the code to locate specific functions (or data) to to these sections as you attempted previously. But locating an entire module is preferable as it does not require code modification and maintenance.

Alignment in linker scripts

I am looking at trezor's bootloader linker script:
/* TREZORv2 bootloader linker script */
ENTRY(reset_handler)
MEMORY {
FLASH (rx) : ORIGIN = 0x08020000, LENGTH = 128K
CCMRAM (wal) : ORIGIN = 0x10000000, LENGTH = 64K
SRAM (wal) : ORIGIN = 0x20000000, LENGTH = 192K
}
main_stack_base = ORIGIN(CCMRAM) + LENGTH(CCMRAM); /* 8-byte aligned full descending stack */
/* used by the startup code to populate variables used by the C code */
data_lma = LOADADDR(.data);
data_vma = ADDR(.data);
data_size = SIZEOF(.data);
/* used by the startup code to wipe memory */
ccmram_start = ORIGIN(CCMRAM);
ccmram_end = ORIGIN(CCMRAM) + LENGTH(CCMRAM);
/* used by the startup code to wipe memory */
sram_start = ORIGIN(SRAM);
sram_end = ORIGIN(SRAM) + LENGTH(SRAM);
_codelen = SIZEOF(.flash) + SIZEOF(.data);
SECTIONS {
.header : ALIGN(4) {
KEEP(*(.header));
} >FLASH AT>FLASH
.flash : ALIGN(512) {
KEEP(*(.vector_table));
. = ALIGN(4);
*(.text*);
. = ALIGN(4);
*(.rodata*);
. = ALIGN(512);
} >FLASH AT>FLASH
.data : ALIGN(4) {
*(.data*);
. = ALIGN(512);
} >CCMRAM AT>FLASH
.bss : ALIGN(4) {
*(.bss*);
. = ALIGN(4);
} >CCMRAM
.stack : ALIGN(8) {
. = 4K; /* this acts as a build time assertion that at least this much memory is available for stack use */
} >CCMRAM
}
It can be found here.
I understand that the code needs to be 32bit ( ALIGN(4) ) aligned, because the ARM processor can crash if it tries to access unaligned address, but I do not understand why the stack alignment is 8 bytes and furthermore why the hell do you need to waste(?) 512 bytes for alignment of the flash section?!
I would like to understand how the alignment is decided when writing a linker script.
Thank you in advance for your answers!
EDIT:
I think i answered my own question:
1. .flash section:
It is aligned like that, because the vector table, that is inside it always needs to be "32-word aligned". This can also be seen be the case in Trezor's boardloader linker script. As you can see the vector table is 512 byte (4 x 32-word) aligned.
2. .stack section:
According to ARM's own documentation the stack section needs to always be 8 byte aligned.
P.S. Of course if this is not the case, please correct me.
Okay, so since cooperised confirmed my theory I can now close this question.
1. .flash section:
It is aligned like that, because the vector table, that is inside it always needs to be "32-word aligned". This can also be seen be the case in Trezor's boardloader linker script. As you can see the vector table is 512 byte (4 x 32-word) aligned.
2. .stack section:
According to ARM's own documentation the stack section needs to always be 8 byte aligned.
Thank you cooperised for the confirmation!

Why won't debug linker file compile (ld:200 cannot move location counter backwards)

Now before you you tell me my program is using too much memory...
I know what "cannot move location counter backwards means"...
here is the real problem...
I have a linker file that WILL compile no problem.
call this linker file release version....
MEMORY
{
/* SOFTCONSOLE FLASH USE: microsemi-smartfusion2-envm */
rom (rx) : ORIGIN = 0x20000000, LENGTH = 35k
/* SmartFusion2 internal eSRAM */
ram (rwx) : ORIGIN = 0x20008C00, LENGTH = 29k
}
RAM_START_ADDRESS = 0x20008C00; /* Must be the same value MEMORY region ram ORIGIN above. */
RAM_SIZE = 29k; /* Must be the same value MEMORY region ram LENGTH above. */
MAIN_STACK_SIZE = 11k; /* Cortex main stack size. */
MIN_SIZE_HEAP = 512; /* needs to be calculated for your application */
notice the entire space occupied is 64k... this compiles
but when I try to use the following linker file (debug); I get the location counter error
MEMORY
{
/* SmartFusion2 internal eSRAM */
ram (rwx) : ORIGIN = 0x20000000, LENGTH = 64k
}
RAM_START_ADDRESS = 0x20000000; /* Must be the same value MEMORY region ram ORIGIN above. */
RAM_SIZE = 64k; /* Must be the same value MEMORY region ram LENGTH above. */
MAIN_STACK_SIZE = 11k; /* Cortex main stack size. */
MIN_SIZE_HEAP = 512; /* needs to be calculated for your application */
THE ONLY differences between the two linker files are show plus anywhere there is a >rom or >ram AT>rom directive in the release version, it is replace by >ram in the debug version...
I am using the same exact optimize and debugging flags
When I try to link the debug version i get the following error
ld:200 cannot move location counter backwards (from 20011d80 to 2000d400)
Anyone have any ideas???

Resources