【问题标题】:Why doesn't the stack pointer decrease when I am using a 64 bit local variable?为什么当我使用 64 位局部变量时堆栈指针不减少?
【发布时间】:2018-11-30 03:25:06
【问题描述】:

这是从C编译的反汇编代码:

00799d60 <sub_799d60>:
  799d60:   b573        push    {r0, r1, r4, r5, r6, lr}
  799d62:   0004        movs    r4, r0
  799d64:   f000 e854   blx 799e10 <jmp_sub_100C54>
  799d68:   4b15        ldr r3, [pc, #84]   ; (799dc0 <sub_799d60+0x60>)
  799d6a:   0005        movs    r5, r0
  799d6c:   4668        mov r0, sp
  799d6e:   4798        blx r3

子程序调用的目标 (799d6e: 4798 blx r3) 接受一个 64 位整数指针参数并返回一个 64 位整数。该例程是一个库函数,因此我无法对其进行任何修改。 此操作是否会覆盖存储 lr 和 r6 值的堆栈?

【问题讨论】:

  • 编译器不需要为少数本地对象使用堆栈,因为寄存器可用。
  • blx 指令在执行之前不会修改压入堆栈的内容。被调用的例程可以但不应该;这将违反 ABI 规则。
  • 你为什么要问?您是在寻找错误还是只是好奇?
  • 不完全清楚你在问什么。 lr 在正常的 ARM 调用约定中被调用破坏,但 r6 不是。例如例如,允许函数以 pop {r0, r1, r4, r5, r6, pc} 返回,而 LR 被修改。
  • 不知道为什么这个问题被否决了。当然,措辞有些模棱两可,但它本质上是一个关于参数传递和堆栈使用的有效且具体的问题。

标签: c assembly arm


【解决方案1】:

您说分支目标“采用 64 位整数指针参数并返回一个 64 位整数”,但事实并非如此。它需要一个指向 64 位整数的指针作为其唯一参数(并且这个指针是 32 位长,除非你在 aarch64 上,我怀疑其余的代码);它什么也不返回,它只是覆盖了您传入的参数所指向的 64 位值。我确定这就是您的意思,但请小心使用术语,因为这些东西之间的区别很重要!尤其是没有 64 位参数传入我们调用的函数。

关于问题本身。理解编译器在这里做什么的关键是看第一行:

push    {r0, r1, r4, r5, r6, lr}

ARM calling convention 不需要保留调用 r0r1,那么它们在列表中做了什么?答案是编译器添加了这些“虚拟”推送以在堆栈上创建一些空间。上面的push 操作本质上等价于

push    {r4, r5, r6, lr}
sub     sp, sp, #0x08

除了它保存了一条指令。当然,结果并不完全相同,因为 r0r1 中的任何内容最终都会写入这些位置;但鉴于没有办法事先知道那里有什么,而且堆叠的值无论如何都将被覆盖,这无关紧要。所以我们有,作为一个堆栈帧,

      lr
      r6
      r5
      r4
      (r1)
sp -> (r0)

堆栈指针指向r0r1 的虚拟推送创建的空间。现在我们只有

mov   r0, sp

它将堆栈指针复制到r0 以用作您正在调用的函数的指针参数,然后它将覆盖该位置的两个单词以产生堆栈帧

      lr
      r6
      r5
      r4
      (64-bit value, high word)
sp -> (64-bit value, low word)

除了blx r3,您没有显示任何代码,因此无法准确说明函数末尾的堆栈发生了什么。但是如果这个函数不返回任何参数,我希望看到一个匹配的

pop     {r0, r1, r4, r5, r6, pc}

当然,这会导致您的 64 位结果留在 r0r1 中。但是这些寄存器是根据调用约定进行调用破坏的,所以没有问题。

【讨论】:

  • 你是对的。那里的推操作只是一个虚拟推。
猜你喜欢
  • 2019-11-23
  • 2021-04-08
  • 2013-01-14
  • 1970-01-01
  • 1970-01-01
  • 2013-07-05
  • 1970-01-01
  • 2013-09-13
  • 2011-08-22
相关资源
最近更新 更多