System V AMD64 ABI call convention因此被强制执行:
注册
%rbp
,%rbx
和%r12
到%r15
“属于”来电 函数和被调用函数需要保留它们的值。 换句话说,被调用函数必须保留这些寄存器' 其来电者的价值。剩余的寄存器“属于”被叫 功能。如果调用函数想要保留这样的寄存器 函数调用中的值,必须将值保存在本地 堆栈框架。
例如,给定此代码:
void f1(const int i, const double j, const char * k)
{
printf("i = %d\n", i);
printf("j = %g\n", j);
printf("k = %s\n", k);
}
汇编表示为:
f1():
4004f4: 55 push %rbp
4004f5: 48 89 e5 mov %rsp,%rbp
4004f8: 48 83 ec 20 sub $0x20,%rsp
4004fc: 89 7d fc mov %edi,-0x4(%rbp)
4004ff: f2 0f 11 45 f0 movsd %xmm0,-0x10(%rbp)
400504: 48 89 75 e8 mov %rsi,-0x18(%rbp)
400508: b8 70 06 40 00 mov $0x400670,%eax
40050d: 8b 55 fc mov -0x4(%rbp),%edx
400510: 89 d6 mov %edx,%esi
400512: 48 89 c7 mov %rax,%rdi
400515: b8 00 00 00 00 mov $0x0,%eax
40051a: e8 d1 fe ff ff callq 4003f0 <printf@plt>
40051f: b8 78 06 40 00 mov $0x400678,%eax
400524: f2 0f 10 45 f0 movsd -0x10(%rbp),%xmm0
400529: 48 89 c7 mov %rax,%rdi
40052c: b8 01 00 00 00 mov $0x1,%eax
400531: e8 ba fe ff ff callq 4003f0 <printf@plt>
400536: b8 80 06 40 00 mov $0x400680,%eax
40053b: 48 8b 55 e8 mov -0x18(%rbp),%rdx
40053f: 48 89 d6 mov %rdx,%rsi
400542: 48 89 c7 mov %rax,%rdi
400545: b8 00 00 00 00 mov $0x0,%eax
40054a: e8 a1 fe ff ff callq 4003f0 <printf@plt>
40054f: c9 leaveq
400550: c3 retq
在这种情况下,参数已在%edi
,%xmm0
和%rsi
中传递。调用约定声明这些寄存器“属于”被调用函数,这意味着f1
没有义务保留它们的值。事实上,%edi
,%xmm0
和%rsi
都在以下几行中被删除:
400510: 89 d6 mov %edx,%esi
400512: 48 89 c7 mov %rax,%rdi
..
400524: f2 0f 10 45 f0 movsd -0x10(%rbp),%xmm0
我想保留所有参数寄存器。文档说明值可以保存在本地堆栈上,我尝试过如下:
void f1(const int i, const double j, const char * k)
{
uint32_t edi;
__asm ("movl %%edi, %0;" : "=r" ( edi ));
printf("i = %d\n", i);
printf("j = %g\n", j);
printf("k = %s\n", k);
__asm ("movl %0, %%edi;" : "=d"( edi ));
}
这会产生以下结果:
f1():
4004f4: 55 push %rbp
4004f5: 48 89 e5 mov %rsp,%rbp
4004f8: 53 push %rbx
4004f9: 48 83 ec 38 sub $0x38,%rsp
4004fd: 89 7d dc mov %edi,-0x24(%rbp)
400500: f2 0f 11 45 d0 movsd %xmm0,-0x30(%rbp)
400505: 48 89 75 c8 mov %rsi,-0x38(%rbp)
400509: 89 fb mov %edi,%ebx
40050b: 89 5d ec mov %ebx,-0x14(%rbp)
40050e: b8 80 06 40 00 mov $0x400680,%eax
400513: 8b 55 dc mov -0x24(%rbp),%edx
400516: 89 d6 mov %edx,%esi
400518: 48 89 c7 mov %rax,%rdi
40051b: b8 00 00 00 00 mov $0x0,%eax
400520: e8 cb fe ff ff callq 4003f0 <printf@plt>
400525: b8 88 06 40 00 mov $0x400688,%eax
40052a: f2 0f 10 45 d0 movsd -0x30(%rbp),%xmm0
40052f: 48 89 c7 mov %rax,%rdi
400532: b8 01 00 00 00 mov $0x1,%eax
400537: e8 b4 fe ff ff callq 4003f0 <printf@plt>
40053c: b8 90 06 40 00 mov $0x400690,%eax
400541: 48 8b 55 c8 mov -0x38(%rbp),%rdx
400545: 48 89 d6 mov %rdx,%rsi
400548: 48 89 c7 mov %rax,%rdi
40054b: b8 00 00 00 00 mov $0x0,%eax
400550: e8 9b fe ff ff callq 4003f0 <printf@plt>
400555: 89 d7 mov %edx,%edi
400557: 89 d3 mov %edx,%ebx
400559: 89 5d ec mov %ebx,-0x14(%rbp)
40055c: 48 83 c4 38 add $0x38,%rsp
400560: 5b pop %rbx
400561: 5d pop %rbp
400562: c3 retq
这似乎没有恢复%edi
的值。保留所有参数/参数寄存器的正确方法是什么?
答案 0 :(得分:4)
你不能像这样混合C和asm。特别是,在内联汇编中执行隔离的push或pop指令会严重破坏事物;任何给定的内联汇编块必须在堆栈指针上具有0的净偏移量。
实际上,ABI文档与内联asm的使用无关。为此,您需要遵循GCC内联asm合同,记录您用于输入和输出的寄存器以及clobber列表中的内容。如果您在汇编程序中编写整个函数(在.s
文件而不是.c
文件中),则ABI是相关的。