A specific Q on GCC linker script syntax - Page 1
Forum section: electronics / general

author: peter-h
author_rank: Super Contributor
timestamp: July 28, 2024, 08:19:06 pm
content: I have a project (STM ARM32 GCC V11) and I want to locate initialised data (DATA) starting with DATA from main.c and then followed by all the other modules in the project. Is this the correct syntax for that? The align 4 is important to maintain.Code:/* Initialized data sections for non boot block code. These go into RAM. LMA copy is loaded after code. *//* This stuff is copied from FLASH to RAM by C code in the main stub *//* This is stuff like int fred = 1; which is copied flash -> ram (not const) */. = ALIGN(4);/* main.c DATA */.main_data :{. = ALIGN(4);_s_data = .;/* create a global symbol at data start */*main.o (.data .data*)/* .data sections */. = ALIGN(4);} >RAM AT >FLASH_APP/* Remaining DATA */._other_data :{. = ALIGN(4);*(.data .data*)/* .data sections */. = ALIGN(4);_e_data = .;/* define a global symbol at data end */} >RAM AT >FLASH_APP/* used by the main stub C code to initialize data */_si_nonboot_data = LOADADDR(.main_data);AIUI, the above should first collect DATA objects from main.o and then collect all other DATA objects.I have seen suggestions that the KEEP directive should be used but not sure about it.Thank you for any comments.

author: SiliconWizard
author_rank: Super Contributor
timestamp: July 28, 2024, 10:23:21 pm
content: The one point to care about is to make sure you properly copy data from flash to RAM in your startup code, for both sections. But I guess you already figured that.AFAIK, KEEP is only used to make sure the linker won't prune a given section even if no actual data is inside of it. I don't know what other purpose it may have - if it does, someone else may tell us.

author: ejeffrey
author_rank: Super Contributor
timestamp: July 28, 2024, 11:50:25 pm
content: KEEP avoids pruning symbols/sections that have no incoming references. It's normally used for the entry point and things like interrupt vector tables that are directly accessed by the hardware/platform and not necessarily referenced by code visible to the linker.In my experience people overuse KEEP. A common example is to have a bug where e.g., main is accidentally empty. This can result in the linker discarding your entire program. People see that, get pissed at the linker and add KEEP directives to prevent that. But of course the linker is correct in discarding that code, the bug is that main is empty and it won't call the dead code even if you use KEEP to force it to be present.

author: peter-h
author_rank: Super Contributor
timestamp: July 29, 2024, 06:24:54 am
content: QuoteHopefully - it is done with thisCode:extern char _s_data;extern char _e_data;extern char _si_data;memcpy(&_s_data, &_si_nonboot_data, &_e_data - &_s_data);I found this has exactly the same effect, except that there is a missing align 4 after the main.c DATA stuff (which should make no difference; this CPU supports unaligned data). I think all those align 4 statements are fossils from other ST sample code.Code:. = ALIGN(4);/* main.c DATA */.main_data :{. = ALIGN(4);_s_data = .;/* create a global symbol at data start */*main.o (.data .data*)/* main.c .data sections */*(.data .data*)/* all other .data sections */. = ALIGN(4);_e_data = .;/* define a global symbol at data end */} >RAM AT >FLASH_APP_si_nonboot_data = LOADADDR(.main_data);

author: ejeffrey
author_rank: Super Contributor
timestamp: July 29, 2024, 03:19:47 pm
content: The align 4 statement right before edata is only to make the memcpy an aligned length. It only matters for performance of you use C memcpy but if you write your startup code in assembly you might prefer to do exclusively 4 byte accesses.You want the data segments aligned even when the architecture supports unaligned access. The compiler will align objects relative to the section beginning so if the linker places the section at an unaligned location *every* integer in the data segment will be unaligned and incur a performance penalty.In addition, unaligned accesses are not atomic with respect to other cores or DMA which may or may not be a problem for you.Normally input sections have their own alignment specifier based on their contents so you actually have to work at it to create an unaligned binary. The ALIGN directives here should be mostly documentation and a bit of insurance. However you can use linker level ALIGN directives to force a section or subsection to have greater alignment than required by the data. For instance you might want the output sections to be page aligned on a system with virtual memory, or erase block aligned on a system with flash.

author: peter-h
author_rank: Super Contributor
timestamp: July 29, 2024, 04:08:04 pm
content: Thanks - every day is a school dayI've spent a number of days on this problemhttps://www.eevblog.com/forum/microcontrollers/looking-for-help-to-debug-32f4-ethernet/msg5587775/#msg5587775and I am pretty sure that the approach involving two (or more) linkfile sections as in my 1st post above, is problematic for DATA because it creates a mismatch between the FLASH based version and where it is expected to end up in RAM. Took me bloody ages to discover the data (stuff like initialisation values for various peripheral registers) was being copied across with gaps in it.Fairly obviously, align directives within source code work fine, including those silently added by the compiler. But there seems to be a gotcha with DATA used in conjunction with aligns in the linkfile.

author: ejeffrey
author_rank: Super Contributor
timestamp: July 29, 2024, 05:46:19 pm
content: So you are saying that the section alignment is being handled differently for the flash address and the RAM address, so the memcpy that attempts to copy both data sections is doing the wrong thing? essentially LOADADDR(.main_data) - .main_data != LOADADDR(._other_data) - .other_data? Have you actually confirmed that by looking at the object dump of the resulting elf file?

author: peter-h
author_rank: Super Contributor
timestamp: July 29, 2024, 06:45:20 pm
content: Yes.I think it is the use of multiple >RAM AT >FLASH_APP statements which produces a discontinuity between the flash block and the ram block. I did a hex dump of src and dest of the ~1500 byte block and found a gap of a few bytes. I think it was the align directive between the two blocks that was the cause, because I could make it work by packing in 1 byte of DATA at a time until the gap closed.I can well believe nobody has found this before.

author: ejeffrey
author_rank: Super Contributor
timestamp: July 29, 2024, 08:14:01 pm
content: Not looking for gaps, having gaps should be fine. I mean looking for the flash vs. sram addresses not agreeing. Look at `objdump -h <myfile.elf>`and see how the sections are laid out in the VMA vs LMA.

author: peter-h
author_rank: Super Contributor
timestamp: July 30, 2024, 08:02:28 pm
content: I didn't go back to test the objdump stuff (I didn't know you could list it like that until I saw your post) but I think the issue is with using memcpy or similar to set up initialised data inonemove, when you have more than one DATA section defined.I would expect them to concatenate correctly if there are no align directives between the sections but that was exactly what I did have.There is a lot of really weird stuff in the way these linkfiles work. For example, ST's Cube IDE examples have this at the very endCode:.code_constants_etc :{. = ALIGN(4);*(.text) /* .text sections (code) */*(.text*) /* .text* sections (code) */*(.rodata) /* .rodata sections (constants, strings, etc.) */*(.rodata*) /* .rodata* sections (constants, strings, etc.) */*(.glue_7) /* glue arm to thumb code */*(.glue_7t) /* glue thumb to arm code */*(.eh_frame)KEEP (*(.init))KEEP (*(.fini)). = ALIGN(4);_e_code_constants_etc = .; /* define a global symbol at end of code */} >FLASHIt is after collecting DATA, BSS, etc. But there is no reason why one should be collecting the above (into FLASH) after DATA or BSS. But if you put the above before, strange stuff happens. I spent days narrowing this down, and clearly somebody at ST went down the same path, without documenting why. Text and Rodata are clear enough but the rest is quite subtle. Putting *(.eh_frame) into google produces enough reading for days.