如何在Cortex-M上的“noreturn”函数中重置堆栈指针?

问题描述 投票:2回答:1

为了减少堆栈溢出的可能性,我想在输入永不返回的函数后重置堆栈指针。在我的代码中有两种情况发生,main()和shutdown_immediate()ISR将数据保存到闪存并进入深度睡眠状态。我使用LTO使代码适合,因此main()最终成为一个非常大的函数,需要为局部变量分配部分堆栈。

我的第一次尝试是使用__attribute__ ((noreturn))结合调用__builtin_unreachable(),但这不会以任何方式改变生成的程序集。然后我创建了一个内联汇编函数,将堆栈指针重置为最后一个在main()和shutdown_immediate()处理程序中调用的SRAM地址。

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发生在为局部变量等分配堆栈之后。一旦主要开始分支,这将失败。

生成的汇编代码:

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-臂无 - EABI-6-2017-Q2更新
  • GCC-臂无 - EABI-8-2018-Q4,主要
gcc arm embedded cortex-m
1个回答
0
投票

最强大的解决方案是在汇编中实现main,并在重置堆栈后从那里跳转到用户的main(根据需要重命名)。从长远来看,围绕编译器逻辑的攻击太脆弱了。

© www.soinside.com 2019 - 2024. All rights reserved.