Hi all,
First I want to deliver some background information to the given problem: I'm developing a firmware on a SoC with a cortex-M0 (with Arm Compiler for Embedded 6.21) for a metering device with legally relevant software parts. This means, that after certification a change in the legally part of the firmware is not allowed without re-certification. My idea is, to compile the legally relevant code separately and pack the objects into a static library. This code contains only functions which modify data in a fixed positioned data structure in RAM.
At link time, I want to place all those objects from the library in a specific execution region with use of a scatter file. So far so good, up to this step everything was possible to do for me.
My compiler flags to build the library object user_function.o (it contains only one function: add_num) are:
-xc -mcpu=cortex-m0 --target=arm-arm-none-eabi -std=c99 -c -fno-rtti -funsigned-char -fshort-enums -fshort-wchar -mlittle-endian -gdwarf-4 -O0 -fbare-metal-pie
After this step, I create an archive with armar:
armar --create libMetering_1.0.0.a user_function.o
My compiler flags to build the main firmware objects are:
-xc -mcpu=cortex-m0 --target=arm-arm-none-eabi -std=c99 -c -fno-rtti -funsigned-char -fshort-enums -fshort-wchar -mlittle-endian -gdwarf-4 -O0 -fno-function-sections
The linker flags to link the whole image are:
--cpu=cortex-m0 --library_type=microlib --strict --scatter "${CMAKE_SOURCE_DIR}/linker/myScatter.sct" --pad=0xFF --remove --summary_stderr --info summarysizes --bestdebug --map --load_addr_map_info --xref --callgraph --symbols --info=sizes --info=totals --info=unused --info=veneers --verbose
My scatter file looks like this:
LR_IROM1 0x00000000 0x00040000 { ; load region size_region ER_IROM1 0x00000000 0x0001E204 { ; load address = execution address *.o (RESET, +First) *(InRoot$$Sections) .ANY (+RO) .ANY (+XO) } ER_IROM2 0x0001E204 0x000205FC { *libMetering*.a(+RO) ; place all RO code objets from libMeter here } RW_IRAM1 0x20000480 0x00000C00 { ; place meter struct (meter_t) here *(meter_rc) } RW_IRAM2 0x20001080 0x00004F80 { .ANY (+RW +ZI) } RW_IRAM3 0x20006000 0x00001000 { ; NVRAM, will be powered from supercap during *(nv_ram) ; powerloss } }
This ends up in an image like this:
Memory Map of the image Image Entry point : 0x000000c1 Load Region LR_IROM1 (Base: 0x00000000, Size: 0x00000e78, Max: 0x00040000, ABSOLUTE) Execution Region ER_IROM1 (Exec base: 0x00000000, Load base: 0x00000000, Size: 0x00000e60, Max: 0x0001e204, ABSOLUTE) Exec Addr Load Addr Size Type Attr Idx E Section Name Object 0x00000000 0x00000000 0x000000c0 Data RO 3 RESET startup_dev_Keil.o 0x000000c0 0x000000c0 0x00000000 Code RO 62 * .ARM.Collect$$$$00000000 mc_p.l(entry.o) 0x000000c0 0x000000c0 0x00000004 Code RO 69 .ARM.Collect$$$$00000001 mc_p.l(entry2.o) 0x000000c4 0x000000c4 0x00000004 Code RO 72 .ARM.Collect$$$$00000004 mc_p.l(entry5.o) 0x000000c8 0x000000c8 0x00000000 Code RO 74 .ARM.Collect$$$$00000008 mc_p.l(entry7b.o) 0x000000c8 0x000000c8 0x00000000 Code RO 76 .ARM.Collect$$$$0000000A mc_p.l(entry8b.o) 0x000000c8 0x000000c8 0x00000008 Code RO 77 .ARM.Collect$$$$0000000B mc_p.l(entry9a.o) 0x000000d0 0x000000d0 0x00000000 Code RO 79 .ARM.Collect$$$$0000000D mc_p.l(entry10a.o) 0x000000d0 0x000000d0 0x00000000 Code RO 81 .ARM.Collect$$$$0000000F mc_p.l(entry11a.o) 0x000000d0 0x000000d0 0x00000004 Code RO 70 .ARM.Collect$$$$00002712 mc_p.l(entry2.o) 0x000000d4 0x000000d4 0x00000078 Code RO 4 .text startup_dev_Keil.o 0x0000014c 0x0000014c 0x000000a2 Code RO 10 .text main.o 0x000001ee 0x000001ee 0x00000002 PAD 0x000001f0 0x000001f0 0x00000044 Code RO 19 .text delay.o 0x00000234 0x00000234 0x0000024e Code RO 28 .text gpio.o 0x00000482 0x00000482 0x00000002 PAD 0x00000484 0x00000484 0x00000908 Code RO 36 .text system_dev.o 0x00000d8c 0x00000d8c 0x00000014 Code RO 46 .text wd.o 0x00000da0 0x00000da0 0x0000003e Code RO 85 .text mc_p.l(uidiv_div0.o) 0x00000dde 0x00000dde 0x00000002 PAD 0x00000de0 0x00000de0 0x00000030 Code RO 91 .text mc_p.l(init.o) 0x00000e10 0x00000e10 0x0000000e Code RO 95 i.__scatterload_copy mc_p.l(handlers.o) 0x00000e1e 0x00000e1e 0x00000002 Code RO 96 i.__scatterload_null mc_p.l(handlers.o) 0x00000e20 0x00000e20 0x0000000e Code RO 97 i.__scatterload_zeroinit mc_p.l(handlers.o) 0x00000e2e 0x00000e2e 0x00000002 PAD 0x00000e30 0x00000e30 0x00000030 Data RO 94 Region$$Table anon$$obj.o Execution Region ER_IROM2 (Exec base: 0x0001e204, Load base: 0x00000e60, Size: 0x00000010, Max: 0x000205fc, ABSOLUTE) Exec Addr Load Addr Size Type Attr Idx E Section Name Object 0x0001e204 0x00000e60 0x00000010 Code RO 55 .text.add_num libMetering_1.0.0.a(user_function.o) Execution Region RW_IRAM1 (Exec base: 0x20000480, Load base: 0x00000e70, Size: 0x00000000, Max: 0x00000c00, ABSOLUTE) **** No section assigned to this execution region **** Execution Region RW_IRAM2 (Exec base: 0x20001080, Load base: 0x00000e70, Size: 0x00000600, Max: 0x00004f80, ABSOLUTE) Exec Addr Load Addr Size Type Attr Idx E Section Name Object 0x20001080 0x00000e70 0x00000004 Data RW 38 .data.SystemCoreClock system_dev.o 0x20001084 0x00000e74 0x00000004 Data RW 39 .data.SystemCoreClockDivisor system_dev.o 0x20001088 - 0x00000004 Zero RW 22 .bss.delay_cnt delay.o 0x2000108c - 0x000001ec Zero RW 40 .bss.event_fn_ary system_dev.o 0x20001278 - 0x00000004 Zero RW 12 .bss.test main.o 0x2000127c 0x00000e78 0x00000004 PAD 0x20001280 - 0x00000400 Zero RW 1 STACK startup_dev_Keil.o Execution Region RW_IRAM3 (Exec base: 0x20006000, Load base: 0x00000e78, Size: 0x00000000, Max: 0x00001000, ABSOLUTE) **** No section assigned to this execution region ****
As I said before, this is the result I wanted to achieve. My user_function.o is placed at 0x0001e204. But, if I debug the firmware in gdb, the program runs until my function "add_num" in user_function.o. After that, the program crashes. If I have a look on the disassembly output, it shows:
─── Assembly ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── 0x0001e204 add_num+0 @ <UNDEFINED> instruction: 0xffffffff 0x0001e208 add_num+4 @ <UNDEFINED> instruction: 0xffffffff 0x0001e20c add_num+8 @ <UNDEFINED> instruction: 0xffffffff 0x0001e210 add_num+12 @ <UNDEFINED> instruction: 0xffffffff
The output with fromelf -c of user_function.o looks like this:
======================================================================== ** Section #1 '.strtab' (SHT_STRTAB) Size : 187 bytes ** Section #2 '.text' (SHT_PROGBITS) [SHF_ALLOC + SHF_EXECINSTR] Size : 0 bytes (alignment 4) Address: 0x00000000 ** Section #3 '.text.add_num' (SHT_PROGBITS) [SHF_ALLOC + SHF_EXECINSTR] Size : 16 bytes (alignment 2) Address: 0x00000000 [Anonymous symbol #2] $t.0 add_num 0x00000000: b082 .. SUB sp,sp,#8 0x00000002: 9001 .. STR r0,[sp,#4] 0x00000004: 9100 .. STR r1,[sp,#0] 0x00000006: 9801 .. LDR r0,[sp,#4] 0x00000008: 9900 .. LDR r1,[sp,#0] 0x0000000a: 1840 @. ADDS r0,r0,r1 0x0000000c: b002 .. ADD sp,sp,#8 0x0000000e: 4770 pG BX lr ** Section #4 '.ARM.exidx.text.add_num' (SHT_ARM_EXIDX) [SHF_ALLOC + SHF_LINK_ORDER] Size : 8 bytes (alignment 4) Address: 0x00000000 Link to section #3 '.text.add_num' ** Section #5 '.rel.ARM.exidx.text.add_num' (SHT_REL) [SHF_INFO_LINK] Size : 8 bytes (alignment 4) Symbol table #17 '.symtab' 1 relocations applied to section #4 '.ARM.exidx.text.add_num' ** Section #6 '.debug_abbrev' (SHT_PROGBITS) Size : 82 bytes ** Section #7 '.debug_info' (SHT_PROGBITS) Size : 107 bytes ** Section #8 '.rel.debug_info' (SHT_REL) [SHF_INFO_LINK] Size : 96 bytes (alignment 4) Symbol table #17 '.symtab' 12 relocations applied to section #7 '.debug_info' ** Section #9 '.debug_str' (SHT_PROGBITS) [SHF_MERGE + SHF_STRINGS] Size : 260 bytes ** Section #10 '.comment' (SHT_PROGBITS) [SHF_MERGE + SHF_STRINGS] Size : 69 bytes ** Section #11 '.note.GNU-stack' (SHT_PROGBITS) Size : 0 bytes ** Section #12 '.ARM.attributes' (SHT_ARM_ATTRIBUTES) Size : 84 bytes ** Section #13 '.debug_frame' (SHT_PROGBITS) Size : 40 bytes (alignment 4) ** Section #14 '.rel.debug_frame' (SHT_REL) [SHF_INFO_LINK] Size : 16 bytes (alignment 4) Symbol table #17 '.symtab' 2 relocations applied to section #13 '.debug_frame' ** Section #15 '.debug_line' (SHT_PROGBITS) Size : 214 bytes ** Section #16 '.rel.debug_line' (SHT_REL) [SHF_INFO_LINK] Size : 8 bytes (alignment 4) Symbol table #17 '.symtab' 1 relocations applied to section #15 '.debug_line' ** Section #17 '.symtab' (SHT_SYMTAB) Size : 144 bytes (alignment 4) String table #1 '.strtab' Last local symbol no. 7
What am I missing here? My first guess was, that I have to compile location independent code for the lib objects, therefore I added "-fbare-metal-pie". Without success.
I would be very happy about any advice
Thanks in advance
Hello,
The only thing I can think of is that the code is not correctly written to its execution address, though if that was the case I would expect the code to fail during scatterloading (perhaps writes do not trigger aborts at this address?). What does the debugger show at this address when you first stop at main()?
Looking at your memory map:
Execution Region ER_IROM2 (Exec base: 0x0001e204, Load base: 0x00000e60, Size: 0x00000010, Max: 0x000205fc, ABSOLUTE) Exec Addr Load Addr Size Type Attr Idx E Section Name Object 0x0001e204 0x00000e60 0x00000010 Code RO 55 .text.add_num libMetering_1.0.0.a(user_function.o)
You see that the LOAD address of this region is 0xE60, and this will be copied to the execution address (0x1E204) by the scatterloading init code.
Perhaps you should define this execution region as FIXED, meaning that it does not need to be relocated by the init code.
ER_IROM2 0x0001E204 FIXED 0x000205FC { *libMetering*.a(+RO) ; place all RO code objets from libMeter here }
Hope this helps, Ronan
Hi Ronan,
First of all, many thanks for your answer! Your hint with "FIXED" fixed the problem ;-) Regarding your question, I think you guessed correctly. If I examine 0x1e204 without the FIXED attribute, it shows 0xffffffff. Once I set the execution region to FIXED, the debugger reports the correct code from the object-file at address 0x1e204.
One further question: I noticed that with the attribute FIXED, the image grows massively in size. Do I assume correctly, that FIXED "reserves" the whole section, like a full provisioning on virtual disks?
thanks in advance
florian
Hi Florian,Following on from Ronan's answer:With your original scatter file, by default AC6 would load compress execution regions ER_IROM2 and RW_IRAM* and load them at earlier addresses: after the end of ER_IROM1 (0x00000e30) and before 0x40000 (the limit of the Load Region). These sections then get decompressed and copied during start-up of your program: this is the "scatterloading" function of the Arm C library.Now with the FIXED attribute you're telling the linker not to compress or move ER_IROM2 and to place it at it's correct address from the start. The larger binary size is due to padding that would have been inserted between the end of ER_IROM1 (at 0x00000e30) and 0x0001E204, the start of ER_IROM2.I agree with Ronan that it looks like the scatterloading was likely not happening.With scatter-loading, it's important when you're dubugging btw to run as far as main() and then inspect memory: the decompress and copy done by scatterloading happens before main(), so you need to run at least that far in order to see the results at their execution region addresses.In order to try and find out why the scatterloading wasn't running:a) do you know if you have any code that overrides the C library's startup or the scatterloading process?b) how are you calling the C library initialisation from your startup code/bootcode? (I think this should be a call to __main?)
Hi Stam,
Thank you for your explanation! It improved my "big picture" definitely (I'm in the process to crossover from GCC to AC6).Regarding the question if the startup code it's overriding the C library, I have to dig a bit deeper. We got the startup code from our SoC supplier, but he delivered the SDK for AC5 and I'm still porting our codebase directly to AC6. The only thing I can say for sure that it calls __main.