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

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.

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"
};

Linker with overlapping memory definitions

I made an ld script with the following memory layout:
MEMORY
{
FLASH (rx) : ORIGIN = 0x00027000, LENGTH = 0xC8000
RAM (rwx) : ORIGIN = 0x20003FC0, LENGTH = 0x3B040
RRAM (rwx) : ORIGIN = 0x2000F000, LENGTH = 0x01000
}
As you can see, the RRAM (from 0x2000F000 to 0x2000FFFF) portion overlaps with the RAM portion (from 0x20003FC0 to 0x2002FFF). The code compiles and links with no warnings or errors.
My question is: during normal code execution,.will the system still be able to use the memory from 0x2001000 up to 0x2002FFF? Or does this unusual layout stop the code from using anything after the end of RRAM (unless explicitly allocated to an address)?

Tell the linker to avoid a region

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...

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???

What exactly does ". = 0x7c00" in a linker script do?

What exactly does ". = 0x7c00" in a linker script do?
More specifically, when I place . = 0x7c00 at the beginning of a linker script, why doesn't the resulting output file begin with 0x7c00 = 31,744 zeros?
I understand that when a PC boots, the BIOS places the 512 byte MBR at memory address 0x7c00. However, I am confused as to how exactly the linker's location counter affects how the output file is laid out.
(For context, I'm trying to thoroughly understand the sample code from the "x86 bare metal" project. https://github.com/cirosantilli/x86-bare-metal-examples. I've included the entire linker script below for context.)
SECTIONS
{
/*
We could also pass the -Ttext 0x7C00 to as instead of doing this.
If your program does not have any memory accesses, you can omit this.
*/
. = 0x7c00;
.text :
{
__start = .;
/*
We are going to stuff everything
into a text segment for now, including data.
Who cares? Other segments only exist to appease C compilers.
*/
*(.text)
/*
Magic bytes. 0x1FE == 510.
We could add this on each Gas file separately with `.word`,
but this is the perfect place to DRY that out.
*/
. = 0x1FE;
SHORT(0xAA55)
*(.stage2)
__stage2_nsectors = ABSOLUTE((. - __start) / 512);
. = ALIGN(512);
__end = .;
__end_align_4k = ALIGN(4k);
}
}
It looks like the ". = 0x7c00" is not meaning a length but an absolute address. It reads to me as 'set the current value of the special variable "." to be the hex value 0x7c00 and then it plans to use that address as an offset later in the script like with the . = ALIGN(512) it is also why it saves that address off as __start so it can then do math on the resulting image. If you manipulate . during the script so it points to the last chunk of memory added to the image then you can use it to determine the total size:
__stage2_nsectors = ABSOLUTE((. - __start) / 512);
in English would be
The difference between the starting place and wherever I ended divided by sector size.

Resources