System V amd64 如何处理非常长的 return 值?

How does System V amd64 handle very long return values?

我正在简要研究 amd64 / x86-64 架构的 System V ABI,我很好奇它如何处理超过 128 位的 return 值,其中 raxrdx还不够。

我在 Ubuntu 18.04 64 位(更一般地说,任何符合 amd64 POSIX 的系统)上编写了以下 C 代码:

struct big {
    long long a, b, c, d;
};

struct big bigfunc(void) {
    struct big r = {12, 34, 56, 78};
    return r;
}

将其编译为 gcc -S -masm=intel t.c,并检查 t.s:

        .file   "t.c"
        .intel_syntax noprefix
        .text
        .globl  bigfunc
        .type   bigfunc, @function
bigfunc:
.LFB0:
        .cfi_startproc
        mov     QWORD PTR -40[rsp], rdi
        mov     QWORD PTR -32[rsp], 12
        mov     QWORD PTR -24[rsp], 34
        mov     QWORD PTR -16[rsp], 56
        mov     QWORD PTR -8[rsp], 78
        mov     rcx, QWORD PTR -40[rsp]
        mov     rax, QWORD PTR -32[rsp]
        mov     rdx, QWORD PTR -24[rsp]
        mov     QWORD PTR [rcx], rax
        mov     QWORD PTR 8[rcx], rdx
        mov     rax, QWORD PTR -16[rsp]
        mov     rdx, QWORD PTR -8[rsp]
        mov     QWORD PTR 16[rcx], rax
        mov     QWORD PTR 24[rcx], rdx
        mov     rax, QWORD PTR -40[rsp]
        ret
        .cfi_endproc
.LFE0:
        .size   bigfunc, .-bigfunc
        .ident  "GCC: (Ubuntu 7.3.0-27ubuntu1~18.04) 7.3.0"
        .section        .note.GNU-stack,"",@progbits

毫不奇怪,结构定义没有编译成任何指令,所以输出只包含函数 bigfunc。输出程序集看起来非常简单,从堆栈为 struct big r 分配内存并分配初始值,然后 returning 它。

如果我理解正确,在执行ret之前,寄存器rax在函数调用开始时包含rdi的值(来自QWORD PTR -40[rbp]) .根据 SysV,rdi 是提供给函数的第一个参数,这是不可能的,因为函数不接受任何参数。所以我在这里有几个问题:

  1. 当函数 bigfunc 没有参数时,rdi 是什么?
  2. 什么是rax(作为包含return值的寄存器),当rdx在此函数中未被触及时?
  3. 函数return这个256位的C结构是怎样的?

根据 ABI (1),第 22 页

If the type has class MEMORY, then the caller provides space for the return value and passes the address of this storage in %rdi as if it were the first argument to the function. In effect, this address becomes a “hidden” first ar- gument. This storage must not overlap any data visible to the callee through other names than this argument. On return %rax will contain the address that has been passed in by the caller in %rdi

第 17、18 和 19 页描述了 class化验,我相信 以下第 19 页是将您的 struct big 指定为 MEMORY class.

的子句

(c) If the size of the aggregate exceeds two eightbytes and the first eight- byte isn’t SSE or any other eightbyte isn’t SSEUP, the whole argument is passed in memory.

即调用者必须为 return 值分配内存,并在 %rdi 中传递指向该内存的指针(以及被调用函数 returns 在 %rax 中的相同地址)

(1) https://github.com/hjl-tools/x86-psABI/wiki/X86-psABI 上有更新的 ABI 官方版本,尽管链接目前无法正常工作。