如何在x86-64下保留参数/参数寄存器?

时间:2012-05-25 22:13:15

标签: c gcc assembly 64-bit

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的值。保留所有参数/参数寄存器的正确方法是什么?

1 个答案:

答案 0 :(得分:4)

你不能像这样混合C和asm。特别是,在内联汇编中执行隔离的push或pop指令会严重破坏事物;任何给定的内联汇编块必须在堆栈指针上具有0的净偏移量。

实际上,ABI文档与内联asm的使用无关。为此,您需要遵循GCC内联asm合同,记录您用于输入和输出的寄存器以及clobber列表中的内容。如果您在汇编程序中编写整个函数(在.s文件而不是.c文件中),则ABI是相关的。