2021-12-08 10:56 AM
Hello guys.
I have an interesting project where I need to load user applications (binaries) from external flash memory (FATFS) into RAM and run them from there. Also, I want to use a static library (let's call it libAPI.a for example) that should be available for the main application and user applications. This library must be placed in the MCU flash memory at a specific address (this is already solved by the linker script).
(This is what I need to get. Function addresses are not real. They are just needed to demonstrate this problem.)
I have often used libraries as static and I had no problem with that. But in this project, I have to share library code between the main application and one of the user applications loaded into RAM. Therefore, the approach described above is not a good idea, because at link time the linker only fetches functions from libAPI.a that are needed for the current project. Thus, the addresses of these functions are different each time.
(This is what I have in fact. Function addresses are not real. They are just needed to demonstrate this problem.)
My question is, how can I get libAPI.bin instead of libAPI.a where ALL of its functions will be available, and how can I link it to both projects - main and user application?
Thanks in advance for any advice or ideas!
2021-12-08 11:03 AM
I should add details!
1) MCU - STM32H7
2) IDE - STM32CubeIDE
If I need to add something else about this topic just tell me!
2021-12-08 11:15 AM
Kind of a LINKER task, or a LOADER one.
You'd either need to make a shell application, with a large table of function entry points, making the linker build that into a binary, and perhaps have a means to register entry points into RAM allocation in your hosting application.
Alternatively you stay with ELF objects, and load and bind those from your host side loader. In a EXE / DLL, or shared object model.
I don't think the ELF object is overly complex, but one could also make a simplified or condensed object format more suited to specific needs. I might strip excessive debug and internal symbol noise all the same.
Historically you pass "definition" files to linkers to associate code in binary blobs. Might be simpler to have a structure you share, with function pointers.
2021-12-08 11:26 AM
>> Might be simpler to have a structure you share, with function pointers.
I fully agree with you, it would not be a bad solution, but I also need to link touchGFX in this way. For the touchGFX, therefore, it may be a difficult solution.
I am very tired. Tomorrow I will continue to think about this.
But I like your idea - it is simple and reliable for my library.
2021-12-08 11:38 AM
I've used definition files in ARM RealView to call out ROM based function entry point, lot of floating point stuff that would run faster, and printf stuff that was large, and the ROM wasn't something that could change within the product's production life span.
If think TouchGFX may have limitations, likely wanting a single / monolithic build to get closure. But like all things there may be ways to partition and compartmentalize things achieve the goal.
Look up wrappers and veneers in ARM/LINKER contexts.
An SVC Handler from the host side could also be considered.
2021-12-09 08:32 AM
Hello everyone and @Community member !
Here is our solution for simple libraries. I hope it will be useful to someone ))
1) create an empty project in STM32CubeIDE
2) ignore the startup file
3) in the main.c create some lowlevelInit function for initializing the data and bss sections and initializing the system library. It must be called from the host application before using this library!
void lowlevelInit(void)
{
/////////////////////////////////////
/// Data section
/////////////////////////////////////
extern uint32_t _sidata;
extern uint32_t _sdata;
extern uint32_t _edata;
volatile uint32_t* data_load = &_sidata;
volatile uint32_t* data = &_sdata;
volatile uint32_t* data_end = &_edata;
while( data < data_end ) {
*(data++) = *(data_load++);
}
/////////////////////////////////////
/// BSS section
/////////////////////////////////////
extern unsigned _sbss ;
extern unsigned _ebss ;
unsigned *bss = &_sbss ;
unsigned *bss_end = &_ebss ;
while(bss < bss_end ) {
*(bss++) = 0 ;
}
/////////////////////////////////////
/// System library Initialization
/////////////////////////////////////
extern __attribute__ ((weak)) void __libc_init_array(void);
if (__libc_init_array) {
__libc_init_array();
}
}
4) rename isr_vector section to api_vector in the linker file
/* Specify the memory areas */
MEMORY
{
FLASH (rx) : ORIGIN = 0x08180000, LENGTH = 512K
RAM_D1 (xrw) : ORIGIN = 0x24040000, LENGTH = 256K
}
/* Define output sections */
SECTIONS
{
/* The startup code goes first into FLASH */
.api_vector :
{
. = ALIGN(4);
KEEP(*(.api_vector)) /* Startup code */
. = ALIGN(4);
} >FLASH
/* The program code and other data goes into FLASH */
.text :
{
. = ALIGN(4);
*(.text) /* .text sections (code) */
*(.text*) /* .text* sections (code) */
*(.glue_7) /* glue arm to thumb code */
*(.glue_7t) /* glue thumb to arm code */
*(.eh_frame)
KEEP (*(.init))
KEEP (*(.fini))
. = ALIGN(4);
_etext = .; /* define a global symbols at end of code */
} >FLASH
5) In the main.c declare an array of function pointers on the library’s API
__attribute__ ((section(".api_vector"),used))
void (* const vectors[])(void) = {
(void (*const)(void))lowlevelInit,
(void (*const)(void))ver,
(void (*const)(void))foo,
(void (*const)(void))bar,
};
6) as a result, we have function pointers in the MyLib.bin
To use these functions inside of the host application we use the following approach:
#define LIBRARY_API_ADDRESS 0x08180000
void (*init)(void) = (void(*)(void))(*(uint32_t volatile *)((uint32_t) LIBRARY_API_ADDRESS + 0));
char* (*ver)(void) = (char*(*)(void))(*(uint32_t volatile *)((uint32_t) LIBRARY_API_ADDRESS + 4));
int (*foo)(int a, int b, int c) = (int(*)(int, int, int))(*(uint32_t volatile *)((uint32_t) LIBRARY_API_ADDRESS + 8));
int (*bar)(int a, int b) = (int(*)(int, int))(*(uint32_t volatile *)((uint32_t) LIBRARY_API_ADDRESS + 12));
init();
printf("version = %s\n", ver());
printf("foo = %d\n", foo(1, 2, 3));
printf("bar = %d\n", bar(6, 5));
So, as you can see, this method is not very good for TouchGFX.
Does anyone have any ideas on this issue?