You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

STM32跨固件通过固定地址调用函数返回时触发HardFault问题咨询

问题描述

首先,抱歉我的英语水平有限,我是一名巴西开发者,目前正在同时学习英语和编程。
我正在开发基于STM32单片机的固件应用,存储地址划分为引导加载程序BL、固件FWA、固件FWB三个分区。为了节省Flash存储空间,FWB需要调用FWA中定义的函数,目前我遇到一个无法定位原因的问题:我可以成功进入目标函数并执行内部的所有操作,但当执行到return返回语句时就会触发HardFault错误。我已经使用分散加载文件配置了FWA和FWB的共享内存,分散加载文件配置如下:

LR_IROM1 0x08004000 0x00004000  {    ; load region size_region
  ER_IROM1 0x08004000 0x00004000  {  ; load address = execution address
   *.o (RESET, +First)
   *(InRoot$$Sections)
   .ANY (+RO)
  }
  RW_IRAM1 0x20000000 UNINIT 0x00001000  {  ; No init data
   *(.noinit)
  }
  RW_IRAM2 0x20001000 0x00004000  {
   .ANY (+RW +ZI) //RW data
  }
}

我的目标函数定义如下,已指定存放于固定Flash地址:

uint getInt(void) __attribute__((section(".ARM.__at_0x8006000")));//My fw was at 0x4000, total size is 0x4000,
                                                                  //so space is not the problem.
uint getInt(void){
    return 42;
}

我在FWA中自测调用该函数的代码如下:

uint (*fun_ptr)(void) = (uint(*)(void))0x8006000;
uint _get_int= fun_ptr();
uint result = _get_int();

补充测试情况:当使用如下方式调用时所有场景都可以正常运行,只有通过绝对地址赋值函数指针调用时会出现问题:

IntFunc p = getInt;
uint ccccc = p();
uint aaaa = getInt();

如前文所述,我可以正常进入getInt()函数执行内部所有代码,只有执行到return 42语句时才会触发HardFault。
我已经找到了通过结构体封装函数指针的实现方案,可以正常运行,但我希望能了解当前问题的根本原因,避免后续再出现同类错误。可正常运行的实现方案如下:
main.h文件代码:

typedef struct bootloaderApi bootloaderApi;
typedef   unsigned int (*do_something_t)(void);
struct bootloaderApi{
    do_something_t do_something;
};

Main.c文件代码:

const struct bootloaderApi api __attribute__((section(".ARM.__at_0x8005000")))
= {.do_something = &getInt}; //address 0x5000 to test.

调用代码:

struct bootloaderApi *apit = (struct bootloaderApi *)0x8005000;
uint pa = api.do_something();

以上方案可以正常运行,pa可以正确返回数值42。

根本原因
  1. STM32使用的Cortex-M内核仅支持Thumb指令集,架构规范要求函数指针的最低位必须为1,用于标识当前处于Thumb执行状态。如果函数指针最低位为0,内核会尝试切换到不支持的ARM模式,直接触发硬件错误。
  2. 你直接将0x8006000强制转换为函数指针时,该地址最低位为0,不符合内核要求。调用函数时链接寄存器LR保存的返回状态位非法,执行return语句时内核检测到状态异常,触发HardFault。
  3. 结构体封装方案正常运行的原因:编译器处理&getInt这类函数地址取值操作时,会自动将地址最低位设置为1,符合Thumb指令集要求,因此调用不会出错。
    注:你提供的自测调用代码存在笔误,将函数指针调用后的uint返回值再次当成函数调用,本身也会触发非法地址访问,属于示例编写错误,不影响核心问题定位。
修复方案
  • 方案1:给绝对地址手动设置Thumb标志位,赋值函数指针时改为uint (*fun_ptr)(void) = (uint(*)(void))(0x8006000 | 1);即可正常调用。
  • 方案2:继续使用已验证的结构体封装函数表方案,该方案是跨固件分区调用的工业级常用方案,可扩展性更强,新增函数只需修改结构体定义即可,无需手动管理每个函数的地址。

内容的提问来源于stack exchange,提问作者Cristhian Guedes

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.06 18:27:04