我正在简要研究amd64 / x86-64体系结构的System V ABI,并很好奇它如何处理128位以上的返回值,而其中rax
和rdx
还不够。 / p>
我在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
分配内存,并分配初始值,然后将其返回。
如果我正确理解,在执行ret
之前,寄存器rax
在函数调用开始时(从rdi
开始包含QWORD PTR -40[rbp]
的值。根据SysV,rdi
是提供给该函数的第一个参数,这是不可能的,因为该函数不接受任何参数。所以我在这里有几个问题:
rdi
不带参数时,bigfunc
是什么?rax
时,rdx
是什么(作为包含返回值的寄存器)?答案 0 :(得分:1)
根据ABI(1),第22页
如果类型具有MEMORY类,则调用方为返回提供空间 值,并以%rdi的形式传递此存储的地址,就像它是第一个 该函数的参数。实际上,该地址成为“隐藏”的第一个Ar- 胶质的。此存储空间不得与被调用方通过以下方式可见的任何数据重叠 除此参数外的其他名称。 返回时,%rax将包含由 %rdi
中的呼叫者
第17、18和19页描述了分类,我相信
第19页的以下内容是将struct big
指定为MEMORY类的子句。
(c)如果聚合的大小超过两个八字节,则第一个 整个八字节不是SSE或其他八字节不是SSEUP 参数在内存中传递。
即调用者必须为返回值分配内存,并在%rdi中传递一个指向该内存的指针(被调用的函数在%rax中返回相同的地址)
(1)在https://github.com/hjl-tools/x86-psABI/wiki/X86-psABI上有更新的ABI官方版本,尽管这些链接当前无法正常工作。