I think I solved it, I updated the linker script to the below (see new include of "ltrans.o"). I think I might have run into the same issue that these folks over at qualcomm has[1]. The linker script references a file, but after LTO, the filename is gone or changed to "ltrans.o". I cannot find anything about if this is expected behavior or not with GCC. [1]: https://groups.google.com/g/llvm-dev/c/jNUFfRGOMUs ``` ER_NZI (NOLOAD) : { . = ALIGN(4); __heap_mem_area_not_ret_start__ = .; *jump_table.o(heap_mem_area_not_ret) /* not retained HEAP */ *ltrans.o(heap_mem_area_not_ret) __heap_mem_area_not_ret_end__ = .; } > LR_IROM3 ``` - Niklas On Fri, 27 Sept 2024 at 22:14, Niklas Dusenlund <niklas@xxxxxxxxxxxx> wrote: > Unfortunately it didn't help in my case. I noticed another hint when I > looked at the final binaries using readelf. It seems when I compile with > LTO the section to segment mapping is off. > > Instead of putting `heap_mem_area_not_ret` in `.bss`, it is put in its own > segment. So the variable is there, just not where the rest of the program > expects it to be. Could it be that I have to change something in the linker > script to make it work with LTO? > > ``` > Section to Segment mapping: > Segment Sections... > 00 .ARM.exidx > 01 ER_IROM1 > 02 ER_IROM2 > 03 ER_IROM3 .ARM.exidx .copy.table .zero.table .data .bss ER_NZI > 04 RET_DATA RET_HEAP > 05 RET_DATA_UNINIT_TRNG_STATE > 06 > ``` > > ``` > Section to Segment mapping: > Segment Sections... > 00 .ARM.exidx > 01 ER_IROM1 > 02 ER_IROM2 > 03 ER_IROM3 .ARM.exidx .copy.table .zero.table .data heap_db_area > heap_env_area heap_mem_area_not_ret heap_msg_area trng_state .bss > 04 RET_DATA > 05 > ``` > - Niklas > > > On Fri, 27 Sept 2024 at 16:19, Richard Earnshaw (lists) < > Richard.Earnshaw@xxxxxxx> wrote: > >> On 27/09/2024 14:32, Niklas Dusenlund wrote: >> > Hi! >> > >> > I'm compiling for a cortex-m0 with a very small memory, so I would like >> to >> > use LTO to minimize my program size. >> > >> > I compile all the source files of an SDK to object files. Then I create >> an >> > archive with gcc-ar. Finally I link everything together with gcc. >> > Everything except this global variable works as expected both with and >> > without LTO. I fail to understand why the variable is removed when I >> enable >> > LTO. >> > >> > As you can see in the output maps below, the size in the correct >> version is >> > 0x80c, whereas it is 0 in the bad output. >> > >> > When I look at the object files and archives with gcc-nm they look the >> same >> > with and without -flto. >> > >> > I guess this is a "hack" to define the size of a heap. But if there is >> > another way to achieve the same result I'm all open for it. This memory >> > area is referenced by code that is burned into a ROM at manufacturing >> time. >> > So it is used, but of course GCC doesn't see that. >> > >> > The variable is defined like this: >> > >> > ``` >> > uint32_t >> rwip_heap_non_ret[RWIP_CALC_HEAP_LEN(RWIP_HEAP_NON_RET_SIZE_JT)] >> > __attribute__((section("heap_mem_area_not_ret"))); >> >> When the LTO pass is run, any variable not explicitly referenced in your >> source code will be discarded (LTO doesn't look at the linker script), so >> you need to tell the compiler not to discard it (this doesn't generally >> happen without LTO because the compiler is unable to tell if a variable >> will be referenced by another component in your program, but with LTO we >> can see ALL the code). >> >> So try >> uint32_t rwip_heap_non_ret[RWIP_CALC_HEAP_LEN(RWIP_HEAP_NON_RET_SIZE_JT)] >> __attribute__((used,section("heap_mem_area_not_ret"))); >> >> The extra 'used' attribute tells the compiler that this value is used, >> even if it can't tell where that happens. >> >> A simple testcase demonstrates this: >> >> int __attribute__((used)) x; >> int y; >> int main() { return 0;} >> >> If I build this without LTO (gcc test.c -O3 -o test.exe) I get >> definitions of x and y in my executable, but if I enable LTO (gcc -flto >> test.c -O3 -o test.lto.exe) I only get a definition of x. >> >> nm test.exe >> ... >> >> 0000000000004018 B x >> 0000000000004014 B y >> >> nm test.lto.exe >> ... >> 0000000000004014 B x >> >> R. >> >> > ``` >> > >> > It is referenced like this in the linker script: >> > ``` >> > ER_NZI (NOLOAD) : >> > { >> > . = ALIGN(4); >> > __heap_mem_area_not_ret_start__ = .; >> > *jump_table.o(heap_mem_area_not_ret) /* not retained HEAP */ >> > __heap_mem_area_not_ret_end__ = .; >> > } > LR_IROM3 >> > ``` >> > >> > >> > From the memory map it looks like this when it is correct: >> > >> > ``` >> > ER_NZI 0x07fc754c 0x80c >> > 0x07fc754c . = ALIGN (0x4) >> > 0x07fc754c >> > __heap_mem_area_not_ret_start__ = . >> > *jump_table.o(heap_mem_area_not_ret) >> > heap_mem_area_not_ret >> > 0x07fc754c 0x80c >> > >> /var/folders/9n/c7ggvkrn03ld13___37y6lcw0000gn/T/rustcqiFzCU/libda14531_sdk.a(2077e224400bc0d4-jump_table.o) >> > 0x07fc754c rwip_heap_non_ret >> > 0x07fc7d58 >> > __heap_mem_area_not_ret_end__ = . >> > ``` >> > >> > and this when it is incorrect: >> > >> > ``` >> > ER_NZI 0x07fc800c 0x0 >> > 0x07fc800c . = ALIGN (0x4) >> > 0x07fc800c >> > __heap_mem_area_not_ret_start__ = . >> > *jump_table.o(heap_mem_area_not_ret) >> > 0x07fc800c >> > __heap_mem_area_not_ret_end__ = . >> > ``` >> > >> > Ouput of gcc-nm: >> > >> > ``` >> > 00000000 D rwip_heap_db_ret >> > 00000000 D rwip_heap_env_ret >> > 00000000 D rwip_heap_msg_ret >> > 00000000 D rwip_heap_non_ret >> > ``` >> > >> > - Niklas >> >>