问题描述
从这个问题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
之后,我的寄存器将自动(并且有些神奇地)恢复为值5
。 gdb
表明这是不正确的。
但是我认为也许我误会了这一点。我想这只是意味着“符合x86-64 ABI”的所有函数都应在完成后恢复这些寄存器(换句话说,我的get_array_size
函数在linux ABI中是无效的functino),或者有人可以向我解释我的理解中似乎缺少的东西。
另外,当有人说一个函数应该符合ABI
时,非全局函数也应该这样做吗?还是“内部实现”一点都不重要,而只有我公开(通过globl
公开)的功能应该遵守吗?是否有通常用于表示功能是局部功能还是全局功能(例如在命名方案中)的符号?
当然,我是asm
的初学者,非常感谢您解释我可能会丢失的内容。
解决方法
正确的,您的手写asm get_array_size
不遵循ABI,因为它破坏了呼叫保留的寄存器。这意味着它的调用者需要特别对待它,而不遵循通常的ABI保证。
ABI doc是编译器生成的函数所遵循的标准,大多数手写函数也应遵循该标准,除非您希望制定自己的自定义调用约定。请参阅What are callee and caller saved registers?,以了解有关保留呼叫者和保留呼叫者对呼叫者意味着什么以及函数本身是否要遵循ABI的实现的更多详细信息。
带有自定义调用约定的小型私有“帮助器”函数就可以了,只要您对其进行注释即可(并且永远不要尝试从C调用它们)。尤其是在进行优化时,例如有关代码大小(请参见codegolf x86-64 tips)
asm中没有魔术,每条指令仅对体系结构状态有其记录的影响。(寄存器和内存的内容)。
从英特尔call
和ret
的文档中可以看到,它们修改的唯一整数寄存器是RSP。诸如NASM和GAS之类的常规汇编程序不会神奇地向您的函数添加指令。 (MASM可能有所不同,但是如果您查看反汇编,仍然可以看到真实的代码。)