【问题标题】:What does it mean that "registers are preserved across function calls"?“在函数调用中保留寄存器”是什么意思?
【发布时间】:2021-01-05 18:42:48
【问题描述】:

从这个问题What registers are preserved through a linux x86-64 function call,它说在函数调用中保存了以下寄存器:

r12, r13, r14, r15, rbx, rsp, rbp

所以,我继续进行了以下测试:

.globl _start
_start:
    mov $5, %r12
    mov $5, %r13
    mov $5, %r14
    mov $5, %r15
    call get_array_size
    mov $60, %eax
    syscall

get_array_size:
    mov $0, %r12
    mov $0, %r13
    mov $0, %r14
    mov $0, %r15
    ret

而且,我在想​​,在call get_array_size 之后,我的寄存器会自动(并且有点神奇地)恢复为值5gdb 表明这是不正确的。

但我认为我可能误解了这一点。我想这只是意味着“符合 x86-64 ABI”的任何函数都应该在完成后恢复这些寄存器(换句话说,我的 get_array_size 函数在 linux ABI 中是无效的函数),或者可以有人向我解释我的理解中似乎缺少什么。

此外,当有人说函数应该符合ABI 时,非全局函数也应该这样做吗?或者“内部实现”根本不重要,只有我向公众公开的功能(通过globl)应该遵守它?是否有一种通常用来表示函数是局部函数还是全局函数的符号(例如在命名方案中?)。

当然,我是asm 的初学者,非常感谢您解释我可能遗漏的内容。

【问题讨论】:

  • 正确,您的手写 asm get_array_size 不遵循 ABI,因为它破坏了调用保留寄存器。这意味着它的调用者需要特别对待它,而不是遵循通常的 ABI 保证。 asm 没有魔法,每条指令只对架构状态有其记录的影响。
  • @PeterCordes 我明白了——所以它更像是一种约定,而不是“由处理器完成”的事情。当您调用 linux x86-64 函数时,它“遵循此约定”。这是更好的理解吗?
  • @samuelbrody1249 完全正确。因此,ABI 通常被称为“调用约定”。您不必为自己的代码遵循它,但如果您不这样做,编译器将无法正确调用您的函数。
  • 换句话说,它们被保留在对已编写为符合 ABI 的函数的调用中。
  • 永远记住,虽然我们碰巧有像 gcc 这样的工具所遵循的目标定义(ARM、intel 等)调用约定,但真正选择的是编译器作者,所以不要假设任何编译器都符合该特定编译器的语句。同样,从 asm 调用的库,它们是用/为什么构建的。

标签: assembly x86 x86-64 calling-convention abi


【解决方案1】:

正确,您的手写 asm get_array_size 不遵循 ABI,因为它破坏了调用保留寄存器。这意味着它的调用者需要对其进行特殊处理,而不是遵循通常的 ABI 保证。

ABI 文档是编译器生成的函数遵循的标准,大多数手写函数也应遵循该标准,除非您想制定自己的自定义调用约定。请参阅What are callee and caller saved registers?,了解有关调用保留与调用破坏对调用者意味着什么的更多详细信息,以及函数本身的实现(如果它想遵循 ABI)。

带有自定义调用约定的小型私有“帮助”函数是可以的,只要您注释它们(并且永远不要尝试从 C 调用它们)。尤其是在优化时,例如代码大小(见codegolf x86-64 tips


asm 没有魔法,每条指令仅对架构状态有其记录的影响。(寄存器和内存的内容)。

从 Intel 的 callret 文档中可以看出,它们修改的唯一整数寄存器是 RSP。像 NASM 和 GAS 这样的普通汇编程序不会神奇地向您的函数添加指令。 (MASM可以不同,但​​是如果你看反汇编你仍然可以看到真正的代码。)

【讨论】:

    猜你喜欢
    • 2020-12-31
    • 1970-01-01
    • 2016-12-17
    • 1970-01-01
    • 2019-05-13
    • 2011-10-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多