调试某些Armv5汇编代码时出现奇怪的内容

调试某些Armv5汇编代码时出现奇怪的内容,arm,gdb,Arm,Gdb,我试图通过调试一个简单的ARM组件来学习ARM .global start, stack_top start: ldr sp, =stack_top bl main b . 链接器脚本如下所示: ENTRY(start) SECTIONS { . = 0x10000; .text : {*(.text)} .data : {*(.data)} .bss : {*(.bss)} . = ALIGN(8); . = .

我试图通过调试一个简单的ARM组件来学习ARM

    .global start, stack_top
start:
    ldr sp, =stack_top
    bl main
    b .
链接器脚本如下所示:

ENTRY(start)
SECTIONS
{
    . = 0x10000;
    .text : {*(.text)}
    .data : {*(.data)}
    .bss : {*(.bss)}
    . = ALIGN(8);
    . = . +0x1000;
    stack_top = .;
}
我在qemu arm模拟器上运行这个。二进制文件在
0x10000
处加载。所以我在那里设置了一个断点。一旦bp被击中。我检查了
pc
寄存器。它的值是
0x10000
。然后在
0x10000
处反汇编指令

我看到一条奇怪的评论
;0x1000c
这是什么意思?它来自哪里?

Breakpoint 1, 0x00010000 in start ()
(gdb) i r pc
pc             0x10000  0x10000 <start>
(gdb) x /i 0x10000
=> 0x10000 <start>:     ldr     sp, [pc, #4]    ; 0x1000c <start+12> <========= HERE
(gdb) x /i 0x10004
   0x10004 <start+4>:   bl      0x102b0 <main>
因此0x11520值确实来自链接器脚本符号
stack\u top
。但是它与
0x10000处的
ldr sp[pc,#4]
指令有何关系?

Breakpoint 1, 0x00010000 in start ()
(gdb) i r pc
pc             0x10000  0x10000 <start>
(gdb) x /i 0x10000
=> 0x10000 <start>:     ldr     sp, [pc, #4]    ; 0x1000c <start+12> <========= HERE
(gdb) x /i 0x10004
   0x10004 <start+4>:   bl      0x102b0 <main>
添加1-2019年12月20日上午9:29 非常感谢@old_timer的详细回答

我正在读这本书。我从这本书中了解了管道的事情。引述如下:

    .global start, stack_top, label2 ;<========== HERE I add a new label2
start:
    ldr sp, =stack_top // sp = &stack_top, as soon as we have the stack ready, we can call C function
label2:    
    bl main
    b .

因此,如果管道的事情在今天不再相关是什么原因使得
pc
在当前执行的指令之前值为2?

我刚刚发现下面的线程解决了这个问题:

基本上,这只是另一种情况,即人们在推进技术的过程中不断为自己制造错误/缺陷/陷阱

回到这个问题:

  • 在我的汇编中,使用的是pc相对寻址
  • ARM的PC指针比当前执行的指令早2。(处理好这个问题!)
    • (我想我现在可以解释。如果我错了,请随时纠正我。)

      我尝试了一个稍微不同的组件,多了一个标签。如下图所示:

          .global start, stack_top, label2 ;<========== HERE I add a new label2
      start:
          ldr sp, =stack_top // sp = &stack_top, as soon as we have the stack ready, we can call C function
      label2:    
          bl main
          b .
      
      。全局启动,堆栈顶部,标签2 (我想我现在可以解释了。如果我错了,请随时纠正我。)

      我尝试了一个稍微不同的组件,多了一个标签。如下图所示:

          .global start, stack_top, label2 ;<========== HERE I add a new label2
      start:
          ldr sp, =stack_top // sp = &stack_top, as soon as we have the stack ready, we can call C function
      label2:    
          bl main
          b .
      

      。全局启动,堆栈顶部,标签2 从指令(例如,
      ldr
      mov
      )访问
      pc
      时,ARM(A32)模式中增加8的偏移量,而Thumb(T32)模式中增加4的偏移量。IIRC这是因为函数调用在旧ARM版本中的工作方式。如第A2.3章第页的ARMv7A架构参考手册中记录了这一点。A2-45

      注释
      ;0x1000c实际上是由反汇编程序生成的,用于指示由PC+4计算的地址

      旁注:
      ldr,=
      不是实际的指令,而是由汇编程序翻译成1-2条指令和可选的文字值,以最有效的方式获得所需的值


      如果您对此感兴趣,我在Cortex-M上一步一步地写了一篇关于学习手臂组件的文章。

      当从指令(例如
      ldr
      mov
      )访问
      pc
      时,ARM(A32)模式下会增加8的偏移量,Thumb(T32)模式下会增加4的偏移量。IIRC这是因为函数调用在旧ARM版本中的工作方式。如第A2.3章第页的ARMv7A架构参考手册中记录了这一点。A2-45

          .global start, stack_top
      start:
          ldr sp, =stack_top
          bl main
          b .
      
      注释
      ;0x1000c实际上是由反汇编程序生成的,用于指示由PC+4计算的地址

      旁注:
      ldr,=
      不是实际的指令,而是由汇编程序翻译成1-2条指令和可选的文字值,以最有效的方式获得所需的值

      如果你对此感兴趣,我在Cortex-M上一步一步地写了一篇关于学习手臂装配的文章

          .global start, stack_top
      start:
          ldr sp, =stack_top
          bl main
          b .
      
      假设arm模式中有三条指令,堆栈_top值的第一个可能存在的池位于.b之后

      _start: ( 0x00000000 )
      0x00000000  ldr sp,=stack_top
      0x00000004  bl main
      0x00000008  b .
      0x0000000c  stack_top
      
      从你们所展示的,这就是汇编程序分配空间的地方

      所以在_start+12是堆栈_top值的位置。伪代码ldr sp,=stack_top会转换为mov或pc相对负载。由于历史原因,pc领先两位,这在今天是零相关性的,有些体系结构pc是当前指令,有些是下一条指令可变长度的地址,而在arm(aarch32)和thumb的情况下,pc领先两位,因此8。因此,地址0x00000000处的指令到达0x0000000C的pc相对负载为0xC-8=4。因此,ldr sp,[pc,#4]

      现在,该地址的内容是链接器在链接时计算的链接器脚本中所要求的内容。你在那里放了一些代码,然后填充了一些没有显示其余代码的内容,本可以让这成为一个完整的示例,但不管是哪种方式,从你的帖子中链接器最终都会计算0x11520

      因此,反向工程您的问题和意见,我们看到二进制开始(一旦链接)

      在arm模式下,第一条指令将按照您的要求将值0x11520加载到堆栈指针中。这里没有什么奇怪或错误

      0x1000C只是说明地址0x1000C是距离最近的标签_起点12的偏移量。有时这是有用的信息

      如果您添加了nop或其他一些代码,则使用伪指令而不定义池,汇编程序将尝试找到一个主池

          .global start, stack_top
      start:
          ldr sp, =stack_top
          bl main
          nop
          b .
      
      然后,汇编器很可能现在会在pc+8上设置,在链接之后,pc+8将是0x10010,如果没有其他更改,堆栈指针可能位于相同的值或更远处的4(或更多),这取决于工具沿途进行的对齐和填充

      关键是,如果管道在实际产品中曾经这样做过,那么它就不再以这种方式工作,因此不要将其视为管道,就像mips中的分支阴影指令在今天(启用时)具有任何相关意义一样。对于每个具有pc相对寻址的指令集,您需要知道规则,是该指令的地址(不太常见)、下一条指令的地址(最常见)还是前两条指令的地址,还是其他地址。同样地,人们也会在胸罩里硬编码一段时间