我们有一个使用 ARM Cortex M33 处理器的大量嵌入式项目,用大约 30,000 行“C”编写。我们使用 Gnu 编译器/链接器工具链。混合了自定义软件、一些开源库(例如 FatFS)和特定于供应商的库(例如蓝牙堆栈)。我们有大约 50000 个占用 RAM 的命名静态变量和数据结构。对于一个结构,变量的大小范围从一个字节到数百字节不等。大多数情况下,变量是一个、两个或四个字节。
链接器按大小对单个文件中的所有变量进行分组,并在四字节和两字节变量组或两字节和一字节字节变量组之间切换时添加对齐字节。由于 RAM 有限,因此对齐丢失的 RAM 量已成为一个问题。
我想以某种方式指示编译器和链接器将一个字节的所有变量放入一个链接器部分,将两个字节放入另一个链接器部分,将四个字节放入第三个链接器部分,将其他所有变量放入第四个链接器部分。然后可以控制每个部分的对齐,并且对齐填充应该消失或最小化。如果我可以告诉链接器在分配内存地址时按大小对变量进行排序,它也会起作用。
我该怎么做,或者类似的事情? gnu 编译器和链接器手册似乎没有说明如何告诉编译器做这样的事情。为填补裂缝而消失的 RAM 量现在大约占我们总可用内存的 3%,我们快用完了。我正在寻找有创意的建议,并想听听您可能知道的任何部分解决方案。
我想以某种方式指示编译器和链接器将所有 一个字节的变量放入单个链接器部分,两个字节放入 另一个链接器部分
在链接描述文件中,定义要放置特定大小数据的部分。此示例仅适用于 1 个和两个字节 - 因为它们可能有问题。
_sidata8 = LOADADDR(.data8);
.data8 : ALIGN(1)
{
_sdata8 = .;
KEEP(*(.data8))
KEEP(*(.data8*))
_edata8 = .;
} >RAM AT> FLASH
_sidata16 = LOADADDR(.data16);
.data16 : ALIGN(2)
{
_sdata16 = .;
KEEP(*(.data16))
KEEP(*(.data16*))
_edata16 = .;
} >RAM AT> FLASH
(
KEEP
只是因为我不使用那些变量,编译器很可能会优化它们)
当你声明变量时,将它们放在正确的部分。
char __attribute__((section(".data8"))) a;
short __attribute__((section(".data16"))) d;
char __attribute__((section(".data8"))) b;
short __attribute__((section(".data16"))) e;
char __attribute__((section(".data8"))) c;
short __attribute__((section(".data16"))) f;
结果:
请记住,您需要添加一些启动代码来初始化或清零您的特定部分。
例子
#if defined(__GNUC__)
extern uint8_t _sdata8[];
extern uint8_t _sdata16[];
extern uint8_t _edata8[];
extern uint8_t _edata16[];
extern uint8_t _sidata8[];
extern uint8_t _sidata16[];
static void __attribute__((constructor)) initDatax(void)
{
memcpy(_sdata8, _sidata8, _edata8 - _sdata8);
memcpy(_sdata16, _sidata16, _edata16 - _sdata16);
}
#endif
这将需要一些工作(添加属性 - 您可以使用宏定义来缩短它)但是您可以组织数据以避免变量之间的填充。它将在整个项目范围内工作——因此任何定义为 .data8 的数据都将放置在此部分中。
您可以告诉链接器按对齐方式排序,全局或每个部分。要对所有部分执行此操作,请添加:
--sort-section=alignment
你传递给
ld
的旗帜。
如果您使用
gcc
调用链接器,您需要将其添加到您的 CFLAGS:
-Wl,--sort-section=alignment
或者,您可以在链接器文件中以每个部分为基础进行操作,例如变化:
*(.data*)
到
*(SORT_BY_ALIGNMENT(.data*))
如果需要,.bss 部分也是如此。