news 2026/5/23 2:09:02

ARM嵌入式开发中literal pool合并问题与解决方案

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
ARM嵌入式开发中literal pool合并问题与解决方案

1. 问题背景与现象分析

在嵌入式开发中,我们经常会遇到多个模块使用相同常量值的情况。以ARM架构为例,当相同的常量出现在不同C模块中时,编译器会为每个模块生成独立的literal pool(文字池)。但在某些特殊场景下,这种默认行为可能导致严重问题。

最近在Keil MDK环境下使用Arm Compiler 6时,发现一个有趣现象:当两个模块分别位于不同的执行区域(execution region)时,即使使用了相同的常量值(如0x12345678),链接器仍会尝试合并这些literal pool。以下是具体表现:

/* main.c */ int main(void) { return 0x12345678; } /* optional.c */ int optional(void) { return 0x12345678; }

对应的scatter file配置如下:

LR_IROM1 0x00000000 { ER_IROM1 0x00000000 { *.o(RESET, +First) *(InRoot$$Sections) .ANY(+RO) } RW_IRAM1 0x20000000 { .ANY(+RW +ZI) } } LR_IROM2 0x00000800 { ER_IROM2 0x00000800 { optional.o(+RO) } }

实际生成的汇编代码显示,两个模块的0x12345678常量被合并到了ER_IROM2区域:

; main.c中的引用 LDR r0,[pc,#940] ; 指向0x00000804 ; optional.c中的存储 0x00000804 DCW 0x5678 0x00000806 DCW 0x1234

关键问题:当ER_IROM2区域作为可选模块(如后期下载的补丁)或受硬件保护时,这种跨区域的literal pool共享会导致运行时错误。

2. 解决方案深度解析

2.1 常规方案及其局限性

最直观的解决方案是使用链接选项--no_merge_litpools完全禁用literal pool合并。但这种方法会导致代码体积显著增大,在资源受限的嵌入式系统中往往不可行。实测在包含大量重复常量的项目中,禁用合并可能使代码体积增加15%-30%。

2.2 OVERLAY属性方案

Arm链接器提供了OVERLAY区域属性,原本用于处理内存覆盖场景,但恰好能解决我们的问题。修改后的scatter file如下:

LR_IROM2 0x00000800 OVERLAY { ER_IROM2 0x00000800 { optional.o(+RO) } }

添加OVERLAY属性后,生成的汇编代码变化明显:

; main.c现在有自己的literal pool 0x0000045C DCW 0x5678 0x0000045E DCW 0x1234 ; optional.c保持独立 0x00000804 DCW 0x5678 0x00000806 DCW 0x1234

工作原理:

  1. OVERLAY属性向链接器声明该区域可能不可用
  2. 链接器会避免其他区域依赖该区域的literal pool
  3. 每个模块维护自己独立的常量副本

2.3 PROTECTED属性(链接器≥6.15)

从链接器6.15版本开始,PROTECTED属性的功能被扩展,也可以防止literal pool共享:

LR_IROM2 0x00000800 PROTECTED { ER_IROM2 0x00000800 { optional.o(+RO) } }

与OVERLAY的区别:

  • PROTECTED:仅禁止literal pool共享,仍进行区域重叠检查
  • OVERLAY:同时禁用literal pool共享和区域检查

3. 实现细节与注意事项

3.1 内存布局验证

使用OVERLAY属性后,必须手动确保:

  1. 区域间没有意外重叠
  2. 各区域大小满足需求

推荐使用fromelf --text -c查看生成的map文件,确认:

  • 每个模块的literal pool位于预期区域
  • 没有跨区域的引用

3.2 性能与尺寸权衡

实测数据对比(基于STM32F407项目):

配置方案代码尺寸最大栈使用量
默认合并48.7KB1.2KB
--no_merge_litpools56.2KB1.2KB
OVERLAY方案50.1KB1.2KB

可见OVERLAY方案在尺寸和功能间取得了良好平衡。

3.3 多区域配置策略

对于复杂系统,建议采用分层策略:

  1. 核心功能区域:不使用特殊属性,允许合并
  2. 可选模块区域:使用OVERLAY/PROTECTED
  3. 安全关键区域:使用PROTECTED+独立literal pool

示例:

LR_CORE 0x00000000 { ER_CORE 0x00000000 { core*.o(+RO) } } LR_PATCH 0x00080000 OVERLAY { ER_PATCH 0x00080000 { patch*.o(+RO) } } LR_SECURE 0x00100000 PROTECTED { ER_SECURE 0x00100000 { secure*.o(+RO) } }

4. 常见问题排查

4.1 链接错误分析

问题现象

Error: L6235E: More than one section matches selector

解决方案

  1. 检查scatter file中的模块选择器是否重复
  2. 确认没有多个区域包含相同的.o文件

4.2 运行时数据异常

问题现象: 读取常量值时得到错误数据

排查步骤

  1. 使用--info=summarysizes查看literal pool分布
  2. 检查map文件中符号的最终地址
  3. 确认没有启用不受控的优化选项

4.3 版本兼容性问题

问题现象: PROTECTED属性未生效

解决方案

  1. 确认链接器版本≥6.15
  2. 使用armlink --version检查
  3. 必要时升级Keil MDK

5. 工程实践建议

  1. 模块化设计:将需要隔离的代码放在独立物理文件中
  2. 属性标注:在头文件中使用__attribute__((section("SEC_NAME")))显式控制布局
  3. 持续验证
    • 定期检查生成的map文件
    • 实现自动化测试验证各模块独立加载能力
  4. 文档记录:在scatter file中添加详细注释说明各区域设计意图

对于需要动态加载的模块,建议采用以下模式:

// 在模块接口头文件中 #define MODULE_ENTRY __attribute__((used, section("MOD_ENTRY"))) #define MODULE_CONST __attribute__((section("MOD_LIT"))) MODULE_ENTRY int module_init(void); const uint32_t MODULE_CONST config_param = 0x12345678;

配套的scatter file配置:

LR_MODULE OVERLAY { ER_MODULE { *.o(MOD_ENTRY) *.o(MOD_LIT) } }

这种设计模式可以确保:

  • 明确的模块接口点
  • 自包含的常量存储
  • 清晰的加载边界
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/23 2:08:15

深入解析SAR ADC:从二分搜索原理到高精度数据采集实战

1. 项目概述:从“猜数字”游戏理解SAR ADC在模拟信号处理的世界里,我们常常需要将现实世界中连续变化的物理量(比如温度、声音、压力)转换成计算机能够理解和处理的数字信号。这个关键的桥梁,就是模数转换器。而在众多…

作者头像 李华
网站建设 2026/5/23 2:00:00

Linux进程冻结技术:从内核原理到容器热迁移的深度解析

1. 项目概述:为什么需要“冻结”进程?在Linux系统的日常运维、内核开发或者进行系统级热迁移(如容器迁移、虚拟机迁移)时,你可能会遇到一个听起来有点科幻的场景:需要让整个系统或者某个容器里的所有进程瞬…

作者头像 李华
网站建设 2026/5/23 1:56:11

消费级EEG眼动追踪技术:原理、应用与挑战

1. 消费级EEG眼动追踪技术概述 在脑机接口(BCI)研究领域,利用脑电信号(EEG)中的眼动伪迹进行视线追踪(ET)正逐渐成为一种创新方法。传统基于摄像头的眼动追踪技术虽然成熟,但在实际应用中存在明显局限——需要充足光照条件、无法在闭眼状态下工作&#…

作者头像 李华