GCC链接脚本中VMA与LMA不同导致Cortex-M4函数运行异常求助
Cortex-M4使用AT>指定LMA后函数执行触发HardFault问题解决
问题场景
使用类似STM32的Cortex-M4芯片,计划将高频调用函数放入FAST_FLASH区域优化性能,编写了带AT> FLASH的GCC链接脚本:
.flash_zw0 : ALIGN(4) { *(.flash_ex) } >FAST_FLASH AT> FLASH
编译后.map文件显示函数已归入指定段,但执行该段函数时直接进入HardFault。移除AT>配置后程序运行正常,但此时生成的bin文件因FAST_FLASH与FLASH地址跨度大而体积超标,无法完成升级。
芯片MEMORY配置如下:
MEMORY { FAST_FLASH (rx) : ORIGIN = 0x08014000, LENGTH = 48k FAST_FLASH_1 (rx) : ORIGIN = 0x08020000, LENGTH = 128k FLASH_0 (irx) : ORIGIN = 0x0807f800, LENGTH = 2k FLASH (rx) : ORIGIN = 0x08080000, LENGTH = 512K RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 96K }
根本原因
使用AT> FLASH时,链接器为.flash_zw0段设置了两个地址:
- VMA(虚拟内存地址):
FAST_FLASH的起始地址0x08014000,是CPU执行函数时访问的地址 - LMA(加载内存地址):
FLASH区域的地址,是bin文件中该段实际存储的位置
但Cortex-M4的启动流程只会自动将RAM段(如.data)从LMA复制到VMA,Flash段不会自动执行复制操作。此时FAST_FLASH的VMA区域没有有效代码,CPU跳转到该地址执行时读取到非法指令,直接触发HardFault。
解决方案
需要手动实现LMA到VMA的代码复制,确保函数执行前FAST_FLASH区域已加载有效代码:
1. 修改链接脚本导出段信息
在链接脚本中添加全局符号,导出.flash_zw0段的VMA起始/结束地址,以及LMA起始地址:
.flash_zw0 : ALIGN(4) { __flash_zw0_start = .; /* VMA起始地址 */ *(.flash_ex) __flash_zw0_end = .; /* VMA结束地址 */ } >FAST_FLASH AT> FLASH __flash_zw0_load_start = LOADADDR(.flash_zw0); /* LMA起始地址 */
2. 添加代码复制逻辑
在系统初始化阶段(SystemInit之后、main函数之前),添加复制代码,将FLASH中LMA区域的内容复制到FAST_FLASH的VMA区域。
C语言实现示例:
/* 声明链接脚本导出的全局符号 */ extern uint32_t __flash_zw0_start; extern uint32_t __flash_zw0_end; extern uint32_t __flash_zw0_load_start; void copy_fast_flash_sections(void) { uint32_t *src_addr = &__flash_zw0_load_start; uint32_t *dest_addr = &__flash_zw0_start; const uint32_t *dest_end = &__flash_zw0_end; /* 按字复制内容,确保对齐 */ while (dest_addr < dest_end) { *dest_addr++ = *src_addr++; } }
在启动文件中调用该函数,比如在Reset_Handler的末尾、跳转到main之前执行。
3. 处理Flash写保护
如果FAST_FLASH区域默认开启写保护,需要先调用芯片的Flash解锁API(类似STM32的HAL_FLASH_Unlock()),完成复制后再重新上锁,避免后续操作出错。
验证步骤
- 复制完成后,通过调试工具查看
FAST_FLASH区域(0x08014000开始)的内容,确认与FLASH中LMA区域的代码一致 - 执行
.flash_ex段的函数,确认不再触发HardFault - 生成bin文件,检查体积是否符合升级要求
内容的提问来源于stack exchange,提问作者Vermouth贝尔摩德
相关产品推荐
相关产品推荐

