Search code examples
cembeddediar

How to define a symbol for total image size or its end address in IAR linker script?


I'd like to define a symbol in IAR C program to get the total size (or the end address) of the image, in the build time, so that this size or offset will be contained in the image after linking.

(I know that this can be done in a post-build action. Also, I know how to do this with the gnu linker)

The IAR documentation on expressions in the link scripts says about functions 'size' and 'end'. The problem is that I don't know the section or segment names.

From the map file, the linker creates sections named P1, P2, A1, and so on. The read-only code + data + init data section is likely what I want, it is named "P1".

Snippet from the map file:

*******************************************************************************
*** PLACEMENT SUMMARY
***

"A0":  place at address 0x800'8000 { ro section .intvec };
"P1":  place in [from 0x800'8000 to 0x807'ffff] { ro };
.......... etc.......

I tried: define exported symbol MY_SIZE = size("P1"); or define exported symbol MY_SIZE = size(P1); but get error message "Error[Lc007]: expected an identifier". "Error[Lc010]: "P1" is not a region name".

What is the correct argument for size and end functions? Or any other way to do this?


Solution

  • Yes. The current IAR linker comes with the start(), end() and size() functionalities. However, as for now, those only apply to regions.

    define exported symbol _start_address = start(ROM_region);
    define exported symbol _end_address = end(ROM_region);
    

    However, in the linker configuration, regions are bound to literals. So, here the "_start_address" will refer to the initial address of the ROM_region and "_end_address" will be the ending address, which is independent from the program size.

    An alternative approach for getting where the user program itself starts/ends can be to place 2 rooted dummy sections as wrappers around the sections actually being placed in the ROM_region:

    define root section _start_section with alignment = 4 { public _ss: udata32 0xA5A5A5A5; };
    define root section _end_section with alignment = 4 { public _es: udata32 0xBABABABA; };
    
    "ROM": place in ROM_region { first section _start_section,
                                 ro,
                                 last section _end_section };  
    

    From the C program (/debugger) these addresses can be accessed directly via "extern":

    #include <stdio.h>
    void main() {
      extern const void * _ss;
      extern const void * _es;
      printf("Initial 32-bit address: 0x%08x\n", &_ss);
      printf("Final 32-bit address: 0x%08x\n", &_es);
    }
    /* Output example (Terminal I/O) */
    // Initial 32-bit address: 0x08000000
    // Final 32-bit address: 0x08000B68
    

    If your program's "ROM_region" has complex partitioning, the same concept would still be potentially usable as far as the "start section" and "end section" are placed in a fixed order block which lumps all the sections/blocks other than "ro":

    define block RO_BLOCK with fixed order, end alignment = 4 {
                                             section _start_section,
                                             ro,
                                             /* sections/blocks other than "ro" */
                                             section _end_section };
                                             
    "ROM": place in ROM_region { block RO_BLOCK };                                  
    

    Either way, the addresses are defined within the ELF at linking time, without the need for an external utility to patch it during a post-build step.