"registers are preserved across function calls" 是什么意思?

What does it mean that "registers are preserved across function calls"?

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

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

因此,我继续进行以下测试:

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

get_array_size:
    mov [=12=], %r12
    mov [=12=], %r13
    mov [=12=], %r14
    mov [=12=], %r15
    ret

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

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

此外,当有人说一个函数应该符合 ABI 时,非全局函数也应该这样做吗?还是 'internal-implementations' 根本不重要,只有我向 public(通过 globl)公开的函数应该遵守它?是否有通常用于表示函数是局部函数还是全局函数的符号(例如在命名方案中?)。

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

正确,您的 hand-written asm get_array_size 不遵循 ABI,因为它破坏了 call-preserved 寄存器。这意味着它的调用者需要特殊对待它,而不是遵循通常的 ABI 保证。

ABI 文档是 compiler-generated 函数遵循的标准,大多数 hand-written 函数也应该遵循,除非您想制定自己的自定义调用约定。有关 call-preserved 与 call-clobbered 对调用者的意义以及函数本身的实现(如果它想要遵循 ABI)的更多详细信息,请参阅 What are callee and caller saved registers?

具有自定义调用约定的小型私有“帮助程序”函数很好,只要您对它们进行注释(并且永远不要尝试从 C 调用它们)。特别是当你在优化时,例如代码大小(参见 codegolf x86-64 tips


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

正如您从 Intel 的 call and ret 文档中看到的那样,他们修改的唯一整数寄存器是 RSP。 NASM 和 GAS 等普通汇编程序不会神奇地向您的函数添加指令。 (MASM 可能有所不同,但如果您查看反汇编,您仍然可以看到真正的代码。)