在查看开源代码CmBacktrace时,发现中断中导出的callstack的最后一级执行位置是直接从堆栈内容(R0-R3、R12、LR、PC、PSR)读取后的PC值进入中断。不会从其值中减去 4。 根据我之前的理解,在Cortex-M系列单片机中,PC的值就是当前正在执行的指令的下一条指令的地址。为此我在臭氧上进行了测试。 正常代码执行过程中,PC与当前执行代码的关系:
通过异常命令主动触发异常,观察中断服务函数中断时压栈的PC值与异常指令地址的关系:
通过上面两条测试可以看出,当代码正常执行时,PC值确实是当前执行指令的下一条指令的地址。而触发异常时保存的确实是触发异常的指令的地址。
为什么进入异常时保存的PC值是当前指令地址而不是下一条指令地址?而如果入口是中断,从中断返回时加载到PC中的值是否需要在保存的PC值上加4?
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”,当然这是其中之一,但不是我们通常可以访问的那台)。
调试器产生的问题和它们解决的问题一样多,我几十年前就停止使用它们了,它们没有用处。看来您提供的信息充其量只是误导性的。任何(流水线)处理器中的程序计数器根本就是一种幻想,设计中有很多程序计数器,而不只是一个。调试器可能正在创建要显示的值。当然,单步执行也会改变处理器的工作方式,并且也无法提供正常运行的信息。