1

C代码:

void PtrArg1(int* a,int* b,int* c, int* d, int* e, int* f)
{
    return;
}

void PtrArg2(int* a,int* b,int* c, int* d, int* e, int* f, int* g, int* h)
{
    return;
}

编译

gcc -c -m64 -o basics basics.c -O0

跑步

objdump -d basics -M intel -r

然后导致以下反汇编(英特尔语法):

000000000000000b <PtrArg1>:
   b:   f3 0f 1e fa             endbr64 
   f:   55                      push   rbp
  10:   48 89 e5                mov    rbp,rsp
  13:   48 89 7d f8             mov    QWORD PTR [rbp-0x8],rdi
  17:   48 89 75 f0             mov    QWORD PTR [rbp-0x10],rsi
  1b:   48 89 55 e8             mov    QWORD PTR [rbp-0x18],rdx
  1f:   48 89 4d e0             mov    QWORD PTR [rbp-0x20],rcx
  23:   4c 89 45 d8             mov    QWORD PTR [rbp-0x28],r8
  27:   4c 89 4d d0             mov    QWORD PTR [rbp-0x30],r9
  2b:   90                      nop
  2c:   5d                      pop    rbp
  2d:   c3                      ret    

000000000000002e <PtrArg2>:
  2e:   f3 0f 1e fa             endbr64 
  32:   55                      push   rbp
  33:   48 89 e5                mov    rbp,rsp
  36:   48 89 7d f8             mov    QWORD PTR [rbp-0x8],rdi
  3a:   48 89 75 f0             mov    QWORD PTR [rbp-0x10],rsi
  3e:   48 89 55 e8             mov    QWORD PTR [rbp-0x18],rdx
  42:   48 89 4d e0             mov    QWORD PTR [rbp-0x20],rcx
  46:   4c 89 45 d8             mov    QWORD PTR [rbp-0x28],r8
  4a:   4c 89 4d d0             mov    QWORD PTR [rbp-0x30],r9
  4e:   90                      nop
  4f:   5d                      pop    rbp
  50:   c3                      ret 

和 的参数数量不同PtrArg1PtrArg2但两者的汇编指令相同。为什么?

4

2 回答 2

3

这是由于调用约定(System V AMD64 ABI,当前版本 1.0)。前六个参数在整数寄存器中传递,所有其他参数都被压入堆栈。

执行 until location 后PtrArg2+0x4e,您会得到以下堆栈布局:

+----------+-----------------+
|  offset  |     content     |
+----------+-----------------+
| rbp-0x30 | f               |
| rbp-0x28 | e               |
| rbp-0x20 | d               |
| rbp-0x18 | c               |
| rbp-0x10 | b               |
| rbp-0x8  | a               |
| rbp+0x0  | saved rbp value |
| rbp+0x8  | return address  |
| rbp+0x10 | g               |
| rbp+0x18 | h               |
+----------+-----------------+

由于gh是由调用者推送的,因此您对这两个函数都获得了相同的反汇编。对于来电者

void Caller()
{
    PtrArg2(1, 2, 3, 4, 5, 6, 7, 8);
}

(为了清楚起见,我省略了必要的演员表)我们将得到以下反汇编:

Caller():
    push    rbp
    mov     rbp, rsp
    push    8
    push    7
    mov     r9d, 6
    mov     r8d, 5
    mov     ecx, 4
    mov     edx, 3
    mov     esi, 2
    mov     edi, 1
    call    PtrArg2
    add     rsp, 16
    nop
    leave
    ret

见编译器资源管理器

参数h = 8g = 7在调用之前被压入堆栈PtrArg2

于 2020-04-25T09:40:32.313 回答
1

消失?您期望编译器会发出 asm 指令来实现的函数对它们做什么?

你实际上return;是函数中唯一的语句,void所以函数除了ret. 如果您使用正常级别的优化(如 )进行编译,-O2那么您将得到的就是这些。调试模式代码通常看起来并不有趣,并且充满了冗余/无用的东西。 如何从 GCC/clang 程序集输出中删除“噪音”?

您看到某些参数的任何指令的唯一原因是您在调试模式下编译,即默认优化级别-O0,反优化调试模式。 每个 C 对象(register本地对象除外)都有一个内存地址,调试模式确保该值在每个 C 语句之前/之后实际上存在于内存中。这意味着在函数入口时将寄存器 args 溢出到堆栈中。 为什么clang用-O0(对于这个简单的浮点总和)产生效率低下的asm?

x86-64 System V ABI的调用约定在寄存器中传递前 6 个整数参数,其余的在堆栈中。 堆栈参数已经有内存地址;编译器不会发出代码将它们复制到返回地址下方的其他本地变量旁边;那将毫无意义。被调用者“拥有”它自己的堆栈参数,即它可以将新值存储到调用者写入参数的堆栈空间中,因此即使函数要修改参数,该空间也可以是参数的真实地址。

于 2020-04-25T10:23:00.217 回答