【问题标题】:What determines where the exception frame goes on Cortex-M4?什么决定了异常帧在 Cortex-M4 上的位置?
【发布时间】:2020-07-21 18:25:06
【问题描述】:

我正在努力处理写入本地/自动/堆栈变量的异常堆栈帧。

我正在为 SAM4L 使用 FreeRTOS 8.2.1 和 Microchip ASF 使用 Eclipse MCU 2018/09 和 Segger J-Link 6.40 进行开发。

[编辑]
第一次通过循环时,r7 具有不同的值(0x200044D0),看起来它可能是正确的值(与 SP 相同)。我现在在想,在等待 RTOS 消息队列时正在更改 r7,这发生在循环的顶部(但不是第一次!)

    for(;;){
        if(WaitTx(MSG_WAIT_TIME)){  calls xQueuePeek(...)
            do{
>> First time here, r7 has the value  0x200044D0
>> Subsequent times, r7 has the value 0x200044B0
                // Keep sending data until no more data 
                MsgBlock_t *tosend = BuildFrame(MSG_MAX_LEN);

                if(tosend){

[/编辑]

我有一个运行顶级循环的 RTOS 线程。线程函数中的两个局部变量正在被破坏。在其中一个变量上设置观察点,我看到它在执行中断处理程序的第二条指令时触发。内存转储显示异常堆栈帧已写入线程堆栈的 32 个字节。最低的 5 个堆叠值对应于寄存器 r0-r3,r12。想必其他3个对应着原来的lr、pc和xpsr。这些值看起来正确。 代码:

          TC02_Handler:
00013f84:   push    {r0, r1, r2, r4, r5, r6, r7, lr}
2141        tc_get_status(TC, BOARD_TC_CH_CMX);
00013f86:   movs    r1, #2            <<< Watchpoint triggers halt here
00013f88:   ldr     r0, [pc, #132]  ; 

注册:

r0 = 0x0             == Memory location 0x200044B0
r1 = 0x8             == Memory location 0x200044B4
r2 = 0x0             == Memory location 0x200044B8
r3 = 0x2000aab0      == Memory location 0x200044BC
r4 = 0x2000cd10
r5 = 0x0000cee3
r6 = 0x200044b0
r7 = 0x200044b0
r8-r11 all 0xa5a5a5a5  (RTOS fills stack with this value at startup)
r12= 0x6             == Memory location 0x200044C0
sp = 0x2000e3f8      (nowhere near where exception frame was stacked)
lr = 0xfffffffd

内存:

0x200044B0: 00000000 00000008 00000000 2000AAB0
0x200044C0: 00000006 0000CECB 0000CECC 01000000 
---
0x2000E3F8: 00000000 00000008 00000000 2000CD10
0x2000E408: 0000CEE3 200044B0 200044B0 FFFFFFFD

我不明白的是,当观察点在处理程序的入口处触发时,堆栈指针指向一个完全不同的位置。异常堆栈帧被写入位置 0x200044B0 到 0x200044CF,但堆栈指针(在观察点停止 micro 之后)的值是 0x2000E3F8。

异常处理程序的第一条指令压入 r0-r2、r4-r7 和 lr。这些值被推送到 sp 指向的堆栈位置 0x2000E3F8 - 0x2000E417

在进入异常处理程序时,堆栈指针是否应该指向异常堆栈帧的底部?

其他一些与我相关的线索

  • 调试器查看这些自动变量的错误地址。调试器“认为”我的变量应该位于 200044E4 和 200044E8 位置。

  • 当代码访问它们时,它们会从位置 200044C0 和 200044C4 加载。这些是作为从 r7 的偏移量访问的。

ldr r0,[r7,#16]  (r7 is 0x200044B0).
and 
ldr r0,[r7,#20]  (r7 is 0x200044B0).

  • 在被破坏的线程执行期间,堆栈指针的值为 0x200044d0,但 r7(我猜它被用作“帧指针”)的值为 0x200044b0。异常栈帧根据sp的值正确放置。

谢谢

【问题讨论】:

  • 答案取决于您用于给定目标/工具链的 freeRTOS 端口。 freeRTOS 文档中可能至少有一些抽象语句,所以你应该先检查一下。请注意,每个 freeRTOS 端口都可能以不同的方式处理这个问题。对于一个不同的 freeRTOS 端口,我看到 IRQ 处理程序将中断的上下文放置到专用的系统堆中,并将调度任务的上下文放置到它们各自的堆栈中。
  • 感谢@HelpingHand - 看起来初始中断堆栈帧进入活动线程的堆栈,然后 FreeRTOS 切换到专用中断堆栈。这个问题神奇地消失了(在 PC 重新启动后,但没有重建)。我唯一的猜测是设备 Flash 中留下了一些调试器代码,这把事情搞砸了。
  • 不客气 - 我很高兴你能解决你的问题。我从来没有听说过重新启动(开发)PC 可以解决出现在目标上的问题(或者它是由 freeRTOS 运行的 PC,哈哈……)。也许,您已经在构建 PC 上安装了一些工具或库,并且必须重新加载一些路径变量和脚本才能再次正常工作。

标签: gcc arm freertos cortex-m


【解决方案1】:

为了回答我自己的问题,初始堆栈帧直接进入当前线程的堆栈,然后 RTOS 可以切换到单独的中断堆栈。

我遇到的问题是保存堆栈帧的 r7 已损坏,因此堆栈帧中的变量(由 r7 引用)不在正确的位置。中断堆栈帧正在写入正确的位置,但由于 r7 损坏,变量位于错误的位置。

我不确定这是否是复活节奇迹,或者是否遵循所有 IT 专家的建议解决了它(您是否尝试过将其关闭并再次打开?)。无论哪种方式,重新启动我的电脑后问题都神奇地消失了,我无法重现它。我唯一的猜测是调试器在闪存中留下了一些它“忘记”的代码。无论如何,现在都解决了。

感谢收看。

【讨论】:

    猜你喜欢
    • 2021-01-18
    • 2017-01-20
    • 2020-03-22
    • 2018-12-04
    • 2017-06-23
    • 2020-11-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多