上下文为:使用arm-None-eabi-gcc编译的STM32 H753裸金属软件.

重置处理程序是用C实现的,并位于闪存中:

void reset_handler_c(void)
{
   asm_func();
}

asm函数在.s文件中实现,并位于RAM中:

.global asm_func

asm_func:
  ldr sp,=xxx
  bl entry_point
  bx lr

(事实上,这并没有多大意义,但这显然是一个简化的例子,只是为了重现这个问题)

生成的asm如下:

enter image description here

问题是:BLX指令只能取一个寄存器作为参数and a hardfault is generated at execution.摘录自STM32H7编程手册:

enter image description here

现在,如果我调用一个C函数,BLX将被BL替换,这是正确的:

enter image description here

你知道为什么gcc会生成这个奇怪的BLX指令吗?

编辑:编译选项为-mcpu=cortex-m7 -std=c99 -mfpu=fpv5-d16 -mfloat-abi=hard -mthumb -O1 ...

推荐答案

如果你调用的函数位于另一个节或对象中,函数地址是不知道的. 链接器在链接时生成并修复重定位. 要为重新定位插入正确的函数调用,链接器需要知道调用的函数是ARM还是Thumb函数. 它通过判断您调用的符号地址的最低有效位来知道这一点. 如果设置了,它会生成代码来调用Thumb函数. 如果它是明确的,它会生成代码来调用ARM函数. 这就是你的例子中出错的地方:地址的LSB是清除的,因此生成了一个调用ARM函数的BLX指令.

地址的最低有效位需要由thumb函数的汇编程序设置,以便工作. 然而,这里有一个小问题:设置最低有效位对于函数符号是正确的,但对于所有其他符号是错误的. 例如,您在文本部分放置了一个查找表,并希望通过一个符号访问它. 如果汇编程序在查找表的符号中设置LSB,则当您试图访问表时,它会引入离一错误. 出于这个原因,只有当您将符号声明为函数类型符号时,汇编程序才设置LSB. 为了实现这一点,您需要在定义符号的翻译单元中发出适当的.type指令:

.type asm_func, %function

正确声明了符号类型,汇编器将正确设置LSB,链接器将生成正确类型的函数调用.

不管架构如何,对每个涉及功能的符号都这样做是一个好习惯. 这将修复一些你可能会遇到的扩散问题.

另外,通过发出

.thumb

指令作为源代码中的第一件事. 我想你已经这样做了. 更改目标不会改变发出的模式代码,但可能会导致汇编程序拒绝在ARM模式下汇编代码,这至少是构建时可见的失败,而不是运行时的silent失败.

C++相关问答推荐

了解一些CLIPS原语数据类型

单指针和空参数列表之间的函数指针兼容性

将指针作为参数传递给函数

使用双指针动态分配和初始化2D数组

函数内的局部字符指针

如何只获取字符串的第一个单词,然后将其与c中的另一个单词进行比较?

当b是无符号字符时,int a=(b<;<;2)>;>;2;和int a=b&;0x3F;之间有什么区别?

这个空指针类型的转换是有效代码还是恶意代码?

我在C程序的Flex/Bison中遇到语法错误

C程序向服务器发送TCPRST

隐藏测试用例无法在c程序中计算位数.

';malloc():损坏的顶部大小';分配超过20万整数后

浮点正零何时不全为零?

在git补丁中自动添加C的宏

访问未对齐联合的成员是否为未定义行为,即使被访问的成员已充分对齐?

#define X Defined(Y) 是有效的 C/C++ 宏定义吗?

在C中定义函数指针?

是什么阻止编译器优化手写的 memcmp()?

无法在线程内用 C 打印?

inline 关键字导致 Clion 中的链接器错误