为什么Cortex-M架构中堆栈只保存当前导致故障的指令地址,而不保存下一条指令地址?

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

在查看开源代码CmBacktrace时,发现中断中导出的callstack的最后一级执行位置是直接从堆栈内容(R0-R3、R12、LR、PC、PSR)读取后的PC值进入中断。不会从其值中减去 4。 根据我之前的理解,在Cortex-M系列单片机中,PC的值就是当前正在执行的指令的下一条指令的地址。为此我在臭氧上进行了测试。 正常代码执行过程中,PC与当前执行代码的关系:

Test image

通过异常命令主动触发异常,观察中断服务函数中断时压栈的PC值与异常指令地址的关系:

Test image2.1

Test image2.2

通过上面两条测试可以看出,当代码正常执行时,PC值确实是当前执行指令的下一条指令的地址。而触发异常时保存的确实是触发异常的指令的地址。

为什么进入异常时保存的PC值是当前指令地址而不是下一条指令地址?而如果入口是中断,从中断返回时加载到PC中的值是否需要在保存的PC值上加4?

arm cortex-m mcu
1个回答
0
投票
0800005a <test1>:
 800005a:   4678        mov r0, pc
 800005c:   4770        bx  lr

0800005e <test2>:
 800005e:   4670        mov r0, lr
 8000060:   4770        bx  lr

 800026c:   f7ff fef7   bl  800005e <test2>
 8000270:   f7ff ffd2   bl  8000218 <hexstring>

0800005E  test1 result
08000271  test2 result

08000066 <dosvcall>:
 8000066:   df00        svc 0
 8000068:   e7fe        b.n 8000068 <dosvcall+0x2>
 800006a:   46c0        nop         ; (mov r8, r8)

    dosvcall(1,3,5,7);

 800027c:   f7ff fef3   bl  8000066 <dosvcall>
 8000280:   2000        movs    r0, #0

stack info

20000FD8 00000001  r[0]
20000FDC 00000003  r[1]
20000FE0 00000005  r[2]
20000FE4 00000007  r[3]
20000FE8 FFFFFFFF  r[12]
20000FEC 08000281  LR
20000FF0 08000068  ReturnAddress();
20000FF4 21000000  (xPSR<31:10>:frameptralign:xPSR<8:0>)

程序计数器不在返回地址所在的堆栈上。您还可以看到程序计数器领先两个而不是一个(根据设计)。

链接寄存器是0xFFFFFFF9,它告诉逻辑这是一个异常以及如何返回(从堆栈中获取地址并简单地返回到它)。 (并替换/恢复堆栈中的其他寄存器,r0,1,2,3,12,14,psr)

以上应该可以解答您的所有问题。

此处无法确定指令大小,因此需要下一条指令的地址才能返回。 (这不是 PC,嗯,ARM 架构中有很多“PC”,当然这是其中之一,但不是我们通常可以访问的那台)。

调试器产生的问题和它们解决的问题一样多,我几十年前就停止使用它们了,它们没有用处。看来您提供的信息充其量只是误导性的。任何(流水线)处理器中的程序计数器根本就是一种幻想,设计中有很多程序计数器,而不只是一个。调试器可能正在创建要显示的值。当然,单步执行也会改变处理器的工作方式,并且也无法提供正常运行的信息。

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