【问题标题】:Aligning a Stack pointer 8 byte from 4 byte in ARM assembly在 ARM 程序集中将堆栈指针从 4 字节对齐 8 字节
【发布时间】:2013-11-19 12:22:04
【问题描述】:

如何将堆栈指针对齐到 8 字节,现在在 ARM 中对齐为 4 字节。根据我的理解,如果堆栈指针指向某个地址,如 0x4 、0x8、0x12 和 0x16 等,则它是 4 字节对齐的。

因此,将堆栈指针对齐为 8 字节意味着它应该指向 0x8 、0x16 、0x24 和 0x32 等地址。

现在如何将 4 字节堆栈指针对齐到 8 字节对齐指针?

【问题讨论】:

  • ARM EABI 告诉 SP 应该是 8 字节对齐的,在这种情况下你让它对齐到 8?你在写汇编吗?
  • 是的,我正在尝试用汇编语言编写
  • 我认为一种简单的方法可以实现,当您编写汇编时,即使您不使用它们,也总是将 2 个寄存器推到一起 - 所以不要推奇数个寄存器。我不知道这是否是一个成语,但这会让你的生活更轻松,然后特别是向上或向下舍入,这可能会根据硬件配置发生变化。 (很可能不是:))
  • @auselen 请在你的答案中添加它,我在两条指令中这样做,比如和 r4, sp, #4 , sub sp, sp, r4 ,知道它是否正常

标签: c memory arm


【解决方案1】:

不要尝试自己手动对齐sp,而是push 再注册一个来对齐。例如,而不是

push {r3, r4, lr}

在列表中再添加一个寄存器以轻松对齐到 8。

push {r1, r3, r4, lr}

这可能感觉像是额外的内存访问,但通常缓存使用比原生字长更宽的位向量。

另一个注意事项是,如果您不进行外部调用或接收,则无需强迫自己正确进行堆栈对齐。因此,如果您有封闭的盒子组装例程,它不调用外部世界或接收一些,只要它不占用您自己的负载,您就可以忍受损坏的堆栈对齐。

【讨论】:

  • 感谢 ausleen 的回答,但仍然不明白如何将额外的寄存器推送到堆栈将使 sp 8 字节对齐,以及我如何将它集成到一个简单的 IRQ 处理程序中,在调用 C 调度例程之前我有将 sp 从 4 字节变为 8 字节。
  • 问题是为什么它不是从 8 字节开始对齐的?
  • SP 在你的代码被执行时自动 8 字节对齐,前提是调用者遵守 AtPCS(确实如此,不是吗?)。别担心。
【解决方案2】:

将指针 up 移动到最近的 8 字节边界,但如果它已经是 8 的倍数,则保持不变(伪代码 - 如果在C):

p = (p + 7) & ~7;

或类似地将其向下移动到最近的 8 字节边界:

p = p & ~7;

【讨论】:

  • 假设我指向地址 0100(4 字节对齐)并且执行 p=p&~7 给我值 0 ,那么它将如何对齐 8 字节?
  • 如果 p = 100(十进制),那么 p = p & ~7 将使 p = 96。
  • 对不起@Paul,但我的意思是二进制的 0100,十进制的 4。
  • OK - 小数点 4 的值将按预期变为 0,即 8 的倍数。任何不是 8 的倍数的都将向下舍入到最接近的 8 倍数。跨度>
  • 是的@Paul,明白你的意思,0 是其他数字的倍数。
【解决方案3】:

由于堆栈减少

bic sp, sp, #7

应该足够了。借助 EABI,您可以使用 r12r0-r3 来(重新)存储之前的值。

所有这些都应该只在汇编中完成;在 C 语言中,您可以依赖正确对齐的堆栈指针,并尝试在那里更改它可能会使您的程序崩溃。

编译器注意正确对齐;调用中断时可能会发生未对齐的堆栈。一些 CPU(例如 Cortex-M3)具有特殊寄存器 (STKALIGN),可用于输入 8 位堆栈对齐的 irq。

【讨论】:

  • @encs 我在这里也有同样的疑问,我向 paul 指出,如果我有 4 字节对齐的 SP,说 sp=0x4 是二进制 0100,现在做 bic sp,sp,#7会给我 sp=0 根本不是 8 个字节对齐的。
  • 抱歉@encs,我没听明白,你是说 sp==0 是 8 字节对齐的吗?
  • 哦,我真的很困惑。sp==0 也是 4 字节,对吧?
  • 是的;以及 23 字节和 42 字节对齐;)它可能是最对齐的地址
  • 所以任何地址 lke 0x0 ,0x8 0x16,0x24 和 0x32 都是 8 字节对齐的,对吧?
【解决方案4】:

如果您正在编写叶函数(没有子程序调用),请不要打扰。

您完全可以使用 4 字节对齐的 SP,因为此要求是由于 ldrd 和 strd 指令需要地址为 8 的倍数。 因此,如果您正在编写的函数没有调用您不知道的任何子例程,那么确实没有必要这样做。 (无论如何,ldrd 和 strd 都很少使用)

无论如何,当您的函数从更高级别的语言调用时,SP 已经是 8 字节对齐的。

如果你希望 SP 是 8 字节对齐的,要么不要碰它,要么只保留偶数个寄存器。

【讨论】:

  • 谢谢@Jake 这个调用是否会对齐堆栈指针 8 字节对齐的 STMD sp!,{r0-r3,r12,r14},如果是这样,那么如何保留偶数个寄存器将对齐 sp 到 8字节?
  • 同样在此链接infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.faqs/… 中,armv7 中的中断处理明确努力将 sp 与 AND r1,r1,#4 SUB sp,sp,r1 对齐到 8 个字节。
【解决方案5】:

如果你无法控制你得到的SP的值,并且你想在8字节的边界上对齐SP(例如,调用一个子程序),那么下面的序列这样做,不使用任何其他寄存器:

; Check if SP is aligned on 8 bytes boundary.
tst sp, #0x7
; If SP is aligned on 8 bytes boundary, then we skip a word on the stack
; and then save SP. This consumes 8 bytes on the stack but keeps SP
; aligned on 8 bytes boundary.
streq sp, [sp, #-8]!
; If SP is aligned on 4 bytes boundary, then we save SP. This consumes
; 4 bytes on the stack and also aligns SP on 8 bytes boundary.
strne sp, [sp, #-4]!
; Here, SP is aligned on 8 bytes boundary, and the previous value of SP
; is stored on the top of the stack.

; For example, let's call some subroutine...
blx lr

; In order to restore the original value of SP, just load the value
; at the top of the stack.
ldr sp, [sp]

请注意,上面的代码假定:

  • 您在 ARM 32 位模式下运行(例如,ARMv5、ARMv6、ARMv7、AArch32...)。

  • SP 至少在 4 个字节的边界上对齐,这通常是因为堆栈被视为一个字数组。

【讨论】:

    猜你喜欢
    • 2014-11-28
    • 2023-03-25
    • 1970-01-01
    • 2017-09-27
    • 2021-10-28
    • 2015-09-03
    • 1970-01-01
    • 2016-09-28
    • 1970-01-01
    相关资源
    最近更新 更多