程序计数器没有增加

4

我正在尝试调试一个汇编语言编写的函数Reset_Handler()(我不理解,但是这是标准库的一部分)。使用GDB,我使用ni逐个执行每个指令。以下是结果:

(gdb) ni
0x08005dc4 in Reset_Handler ()
(gdb) ni
0x08005dc6 in Reset_Handler ()
(gdb) ni
0x08005dc6 in Reset_Handler ()
(gdb) ni
0x08005dc6 in Reset_Handler ()
(gdb) ni
0x08005dc6 in Reset_Handler ()

实际上,程序指针停留在0x08005dc6。这是正常的行为吗?每次执行ni时,程序指针是否应该前进?下面是Reset_Handler()的开头:
    .section  .text.Reset_Handler
  .weak  Reset_Handler
  .type  Reset_Handler, %function
Reset_Handler:  

/* Copy the data segment initializers from flash to SRAM */  
  movs  r1, #0
  b  LoopCopyDataInit

CopyDataInit:
  ldr  r3, =_sidata
  ldr  r3, [r3, r1]
  str  r3, [r0, r1]
  adds  r1, r1, #4

LoopCopyDataInit:
  ldr  r0, =_sdata
  ldr  r3, =_edata
  adds  r2, r0, r1
  cmp  r2, r3
  bcc  CopyDataInit
  ldr  r2, =_sbss
  b  LoopFillZerobss
/* Zero fill the bss segment. */  
FillZerobss:
  movs  r3, #0
  str  r3, [r2], #4

编辑: 这里是反汇编指令:

disas
Dump of assembler code for function Reset_Handler:
   0x08005dc0 <+0>:     movs    r1, #0
   0x08005dc2 <+2>:     b.n     0x8005dcc <LoopCopyDataInit>
   0x08005dc4 <+4>:     ldr     r3, [pc, #40]   ; (0x8005df0 <LoopFillZerobss+16>)
=> 0x08005dc6 <+6>:     ldr     r3, [r3, r1]
   0x08005dc8 <+8>:     str     r3, [r0, r1]
   0x08005dca <+10>:    adds    r1, #4
   0x08005dcc <+0>:     ldr     r0, [pc, #36]   ; (0x8005df4 <LoopFillZerobss+20>)
   0x08005dce <+2>:     ldr     r3, [pc, #40]   ; (0x8005df8 <LoopFillZerobss+24>)
   0x08005dd0 <+4>:     adds    r2, r0, r1
   0x08005dd2 <+6>:     cmp     r2, r3
   0x08005dd4 <+8>:     bcc.n   0x8005dc4 <Reset_Handler+4>
   0x08005dd6 <+10>:    ldr     r2, [pc, #36]   ; (0x8005dfc <LoopFillZerobss+28>)
   0x08005dd8 <+12>:    b.n     0x8005de0 <LoopFillZerobss>
   0x08005dda <+0>:     movs    r3, #0
   0x08005ddc <+2>:     str.w   r3, [r2], #4
   0x08005de0 <+0>:     ldr     r3, [pc, #28]   ; (0x8005e00 <LoopFillZerobss+32>)
   0x08005de2 <+2>:     cmp     r2, r3
   0x08005de4 <+4>:     bcc.n   0x8005dda <FillZerobss>
   0x08005de6 <+6>:     bl      0x8005c64 <SystemInit>
   0x08005dea <+10>:    bl      0x8000184 <main>
   0x08005dee <+14>:    bx      lr
End of assembler dump.

栈指针还是程序计数器?0x08005dc6处是什么?闻起来像是RAM。如果你的自动显示是栈指针,请将其更改为程序计数器并反汇编指令。如果控制已转移到典型的默认中止处理程序,则SP不会更改,因为这些处理程序通常是简单的“跳转到自己”的循环。 - Martin James
@MartinJames:抱歉,应该是程序计数器。 - Randomblue
1
嗯,加载r3似乎并不令人兴奋:(你确定你看到的是你认为的东西吗?我自己也曾因错误版本的源文件、映射文件等被误导过很多次。 - Martin James
2个回答

4

根据您发布的代码和反汇编结果,我猜测在_sidata中的地址是无效的。 _sidata被加载到r3中,所以当

 ldr     r3, [r3, r1]

当执行该指令时,无效访问会导致另一个处理器重置,然后执行直到再次命中该指令。或者类似于这样的情况。
检查“_sidata”中的内容。
一些附加注释:
我看到地址xxxx处的指令使用了“r0”,但我没有看到“reset_handler()”中已经初始化“r0”的地方。可能调用“reset_handler()”的代码已经正确设置了“r0”,但为了确定,我们需要查看异常向量表和重置向量实际指向的代码。(我假设这是针对ARM7或类似处理器 - 如果我猜错了,请告诉我)其中,异常向量表可能类似于以下内容(来自ethernut.de),将重置向量引导到名为“_start”的标签:
.global __vectors
__vectors:
ldr     pc, [pc, #24]   /* Reset */
ldr     pc, [pc, #24]   /* Undefined instruction */
ldr     pc, [pc, #24]   /* Software interrupt */
ldr     pc, [pc, #24]   /* Prefetch abort */
ldr     pc, [pc, #24]   /* Data abort */
ldr     pc, [pc, #24]   /* Reserved */

/*
* On IRQ the PC will be loaded from AIC_IVR, which
* provides the address previously set in AIC_SVR.
* The interrupt routine will be called in ARM_MODE_IRQ
* with IRQ disabled and FIQ unchanged.
*/
ldr     pc, [pc, #-0xF20]   /* Interrupt request, auto vectoring. */
ldr     pc, [pc, #-0xF20]   /* Fast interrupt request, auto vectoring. */

.word   _start
.word   __undef
.word   __swi
.word   __prefetch_abort
.word   __data_abort

我认为 _sidata 是一个无效的地址,你是对的。至于 r0 的初始化,我认为它是在 LoopCopyDataInit 的第一行完成的。(复位处理程序将 LoopCopyDataInit 作为第二条指令调用。) - Randomblue
如果r3的加载引发数据终止,单步调试不应该可以识别它吗? - Martin James
@MartinJames: 你可能会这么想,但是你也期望在执行 ldr r3, [r3, r1] 的单步操作时会推进程序计数器! 我不知道使用的确切工具链或如何设置异常向量。 我知道很久以前当我在玩一个针对ARM7的OpenOCD JTAG驱动程序和Eclipse中的旧Zylin CDT插件时,事情并不总是稳定的。 - Michael Burr
@Randomblue:是的 - 我完全错过了 b LoopCopyDataInit - Michael Burr
@MichaelBurr - 你可能是对的 - 循环中PC没有改变看起来很像错误处理程序,无论如何它可能是,哦!!模式和寄存器设置是否被交换了,也许调试器没有注意到中断已经交换了r8-r15? - Martin James

1

嗯,这取决于 :-)

它所依赖的是在0x08005dc6处的指令。在复位处理程序中,你可能会有这样一条指令:

0x08005dc6 jmp 0x08005dc6

这将表现出该行为。

您应该检查实际上在那个位置的内容,可以使用以下方式:

disas 0x08005dc6 0x08005dcf

确实是程序计数器,谢谢。我该如何检查特定位置的指令?我应该使用 objdump 吗? - Randomblue
1
@Randomblue,objdump不行,除非你是个受虐狂 :-) 既然你已经在gdb中(使用ni命令),只需按照我的更新,使用disas命令即可。 - paxdiablo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接