1

System V AMD64 ABI 调用约定是这样规定的:

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

4

1 回答 1

5

You can't mix C and asm like this. In particular, executing an isolated push or pop instruction in inline assembly will horribly break things; any given inline assembly block must have a net offset of 0 on the stack pointer.

Really, the ABI document is rather irrelevant to the use of inline asm. For that, you need to follow the GCC inline asm contract of documenting which registers you use for input and output and what's in the clobber list. The ABI is relevant if you're writing entire functions in assembler (in a .s file not a .c file).

于 2012-05-26T00:07:14.330 回答