gcc - 如何在 Cortex-M 上的“noreturn”函数中重置堆栈指针?
问题描述
为了减轻堆栈溢出的可能性,我想在输入一个永远不会返回的函数后重置堆栈指针。我的代码中有两种情况会发生这种情况,main() 和一个将数据保存到闪存并进入深度睡眠的 shutdown_immediate() ISR。我使用 LTO 使代码适合,因此 main() 最终成为一个相当大的函数,需要为局部变量分配堆栈的一部分。
我的第一次尝试是__attribute__ ((noreturn))
与调用结合使用 __builtin_unreachable()
,但这不会以任何方式更改生成的程序集。然后我创建了一个内联汇编函数来将堆栈指针重置为最后一个 SRAM 地址,该地址首先在 main() 和 shutdown_immediate() 处理程序中调用。
inline __attribute__((always_inline)) void NO_RETURN (void)
{
extern const uint32_t __stack_top__; // Defined in .ld file
asm volatile ("ldr r3, %[stack_top]\n"
"mov sp, r3\n"
: /* no outputs */
: [stack_top] "m" (__stack_top__)
: /* no clobbers */
);
}
int main (void)
{
NO_RETURN();
/* rest of the code here... */
}
void shutdown_immediate (void)
{
NO_RETURN();
}
这会为 ISR 生成看似正确的代码。main()
但是,在为局部变量等分配堆栈之后mov sp, r3
会发生这种情况。一旦 main 开始分支,这将失败。
生成的汇编代码:
00007f60 <shutdown_immediate>:
7f60: b570 push {r4, r5, r6, lr}
7f62: 4b21 ldr r3, [pc, #132] ; (7fe8 <shutdown_immediate+0x88>)
7f64: 681b ldr r3, [r3, #0]
7f66: 469d mov sp, r3
; ...
7fe8: 00202000 eoreq r2, r0, r0 ; last SRAM address
00001180 <main>:
1180: b5f0 push {r4, r5, r6, r7, lr}
1182: 4be7 ldr r3, [pc, #924] ; (1520 <main+0x3a0>)
1184: b097 sub sp, #92 ; 0x5c ; This SUB must be _after_ 0x1188!
1186: 681b ldr r3, [r3, #0]
1188: 469d mov sp, r3
; ...
1520: 00202000 eoreq r2, r0, r0 ; Last SRAM address
有没有人有任何技巧可以正确地做到这一点?我总是可以创建NO_RETURN()
函数的第二个变体,它将堆栈分配值作为参数,编译、反汇编、再次编译并在.sub sp, #nn
之后插入所需的mov sp, r3
,但这是一个混乱的解决方案。
架构:Cortex-M0
测试的工具链:
- gcc-arm-none-eabi-6-2017-q2-update
- gcc-arm-none-eabi-8-2018-q4-major
解决方案
最强大的解决方案是main
在汇编中实现并在main
堆栈重置后从那里跳转到用户(根据需要重命名)。从长远来看,破解编译器逻辑太脆弱了。
推荐阅读
- reactjs - 使用 React 打开显示时组件不更新数据
- php - 如何在laravel中添加来自数据库的时间
- javascript - 从 NodeJs 获取 HTML 而不是 JSON
- python - 在 Python 中使用 Selenium,有没有办法在登录在线帐户时使 webdriver/浏览器成为“可识别的设备”?
- python - 如何使用一系列列名从熊猫数据框中获取系列?
- css - 在加载时向文本添加过渡
- asp.net-mvc - 演员表
视图模型? - java - 在 OSLC4J 中,如何将属性添加到具有七个关联属性的资源中?
- oauth-2.0 - 通过命令行获取 Auth0 访问令牌
- reactjs - 创建 React 应用程序:根据 .env 变量在 package.json 中设置 HTTPS 标志