Consider this simple function:
struct Foo {
int a;
int b;
int c;
int d;
int e;
int f;
};
Foo foo() {
Foo f;
f.a = 1;
f.b = 2;
f.c = 3;
f.d = 4;
f.e = 5;
f.f = 6;
return f;
}
It generates the following assembly:
0000000000400500 <foo()>:
400500: 48 ba 01 00 00 00 02 movabs rdx,0x200000001
400507: 00 00 00
40050a: 48 b9 03 00 00 00 04 movabs rcx,0x400000003
400511: 00 00 00
400514: 48 be 05 00 00 00 06 movabs rsi,0x600000005
40051b: 00 00 00
40051e: 48 89 17 mov QWORD PTR [rdi],rdx
400521: 48 89 4f 08 mov QWORD PTR [rdi+0x8],rcx
400525: 48 89 77 10 mov QWORD PTR [rdi+0x10],rsi
400529: 48 89 f8 mov rax,rdi
40052c: c3 ret
40052d: 0f 1f 00 nop DWORD PTR [rax]
Based on the assembly, I understand that the caller created space for Foo
on its stack, and passed that information in rdi
to the callee.
I am trying to find documentation for this convention. Calling convention in linux states that rdi
contains the first integer argument. In this case, foo
doesn't have any arguments.
Moreover, if I make foo
take one integer argument, that is now passed as rsi
(register for second argument) with rdi used for address of the return object.
Can anyone provide some documentation and clarity on how rdi
is used in system V ABI?
See section 3.2.3 Parameter Passing in the ABI docs which says:
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 argument.
On return %rax will contain the address that has been passed in by the caller in %rdi.