8.3.2 Writing an overlay manager for manually placed overlays

Overlays are not automatically copied to their runtime location when a function within the overlay is called. Therefore, you must write an overlay manager to copy overlays.

The overlay manager copies the required overlay to its execution address, and records the overlay that is in use at any one time. The overlay manager runs throughout the application, and is called whenever overlay loading is required. For instance, the overlay manager can be called before every function call that might require a different overlay segment to be loaded.

The overlay manager must ensure that the correct overlay segment is loaded before calling any function in that segment. If a function from one overlay is called while a different overlay is loaded, then some kind of runtime failure occurs. If such a failure is a possibility, the linker and compiler do not warn you because it is not statically determinable. The same is true for a data overlay.

The central component of this overlay manager is a routine to copy code and data from the load address to the execution address. This routine is based around the following linker defined symbols:

The implementation of the overlay manager depends on the system requirements. This procedure shows a simple method of implementing an overlay manager. The downloadable example contains a Readme.txt file that describes details of each source file.

The copy routine that is called load_overlay() is implemented in overlay_manager.c. The routine uses memcpy() and memset() functions to copy CODE and RW data overlays, and to clear ZI data overlays.

Note:

For RW data overlays, it is necessary to disable RW data compression for the whole project. You can disable compression with the linker command-line option --datacompressor off, or you can mark the execution region with the attribute NOCOMPRESS.

The assembly file overlay_list.s lists all the required symbols. This file defines and exports two common base addresses and a RAM space that is mapped to the overlay structure table:

code_base
data_base
overlay_regions

As specified in the scatter file, the two functions, func1() and func2(), and their corresponding data are placed in CODE_ONE, CODE_TWO, DATA_ONE, DATA_TWO regions, respectively. armlink has a special mechanism for replacing calls to functions with stubs. To use this mechanism, write a small stub for each function in the overlay that might be called from outside the overlay.

In this example, two stub functions $Sub$$func1() and $Sub$$func2() are created for the two functions func1() and func2() in overlay_stubs.c. These stubs call the overlay-loading function load_overlay() to load the corresponding overlay. After the overlay manager finishes its overlay loading task, the stub function can then call $Super$$func1 to call the loaded function func1() in the overlay.

Procedure

  1. Create the overlay_manager.c program to copy the correct overlay to the runtime addresses.
    // overlay_manager.c
    /* Basic overlay manager */
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    /* Number of overlays present */
    #define NUM_OVERLAYS 2
    
    /* struct to hold addresses and lengths */
    typedef struct overlay_region_t_struct
    {
    	void* load_ro_base;
    	void* load_rw_base;
    	void* exec_zi_base;
    	unsigned int ro_length;
    	unsigned int zi_length;
    } overlay_region_t;
    
    /* Record for current overlay */
    int current_overlay = 0;
    
    /* Array describing the overlays */
    extern const overlay_region_t overlay_regions[NUM_OVERLAYS];
    
    /* execution bases of the overlay regions - defined in overlay_list.s */
    extern void * const code_base;
    extern void * const data_base;
    
    void load_overlay(int n)
    {
        const overlay_region_t * selected_region;
    
        if(n == current_overlay)
        {
            printf("Overlay %d already loaded.\n", n);
            return;
        }
        
        /* boundary check */
        if(n<1 || n>NUM_OVERLAYS)
        {
            printf("Error - invalid overlay number %d specified\n", n);
            exit(1);
        }
        
        /* Load the corresponding overlay */
        printf("Loading overlay %d...\n", n);
        
        /* set selected region */
        selected_region = &overlay_regions[n-1];
        
        /* load code overlay */
        memcpy(code_base, selected_region->load_ro_base, selected_region->ro_length);
        
        /* load data overlay */
        memcpy(data_base, selected_region->load_rw_base,
               (unsigned int)selected_region->exec_zi_base - (unsigned int)data_base);
        
        /* Comment out the next line if your overlays have any static ZI variables
         * and should not be reinitialized each time, and move them out of the
         * overlay region in your scatter file */
        memset(selected_region->exec_zi_base, 0, selected_region->zi_length);
    
        /* update record of current overlay */
        current_overlay=n;
        
        printf("...Done.\n");
        
    }
  2. Create a separate source file for each of the functions func1() and func2().
    // func1.c
    #include <stdio.h>
    #include <stdlib.h>
    
    extern void foo(int x);
    
    // Some RW and ZI data
    char* func1_string = "func1 called\n";
    int func1_values[20];
    
    void func1(void)
    {
        unsigned int i;
        printf("%s\n", func1_string);
        for(i = 19; i; i--)
        {
            func1_values[i] = rand();
            foo(i);
            printf("%d ", func1_values[i]);
        }
        printf("\n");
    }
    // func2.c
    #include <stdio.h>
    
    extern void foo(int x);
    
    // Some RW and ZI data
    char* func2_string = "func2 called\n";
    int func2_values[10];
    
    void func2(void)
    {
        printf("%s\n", func2_string);
        foo(func2_values[9]);
    }
  3. Create the main.c program to demonstrate the overlay mechanism.
    // main.c
    #include <stdio.h>
    
    
    /* Functions provided by the overlays */
    extern void func1(void);
    extern void func2(void);
    
    int main(void)
    {
        printf("Start of main()...\n");
        func1();
        func2();
    
        /*
         * Call func2() again to demonstrate that we don't need to
         * reload the overlay
         */
        func2();
    
        func1();
        printf("End of main()...\n");
     
        return 0;
    }
    
    void foo(int x)
    {
        return;
    }
  4. Create overlay_stubs.c to provide two stub functions $Sub$$func1() and $Sub$$func2() for the two functions func1() and func2().
    // overlay_stub.c
    extern void $Super$$func1(void);
    extern void $Super$$func2(void);
    
    extern void load_overlay(int n);
    
    void $Sub$$func1(void)
    {
        load_overlay(1);
        $Super$$func1();
    }
    
    void $Sub$$func2(void)
    {
        load_overlay(2);
        $Super$$func2();
    }
  5. Create overlay_list.s that lists all the required symbols.
    ; overlay_list.s
        AREA    overlay_list, DATA, READONLY
    
        ; Linker-defined symbols to use
        
        IMPORT ||Load$$CODE_ONE$$Base||
        IMPORT ||Load$$CODE_TWO$$Base||
        IMPORT ||Load$$DATA_ONE$$Base||
        IMPORT ||Load$$DATA_TWO$$Base||
    
        IMPORT ||Image$$CODE_ONE$$Base||
        IMPORT ||Image$$DATA_ONE$$Base||
        IMPORT ||Image$$DATA_ONE$$ZI$$Base||
        IMPORT ||Image$$DATA_TWO$$ZI$$Base||
    
        IMPORT ||Image$$CODE_ONE$$Length||
        IMPORT ||Image$$CODE_TWO$$Length||
    
        IMPORT ||Image$$DATA_ONE$$ZI$$Length||
        IMPORT ||Image$$DATA_TWO$$ZI$$Length||
    
        ; Symbols to export
        
        EXPORT code_base
        EXPORT data_base
        EXPORT overlay_regions
    
    ; Common base execution addresses of the two OVERLAY regions
    
    code_base DCD ||Image$$CODE_ONE$$Base||
    data_base DCD ||Image$$DATA_ONE$$Base||
    
    ; Array of details for each region -
    ; see overlay_manager.c for structure layout
    
    overlay_regions
    ; overlay 1
        DCD ||Load$$CODE_ONE$$Base||
        DCD ||Load$$DATA_ONE$$Base||
        DCD ||Image$$DATA_ONE$$ZI$$Base||
        DCD ||Image$$CODE_ONE$$Length||
        DCD ||Image$$DATA_ONE$$ZI$$Length||
        
    ; overlay 2    
        DCD ||Load$$CODE_TWO$$Base||
        DCD ||Load$$DATA_TWO$$Base||
        DCD ||Image$$DATA_TWO$$ZI$$Base||
        DCD ||Image$$CODE_TWO$$Length||
        DCD ||Image$$DATA_TWO$$ZI$$Length||
    
        END
  6. Create retarget.c to retarget the __user_initial_stackheap function.
    // retarget.c
    #include <rt_misc.h>
    
    extern unsigned int Image$$HEAP$$ZI$$Base;
    extern unsigned int Image$$STACKS$$ZI$$Limit;
    
    __value_in_regs struct __initial_stackheap __user_initial_stackheap(
            unsigned R0, unsigned SP, unsigned R2, unsigned SL)
    {
        struct __initial_stackheap config;
    
        config.heap_base = (unsigned int)&Image$$HEAP$$ZI$$Base;
        config.stack_base = (unsigned int)&Image$$STACKS$$ZI$$Limit;
    
        return config;
    }
  7. Create the scatter file, embedded_scat.scat.
    ; embedded_scat.scat
    ;;; Copyright Arm Limited 2002. All rights reserved.
    
    ;; Embedded scatter file
    
    ROM_LOAD 0x24000000 0x04000000
    {
        ROM_EXEC 0x24000000 0x04000000
        {
            * (InRoot$$Sections)      ; All library sections that must be in a root region
                                      ; e.g. __main.o, __scatter*.o, * (Region$$Table)
            * (+RO)                   ; All other code
        }
        
        RAM_EXEC 0x10000
        {
            * (+RW, +ZI)
        }
    
        HEAP +0 EMPTY 0x3000
        {
        }
    
        STACKS 0x20000 EMPTY -0x3000
        {
        }
    
        CODE_ONE 0x08400000 OVERLAY 0x4000
        {
            overlay_one.o (+RO)
        }
    
        CODE_TWO 0x08400000 OVERLAY 0x4000
        {
            overlay_two.o (+RO)
        }
        
        DATA_ONE 0x08700000 OVERLAY 0x4000
        {
            overlay_one.o (+RW,+ZI)
        }
        
        DATA_TWO 0x08700000 OVERLAY 0x4000
        {
            overlay_two.o (+RW,+ZI)
        }
        
    }
  8. Build the example application:
    armclang -c -g -target arm-arm-none-eabi -mcpu=cortex-a9 -O0 main.c overlay_stubs.c overlay_manager.c retarget.c
    armclang -c -g -target arm-arm-none-eabi -mcpu=cortex-a9 -O0 func1.c -o overlay_one.o
    armclang -c -g -target arm-arm-none-eabi -mcpu=cortex-a9 -O0 func2.c -o overlay_two.o
    armasm --debug --cpu=cortex-a9 --keep overlay_list.s
    armlink --cpu=cortex-a9 --datacompressor=off --scatter embedded_scat.scat main.o overlay_one.o overlay_two.o overlay_stubs.o overlay_manager.o overlay_list.o retarget.o -o image.axf
Non-ConfidentialPDF file icon PDF versionDUI0773J
Copyright © 2014–2017, 2019 Arm Limited or its affiliates. All rights reserved.