This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

Self-contained C module or PIC

I need to write a self-contained module in C (is this even possible?) which is a part of a bigger app. This module should be loaded from some mass memory (most probably microSD card) to RAM and executed from there.
Actually, this code doesn't need to be position independent, because I can guarantee that the execution address in RAM is always the same. There is only one simple access function (kind of a data translator), it does not use any static variables, and can be (and, for now, it is) fully reentrant.

Of course, there is a dedicated load region for this module defined in the scatter file.
But:
- how to force the compiler to not use standard library function calls to the main app (as the adresses of library functions could change when we release new versions of the firmware)
or
- how to force the compiler to embed copies of library functions in the same region as the installable module, independent of what is in the main app,
or
- make the module ROPI, but then, how to call my access function from the main code? I thought, the only thing I need is a single const pointer to this function, placed in the first four bytes of the dedicated load region (still at an absolute address, remember?). I event created a section inside this region for this pointer only, declared it inside the my module and statically intialized it with the address of the access function. But the compiler complains about it, warning me like 'static initialization can cause link failure --ropi'.

After many hours of learning about this stuff I think, making the module ROPI does not change a thing, because there can still be calls to absolute addresses in the main app. Am I right?

Please help... I spent for now a few days looking for solutions, but found nothing really helpful ;( There are for real many questions about ropi or installable modules but still no precise answers...

  • Build the module as a stand-alone application, i.e. make it a separate project.

  • Build it as a separate, freestanding project, and create your interface in the startup.s (or equivalent) file. This way it won't have any dependency on your main code.

    Define the IROM section as the area of RAM you expect to load it, and another RAM area for IRAM where it can put it's statics, variables, stack, etc. Define the sizes you carve out to match your needs.

    Say IROM 0x20000000,0x1000, IRAM 0x20001000,0x1000

    Other than that you could also dynamically memory, and use an executable/object format, and write a loader.

    Are you familiar with the concepts/implementation of DLLs?

  • Building the module as a separate project, is an acceptable solution. But my startup.s file in this project can not declare any stack, heap or vectors, because the code won't (and can't!) make use of them in a standalone way. This applies mainly to the stack. So my startup code should look like

    IMPORT my_access_function_exec
    AREA DATA,READONLY
    DCD my_access_function_exec
    END
    


    Right?

    The only lame thing about it is, I can't run or debug it without the main app. And debugging this code from within the main app will be a bit nasty, too...

    I really like the idea of using an executable format and a separate loader. But how to make an executable image like this? I would prefere an elf file which is easy to parse and interpret. I do have some knowledge about DLLs, also. It is no problem to build a DLL for Windoze on a PC (I have done this many times), but I have no clue how to build modules, which would work in a similar way, using Keil.
    And building them is only one thing, the other is to debug them in a usual way like any other app...

  • How much debugging do you need to be doing on the embedded system? If it's C code not tied to any specific hardware resources you can test/validate on a more capable system.

    Not sure of the purpose of this module/overlay scheme, but you could always fixture it directly in some test application code, that perhaps uses some #define's and #includes the module code.

    The Keil debugger can transition into/out of code for which you don't have source. You can output data via USART/DCC/SWV

  • It works! I built my module as a separate project, and converted with fromelf the output elf file to a C array. In the main app I wrote a 'loader' which copies this array to the destination. Then I set a function pointer to the DCD'd address in the module, and run the function. A test routine worked perfectly ;]
    Thanks very much, Westonsupermare Pier.

    To share the knowledge with others, I post the codes here.
    Module's asm.s file:

    RAMLOC  EQU 0x2001E000
    
                    PRESERVE8
                    THUMB
    
                    IMPORT exec
                    AREA FIRSTDCD,DATA,READONLY
                    DCD RAMLOC ; help to verify the expected load address
                    DCD exec ; the real function pointer
                    END
    

    C source file:

    void exec(void *indata,void *outdata)
    {
    //do some
    }
    

    scatter file:

    LR_IRAM1 0x2001E000 0x00002000  {
    
      ER_IROM1 0x2001E000 {
       asm.o (FIRSTDCD, +First)
       .ANY (+RO)
      }
      RW_IRAM1 +0  {  ; RW data
       .ANY (+RW +ZI)
      }
    }
    

    What we get in the binaries after building the module:

    u8 destination[]=
    { 0x00,0xE0,0x01,0x20, //expected load address
    0x09,0xE0,0x01,0x20, //the actual pointer to exec (this address will vary)
    0x2D,0xE9,0xF0,0x41,... //that's the rest of the code
    }

    In the main app, after copying the data to destination, just set a function pointer to the second u32:

    typedef void (*execfunc_t)(void*,void*);
    execfunc_t execfunc=(execfunc_t)((u32*)destination[1]);

    and execute:

    u32 idata[2],odata[2];
    execfunc(idata,odata);

    And that's it ;)

    But I have one more question, about PIC - how can I force the compiler to prepend the machine code with an address offset of one (or more) selected functions in the PIC region, like in my non-PI code above?

    Or should I just write and use an elf loader?

  • An ELF loader is probably overkill, the form generally output by the linker is a fixed address form, the object version generated by the compiler is a bit too involved. The latter you're basically writing a linker rather than a loader, and the former you might as well just dump hex or binary. If you're committed to a fixed address, then binary would be the way to go, with some vector table or index of functions at the front of the applet.

    I've built ELF linkers/loaders, I'd probably tend to a simpler format that addressed the import/export requirements of the modules, and any relocations required for a flexible load address. I'd do the translation on a PC as a post link step, or directly with the object(s)

    For an offset something like this perhaps?

    basis           DCD RAMLOC ; help to verify the expected load address
                    DCD exec - basis; the real function pointer offset
    

  • Placing my module at a fixed address works for me now, but in the future reserving large spaces of RAM at fixed addresses can be annoying...

    [...]I'd probably tend to a simpler format that addressed the import/export requirements of the modules, and any relocations required for a flexible load address. I'd do the translation on a PC as a post link step[...]
    


    You're right. Having a binary executable with PI code, the only things I need to know are offsets to functions, beginning from zero base address (assume zero for simplicity). In my application, these functions don't use any global RW or ZI, so offsets only are good enough.

    The question is, is it possible to embed these offsets in an array placed at a fixed offset (zero would be nice) in a binary output file, thus avoiding post link steps, using the compiler and/or linker only?
    Like this:

    u32 func_ptr_array_size; // <- at offset 0
    u32 func_offsets[2]; //assume size==2
    u8 code[];
    

    I tried to achieve this with static initialization of an array of function pointers, and placing this array in a dedicated section, defined in the scatter file. But this won't link...

  • Observe how the assembler, linker, and scatter file work together on Cortex-M3/4 parts to place a vector table at the front of an image, and adapt to your needs. The new vector table for your applet format could include signatures, length, and a list of entry points, etc.

  • As I wrote earlier, all things work fine if position independency is not used. Marking the load region as PI and compiling the .c and .s files with --ropi causes link failure, because of the DCD in the .s file

                    PRESERVE8
                    THUMB
    
                    AREA FIRSTDCD,DATA,READONLY
                    DCD exec_vector ; here is the problem
    
                    AREA |.text|,CODE,READONLY
    
    exec_vector PROC ; dummy test function
                    EXPORT exec_vector [WEAK]
                    add r0,r0,r0
                    bx lr
                    ENDP
    
                    END
    

    The linker sais "asm.o FIRSTDCD in PI region 'ER_IRAM1' cannot have address type relocation to exec_vector in PI region 'ER_IRAM1' "...
    This DCD gives me the address of the access function, and without that address, all this stuff is pointless ;( I would like an offset here instead the absolute address...
    A classic startup file does not compile as position independent, too. Is there a obvious solution that I don't get, or is it more complicated?