GNU Linker: ELF has a LOAD segment with RWX permissions. Embedded ARM project - ld

I have updated my arm-none-eabi GCC and the associated tools and rebuilt an embedded project I develop.
$ arm-none-eabi-ld --version
GNU ld (GNU Binutils) 2.39
Suddenly, I'm getting the warning
/usr/lib/gcc/arm-none-eabi/12.1.0/../../../../arm-none-eabi/bin/ld: warning: my_elf_file.elf has a LOAD segment with RWX permissions.
This warning seems to be newly introduced. I haven't changed the source / linkerscript lately. (EDIT: I checked an old ELF file created with a previous version. It didn't print the warning during linking but has the same issue). I develop for an STM32F407 microcontroller.
The memory configuration in my linker script is the following:
MEMORY
{
FLASH (xr) : ORIGIN = 0x08000000, LENGTH = 512K
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K
CCM (rw) : ORIGIN = 0x10000000, LENGTH = 64K
}
Looking into the linked ELF I see:
$ readelf -l my_elf_file.elf
Elf file type is EXEC (Executable file)
Entry point 0x800b1f1
There are 5 program headers, starting at offset 52
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
LOAD 0x010000 0x08000000 0x08000000 0x2220c 0x2220c RWE 0x10000
LOAD 0x040000 0x10000000 0x0802220c 0x003e8 0x00d30 RW 0x10000
LOAD 0x050000 0x20000000 0x080225f4 0x00c1c 0x00c1c RW 0x10000
LOAD 0x000c20 0x20000c20 0x08023210 0x00000 0x01e70 RW 0x10000
LOAD 0x002a90 0x20002a90 0x08023210 0x00000 0x08580 RW 0x10000
Section to Segment mapping:
Segment Sections...
00 .vectors .text .ARM .flashcrc
01 .ccmdata .ccmbss
02 .data
03 .bss
04 .heap_stack
Indeed, the first segment is flagged as RWE. It contains the sections .vectors, .text, .ARM and .flashcrc.
The .vectors section and the .text section contain the Vector table and the program code. I added another section in my linkerscript called .flashcrc
.flashcrc : ALIGN(4)
{
KEEP(*(.flashcrc))
KEEP(*(.flashcrc.*))
. = ALIGN(4);
} >FLASH =0xFF
I use this section in the source code to position a const struct in there, that contains CRC checksums. These checksums are calculated and patched into the ELF later by a spearate python script. The struct is easier to find in the ELF, if it resides in its own section.
Removing this section or simply relocating it to RAM like this:
.flashcrc : ALIGN(4)
{
KEEP(*(.flashcrc))
KEEP(*(.flashcrc.*))
. = ALIGN(4);
} >RAM AT >FLASH =0xFF
removed the "W" flag from the segment and the warning is gone.
I don't understand why the ELF file contains an "writable" flag for a section that is located in FLASH which is marked in the linkerscript as non writable. I tried using (xr!w) in the MEMORY definition, but it didn't change anything.
How do I convince the linker that the segment is not writable to silence this warning?
Why don't the flags given in the MEMORY part of the linker script have any impact?
Weirdly enough this doesn't happen with the .vectors section which contains a const array of function pointers. So this section is basically identical to the .flashcrc section.
EDIT2:
Today I found a little more time to play around. The struct in the .flashcrc section is defined in code (globally) like this:
volatile const struct flash_crcs __attribute__((section(".flashcrc"))) crcs_in_flash = {
.start_magic = 0xA8BE53F9UL,
.crc_section_ccm_data = 0UL,
.crc_section_text = 0UL,
.crc_section_data = 0UL,
.crc_section_vectors = 0UL,
.end_magic = 0xFFA582FFUL,
};
The crc values are patched into the ELF after linking.
I had to make the struct volatile. If it isn't volatile, the compiler optimizes away the access to the struct because the elements are all 0 from its perspective as it doesn't know that these are patched after linking.
It turns out, that the warning disappears, if the volatile keyword is removed. For some reason the volatile keyword tricks the compiler/linker into thinking that this should be writable.
Is there another way to prevent the compiler optimizing away the access to this struct but not using volatile?

Related

linker script and changing the flash address

I would like to ask the following a question: im using stm32g0xx microcontroller and i want to change the flash address in linker script automatically and not be forced to changed manually every time i want to generate an apllication image to let it run from diffrent address. what im doing i wrote an application and i wrote it to tow different address"0x08001000 and 0x08004800" to have the apility to switch to other application incase one of them is updated or damaged. it worked fine but i need by every image to change the flash address manually and i would like to ask if it is possible to changed somewhere else out of the linker script like startup.s?
MEMORY
{
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 8K
FLASH (rx) : ORIGIN = 0x8001000, LENGTH = 32K
}
MEMORY
{
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 8K
FLASH (rx) : ORIGIN = 0x8004800, LENGTH = 32K
}
You can create two linker files and compile two times, each time with a different linker script and a different output binary. You will obtain the two necessary binaries. To integrate it on your project, it depends on your way of working (STM IDE, standalone Makefile...) which you did not mention.
As a side note, you should modify the LENGTH on your linker scripts, it will prevent the linker to place data where you have another application.
Your first application starts at 4KB (0x1000), and the second start at 18KB (0x4800), the lenght of the first application should be 18-4 = 16KB and the second LENGTH should be 32-18 = 14KB (if the FLASH total size is 32KB).
You can write two different linker script and apply one or the other in your building enviroment (with the -T linker flag) or you can use a variable for your ORIGIN and pass it with -Wl,--defsym=<VAR_NAME>=<VAR_VALUE>

External Flash Loader linker script file

I'm trying to make a custom external flash loader for my STM32F746 uC based board and i found some templates for this purpose from this link that is belongs to the ST workshop on this YouTube link. So when i dug into the corresponding files i saw that in a linker script file named linker.ld the RAM memory region is defined from base address 0x20000004 though i know that the RAM memory region in STM uCs starts from address 0x20000000:
MEMORY
{
RAM (xrw) : ORIGIN = 0x20000004, LENGTH = 320K
}
I searched for many other templates file existing in this link and also they are using the same way for RAM memory region base address.
So the problem is why we must define RAM memory address from 0x20000004 for external flash loader algorithm to store in and not from 0x20000000 instead. Could anyone please help me to explain the reason and tell me that what will happen if we do the:
MEMORY
{
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 320K
}
instead of previous one and what errors might be occurred?
Thanks.

How can I change the start address on flash?

I'm using STM32F746ZG and FreeRTOS.
The start address of flash is 0x08000000. But I want to change it to 0x08040000. I've searched this issue through google but I didn't find the solution.
I changed the linker script like the following.
MEMORY
{
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 320K
/* FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 1024K */
FLASH (rx) : ORIGIN = 0x8040000, LENGTH = 768K
}
If I only change it and run the debugger, it has the problem.
If I change the VECT_TAB_OFFSET from 0x00 to 0x4000, it works fine.
/* #define VECT_TAB_SRAM */
#define VECT_TAB_OFFSET 0x40000 /* 0x00 */
SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET;
But if I don't use debugger, it doesn't work anything.
It means it only works when using ST-Linker.
Please let me know if you know the solution.
Thank you for in advance of your reply.
The boot address can be set in the option bytes.
You can set any address in the flash with 16k increments. There are two 16 bit registers in the option bytes area, one is used when the boot pin is low at reset, the other when the pin is high. Write the desired address shifted right by 14 bits, i.e. divided by 16384.
To boot from 0x08040000, write 0x2010 into the register as described in the Option bytes programming chapter of the reference manual.
You could also write a bootloader. Bootloader sits on the 0x0800 0000 address and loads your application firmware meaning jumps to it.
This is the other way to do it.
You need to place 8 bytes at the original beginning of the FLASH. Stm32 boots always from the address 0x00000000 which is aliased to the one of the memories (depending on the boot pins and options).
The first word contains the stack pointer the second one your reset handler. You never get to your code as it boots always from the same address.
You will need to modify your linker script and the startup files where vectors are defined

STM32 Boot from specific NOR block

I've ported an STM32F4 project from Keil uVision5 to Eclipse (GNU MCU Plugin).
In my Keil uVision5 project, I used to use 7th block (0x080E0000) for my boot code (I verify that using STM32 ST-LINK Utility tool that my boot code is really at 7th block) which I used to configure very easily using Keil uVision5 interface as shown below:
If I'm not mistaken, in my Keil uVision project (whose memory configuration is shown above), the MCU boots from 0th block (0x08000000) and then jumps immediately to 7th block (0x080E0000) to execute my code. Tell me if I'm wrong.
My problem is with my Eclipse project. As you might already know, we configure the memory map using a linker script file named mem.ld. I tried to imitate the same behavior using mem.ld but I suppose that I've failed. Whenever I flash the executable generated by Eclipse, my code doesn't run. Moreover, I couldn't find a way to indicate an area for startup in mem.ld file. Below is my memory configuration in mem.ld file:
MEMORY
{
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K
CCMRAM (xrw) : ORIGIN = 0x10000000, LENGTH = 64K
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 16K
FLASHB1 (rx) : ORIGIN = 0x080E0000, LENGTH = 640K
EXTMEMB0 (rx) : ORIGIN = 0x00000000, LENGTH = 0
EXTMEMB1 (rx) : ORIGIN = 0x00000000, LENGTH = 0
EXTMEMB2 (rx) : ORIGIN = 0x00000000, LENGTH = 0
EXTMEMB3 (rx) : ORIGIN = 0x00000000, LENGTH = 0
MEMORY_ARRAY (xrw) : ORIGIN = 0x20002000, LENGTH = 32
}
Could you please help me to imitate the same behavior with Keil uVision5 using mem.ld file? What mistake do I do in mem.ld configuration? What should be the correct configuration?
Thanks a lot in advance.
The address of the code which is executed after the reset is taken from the 4-7 bytes of your vector table (it is called the reset vector).
In the gcc style linker script the ENTRY directive is important not your memory sections definitions.
you can change the entry to the address value or a symbol. For example
ENTRY(My_Startup_Function) or as it is in the standard linker scripts generated by the CubeMX ENTRY(Reset_Handler)
in keil as I remember you have couple of options:
command line
--entry=location
where location can be address or symbol
here is more information about the root region and the entry point. I do not use keil anymore and personally prefer gcc.

How to interpret avr32-size output?

I have C program running on a AVR32 microcontroller (UC3C0512C).
Issuing the avr32-size -A PROGRAM.elf command generates the following output:
PROGRAM.elf :
section size addr
.reset 8200 2147483648
.rela.got 0 2147491848
.text 99512 2147491848
.exception 512 2147591680
.rodata 5072 2147592192
.dalign 4 4
.data 7036 8
.balign 4 7044
.bss 5856 7048
.heap 48536 12904
.comment 48 0
.debug_aranges 8672 0
.debug_pubnames 14476 0
.debug_info 311236 0
.debug_abbrev 49205 0
.debug_line 208324 0
.debug_frame 23380 0
.debug_str 43961 0
.debug_loc 63619 0
.debug_macinfo 94469328 0
.stack 4096 61440
.data_hram0 512 2684354560
.debug_ranges 8368 0
Total 95379957
Can someone explain how to interpret these values?
How can I calculate the flash and ram usage based on this list?
Update 1:
Without the -A flag, I am getting the following:
text data bss dec hex filename
113296 7548 58496 179340 2bc8c PROGRAM.elf
Update 2:
I'm not using dynamic memory allocation, so according the avr-libc user-manual, the free RAM space should be simply: stackpointer minus __heap_start.
In this case: 61440 - 12904 = 48536 byte free RAM space.
Can someone confirm that?
(There is a mismatch in the two outputs in your question. The bss number is wildly different.)
If you don't use malloc, and don't count the stack, then yes, the RAM usage is the data plus the bss (plus some alignment spacing). The data are the variables that are set in a declaration, and the bss are the variables that are not. The C runtime will probably initialize them to 0, but it doesn't have to.
The flash usage will be the text and the data. That is, the flash will include the program instructions and C runtime, but also the values that need to get copied into RAM on startup to initialize those variables. This data is generally tacked onto the end of the program instructions.
Re: update 2
RAM holds global variables, the heap, and then the stack in that order.
The global variables can be initialized in the program, or not. The .data section is stored in flash, and the C runtime copies these values into the beginning of RAM where the corresponding variables live before your code runs. The .bss section of global variables needs space in RAM to hold the values, but they aren't necessarily initialized. The C runtime that comes with avr-gcc does actually initialize these to 0. The point it that your don't need to store an array of 0s to copy over, as you do with the .data section.
You are not using heap, but dynamically allocated memory is obtained from the addresses between heap_start and heap_end.
But the stack is not limited. Yes, the stack-pointer is initialized at startup, but it changes as your program runs, and can move well into the heap or even into the global variables (stack overflow). The stack pointer moves whenever a function is called, or local variables within a function are used. For example, a large array declared inside a function will go on the stack.
So in answer to your question, there is no RAM that is guaranteed to remain free.
I think you should remove the -A (all) flag, since that gives you the more low-level list you're showing.
The default output is easier to parse, and seems to directly state the values you're after.
Note: I didn't try this, not a system with an AVR toolchain installed.
I guess that in your linker script you have RAM at 0, and Flash at 0x80000000, so all things that need to go to RAM are at addresses 0+ (.stack is the last at 61440 (spanning next 4k)). So you would need a bit more that 64k of RAM. Everything else you have is flash.
That is provided that your linker script is correct.
Also see unwind's comment.
These values are the assembly language sections of the compiled C code. See the docs for the details. This article is also helpful.
The section titled .text represents the instruction section, i.e. the assembly instructions. The .data section represents the size of the variables (ints, arrays, etc.). The size column has the significant info, and it has the size of each section in bytes. The .stack and .heap represent the memory allocated in preparation for the execution of the program to set up the virtual memory.
You can try
avr-nm --print-size --radix d --demangle x.elf
to get the sizes in decimal notation.
Then you can copy & paste into a spreadsheet, filter, sort by the sections, and sum it up.