System V amd64 如何处理非常长的 return 值?
How does System V amd64 handle very long return values?
我正在简要研究 amd64 / x86-64 架构的 System V ABI,我很好奇它如何处理超过 128 位的 return 值,其中 rax
和 rdx
还不够。
我在 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
是提供给函数的第一个参数,这是不可能的,因为函数不接受任何参数。所以我在这里有几个问题:
- 当函数
bigfunc
没有参数时,rdi
是什么?
- 什么是
rax
(作为包含return值的寄存器),当rdx
在此函数中未被触及时?
- 函数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 官方版本,尽管链接目前无法正常工作。
我正在简要研究 amd64 / x86-64 架构的 System V ABI,我很好奇它如何处理超过 128 位的 return 值,其中 rax
和 rdx
还不够。
我在 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
是提供给函数的第一个参数,这是不可能的,因为函数不接受任何参数。所以我在这里有几个问题:
- 当函数
bigfunc
没有参数时,rdi
是什么? - 什么是
rax
(作为包含return值的寄存器),当rdx
在此函数中未被触及时? - 函数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 官方版本,尽管链接目前无法正常工作。