多个独立编译的二进制文件/十六进制文件之间的代码共享



我正在寻找有关如何在为 Cortex-m/0/4/7 架构编译的多个二进制文件之间共享信息/代码的文档/信息。这两个二进制文件将位于同一芯片和相同的体系结构上。它们在不同的位置闪烁,并设置主堆栈指针并重置程序计数器,以便一个二进制文件"跳转"到另一个二进制文件。我想在这两个二进制文件之间共享代码。

我已经将函数指针数组简单复制到链接器脚本中定义的部分到 RAM 中。然后在另一个二进制文件中读出 RAM 并将其转换为数组,然后使用该索引调用另一个二进制文件中的函数。这确实可以作为概念验证,但我认为我正在寻找的东西有点复杂。因为我想要某种方法来描述两个二进制文件之间的兼容性。我想要一些共享库的功能,但我不确定我是否需要与位置无关的代码。

作为当前复制过程如何完成的示例,它基本上是:

源二进制文件:

void copy_func()
{
memncpy(array_of_function_pointers, fixed_size, address_custom_ram_section)
}

也从源二进制文件跳转的二进制文件:

array_fp_type get_funcs()
{
memncpy(adress_custom_ram_section, fixed_size, array_of_fp)
return array_of_fp;
}

然后,我可以使用该array_of_fp从跳转二进制调用驻留在源二进制文件中的函数。

因此,我正在寻找的是实施类似系统的人的一些资源或输入。就像我不想有一个自定义的 RAM 部分,我将函数指针复制到其中。

我可以让源二进制文件的编译步骤输出一些可以包含在跳转二进制文件的编译步骤中的东西。但是,它需要可重现,并且只要不更改接口,重新编译源二进制文件就不会破坏与跳转二进制文件的兼容性(即使它包含与现在输出的文件不同的文件)。

要阐明源二进制文件,不需要任何有关跳转二进制文件的特定知识。代码不应同时驻留在两个二进制文件中,因为这会破坏此机制的目的。如果此机制是在 cortex-m 处理器上创建多二进制应用程序时节省空间的一种方式,则总体目标是。

欢迎任何想法或资源链接。如果您还有其他问题,请随时对该问题发表评论,我会尽力回答。

我很难想象你想做什么,但如果你有兴趣在引导加载程序/ROM 上有一个应用程序链接,那么请参阅在链接时加载符号文件以获取有关您可以做什么的提示。

构建您的"源"(?)图像,抓取其映射文件并制作一个符号文件,然后在链接"跳转"(?)图像时使用它。

这确实意味着您需要将"跳转"图像链接到特定版本的"源"图像。

如果你需要它们与半版本无关(即你定义了一组被导出的函数,但你可以在任何一侧重建),那么你需要在"源"图像中的已知位置导出函数指针,并链接到"跳转"图像中的这些函数指针。 您可以通过使函数指针的结构通过任一端的函数来访问函数来简化簿记。

例如:

shared_functions.h:

struct FunctionPointerTable
{
void(*function1)(int);
void(*function2)(char);
};
extern struct FunctionPointerTable sharedFunctions;

"源"图像中的源文件:

void function1Implementation(int a) 
{
printf("You sent me an integer:  %drn", a);
function2Implementation((char)(a%256)) 
sharedFunctions.function2((char)(a%256));
}
void function2Implementation(char b) 
{
printf("You sent me an char:  %crn", b);
}
struct FunctionPointerTable sharedFunctions = 
{
function1Implementation,
function2Implementation,
};

"跳转"图像中的源文件:

#include "shared_functions.h"
sharedFunctions.function1(1024);
sharedFunctions.function2(100);

编译/链接"源"时,获取其映射文件并提取共享函数的位置,并创建一个与源链接的符号文件"跳转"图像。

注意:printfs(或共享函数直接调用的任何内容)将来自"源"图像(而不是"跳转"图像)。

如果您需要它们来自"跳转"图像(或可覆盖),则需要通过相同的函数指针表访问它们,并且"跳转"图像需要将函数指针表与其相关函数的版本固定在一起。 我更新了函数 1() 来显示这一点。 对 function2 的直接调用将始终是"源"版本。 它的共享函数调用版本将遍历跳转表并调用"源"版本,除非"跳转"图像更新函数表以指向其实现。

你可以摆脱结构,但随后你需要一个接一个地导出函数指针(这不是一个大问题),但你想让它们保持有序并固定在固定的位置,这意味着将它们显式地放在链接器描述符文件中,等等。 我展示了结构方法,将其提炼为最简单的示例。

如您所见,事情变得非常棘手,并且有一些惩罚(通过函数指针调用速度较慢,因为您需要加载要跳转到的地址)

如注释中所述,我们可以想象一个应用程序和一个引导加载程序依赖于相同的动态库。因此,应用程序和引导加载程序依赖于库,可以更改应用程序而不会影响库或引导。

我没有找到一种简单的方法来使用 arm-none-eabi-gcc 做一个共享库。然而 本文档提供了共享库的一些替代方法。我的情况,我会重新命令跳台解决方案。

编写一个库,其中包含需要在引导加载程序和应用程序中使用的函数。

"库"代码

typedef void (*genericFunctionPointer)(void)
// use the linker script to set MySection at a known address
// I think this could be a structure like Russ Schultz solution but struct may or may not compile identically in lib and boot. However yes struct would be much easyer and avoiding many function pointer cast. 
const genericFunctionPointer FpointerArray[] __attribute__ ((section ("MySection")))=
{
(genericFunctionPointer)lib_f1,
(genericFunctionPointer)lib_f2,
}
void lib_f1(void)
{
//some code
}
uint8_t lib_f2(uint8_t param)
{
//some code
}

应用和/或引导加载程序代码

typedef void (*genericFunctionPointer)(void)
// Use the linker script to set MySection at same address as library was compiled
// in linker script also put this section as `NOLOAD` because it is init by library and not by our code
//volatile is needed here because you read in flash memory and compiler may initialyse usage of this array to NULL pointers
volatile const genericFunctionPointer FpointerArray[NB_F] __attribute__ ((section ("MySection")));
enum 
{
lib_f1,
lib_f2,
NB_F,
}
int main(void)
{
(correctCastF1)(FpointerArray[lib_f1])();
uint8_t a = (correctCastF2)(FpointerArray[lib_f2])(10);
}

可以考虑使用链接器部分。如果您的引导加载程序源代码位于文件夹引导加载程序中,则可以使用

SECTIONS
{
.bootloader:
{
build_output/bootloader/*.o(.text)
} >flash_region1
.binary1: 
{
build_output/binary1/*.o(.text)
} >flash_region2
.binary2:
{
build_output/binary2/*.o(.text)
} >flash_region3 
}

最新更新