【问题标题】:I cannot understand how the ARM stack operations work我无法理解 ARM 堆栈操作是如何工作的
【发布时间】:2021-12-28 16:11:46
【问题描述】:

ARM 堆栈上的升序和降序是什么意思?满和空是什么意思?如果栈满了,push指令会不会导致栈溢出?

【问题讨论】:

  • 感谢提醒,我已经添加了我正在阅读的有关 ARM 堆栈的测试
  • @Circuit_Breaker0.7 请不要添加文字图片。相反,将文本作为文本发布。也就是说,文本似乎以非常清晰的方式回答了您的问题。你不明白哪一部分?
  • SO 已将Can i configure a ARM processor for Ascending Stack growth direction? 链接到其答案是“有点,但不是真的”。
  • @Circuit_Breaker0.7 指令做了不同的事情。它们的区别在于堆栈指针是在内存访问之前还是之后更改,以及堆栈指针是递增还是递减。这两个选项的组合产生了四种可能的堆栈模型。
  • 这是“full”和“empty”的不同用法(这里有点烦人)。 “已满”并不意味着“无法接受更多条目”或“将溢出”。它只是堆栈管理样式的名称,其中堆栈指针指向以前使用的位置,而不是下一个要使用的位置。

标签: assembly arm stack-memory microprocessors


【解决方案1】:

硬件堆栈对于大多数 CPU 来说都是通用的,并且有几种不同的方式在硬件中实现它。大多数 CPU 使用降序堆栈,这意味着当您 PUSH 一个新值时,堆栈指针的值会减少推送的数据量(在 ARM 上为 4 个字节),并将新值推送到堆栈上。这是一个使用 Z80 Assembly 的示例,它使用完整的降序堆栈。 (尽管问题是关于 ARM 的,但我觉得最好使用行为类似的更简单的 CPU,以使解释更清楚一些。)

ld sp,0xFFFF
ld bc,0x1234
ld de,0x5678
push bc
push de

在执行的这一点上,如果您要读取堆栈的十六进制转储,您会看到:

0xFF00: 00 00 00 00 00 00 00 00 00 00 00 |78 56| |34 12| 00
                                          E   D    C  B

下面的竖线和寄存器是我添加的,以显示这些值的来源,它们不会出现在典型的 hexdump 程序中。这只是为了说明堆栈的一般工作方式。 ARM 非常相似,但它可以配置为大端(这意味着字节按您期望的顺序推送,而不是如您在此处看到的顺序反转,这实际上是大多数 CPU 的规范)

Z80 使用全栈约定这一事实反映在栈指针的当前值是0xFFFB——它直接指向我们压入的最后一个字节。使用空堆栈约定的 CPU 的堆栈指针现在指向 0xFFF9

堆栈溢出是指堆栈已达到其“限制”并开始覆盖硬件的正常 RAM 区域(也称为“堆”)。我将使用 Game Boy Advance 来展示这个示例,它实际上使用了 ARM CPU。堆从0x02000000 开始,我们会说我们的堆栈指针初始化为0x03000000。对于这个例子,假设我们的“堆”结束于0x027FFFFF

; program begins here
mov sp,#0x03000000
stmfd sp!,{r0-r12,lr}   ;push 56 bytes onto the stack.
stmfd sp!,{r0-r12,lr}   ;push 56 bytes onto the stack.
stmfd sp!,{r0-r12,lr}   ;push 56 bytes onto the stack.
...

最终,如果您继续这样做,您将开始用推送的值覆盖堆中的内存,这就是堆栈溢出。鉴于大多数现代硬件上的堆栈有多大,这通常是汇编代码的结果,其中作者错误地管理堆栈或递归函数被调用太多次。如果您的程序完全由非递归函数组成并且您已确保正确“平衡”堆栈,那么您不太可能遇到这种情况。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-02-16
    • 1970-01-01
    • 2021-12-15
    • 2011-01-16
    • 1970-01-01
    相关资源
    最近更新 更多