Run an image with static library objects placed in a specific execution region

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

Parents
  • 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?)


Reply
  • 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?)


Children