我知道我需要在函数调用开始时推送链接寄存器,并在返回之前将该值弹出到Program Couter,以便执行可以从函数调用之前的位置执行.
我不明白的是为什么大多数人通过在push/pop中添加一个额外的寄存器来做到这一点.例如:
push {ip, lr}
...
pop {ip, pc}
Run Code Online (Sandbox Code Playgroud)
例如,这是ARM的官方ARM博客提供的ARM Hello世界:
.syntax unified
@ --------------------------------
.global main
main:
@ Stack the return address (lr) in addition to a dummy register (ip) to
@ keep the stack 8-byte aligned.
push {ip, lr}
@ Load the argument and perform the call. This is like 'printf("...")' in C.
ldr r0, =message
bl printf
@ Exit from 'main'. This is like 'return 0' in C.
mov r0, #0 @ Return 0.
@ Pop the dummy ip to reverse our alignment fix, and pop the original lr
@ value directly into pc — the Program Counter — to return.
pop {ip, pc}
@ --------------------------------
@ Data for the printf calls. The GNU assembler's ".asciz" directive
@ automatically adds a NULL character termination.
message:
.asciz "Hello, world.\n"
Run Code Online (Sandbox Code Playgroud)
问题1:他们称之为"虚拟注册"的原因是什么?为什么不简单地推{lr}和pop {pc}?他们说这是保持堆栈8字节对齐,但不是堆栈4字节对齐?
问题2:什么寄存器是"ip"(即r7或什么?)
8字节对齐是符合AAPCS的对象之间互操作性的要求.
ARM就此主题提供了一份咨询说明:
用于ARM®体系结构的ABI建议 - 在进入符合AAPCS的功能时,SP必须是8字节对齐的
文章提到使用8字节对齐的两个原因
对齐故障或UNPREDICTABLE行为.(硬件/体系结构相关原因 - LDRD/STRD可能导致对齐错误或在ARMv7以外的体系结构上显示UNPREDICTABLE行为)
应用失败.(编译器 - 运行时假设差异,他们给出va_start并va_arg作为例子)
当然这都是关于公共接口的,如果你制作一个没有额外链接的静态可执行文件,你可以将堆栈对齐为4个字节.
他们称之为"虚拟注册"的原因是什么?为什么不简单地推{lr}和pop {pc}?他们说这是保持堆栈8字节对齐,但不是堆栈4字节对齐?
堆栈只需要4字节对齐; 但如果数据总线是64位宽(就像在许多现代ARM上一样),那么将它保持在8字节对齐更有效.然后,例如,如果调用需要堆栈两个寄存器的函数,则可以在单个64位写入中完成,而不是两个32位写入.
更新:显然,这不仅仅是为了提高效率; 如评论中所述,这是官方程序调用标准的要求.
如果您的目标是较旧的32位ARM,那么额外的堆栈寄存器可能会略微降低性能.
什么寄存器是"ip"(即,r7或什么?)
r12.见,例如,这里的全套由程序调用标准使用的寄存器的别名.
| 归档时间: |
|
| 查看次数: |
10958 次 |
| 最近记录: |