Page 1 of 1
[Stack Cleanup] returning a C struct from assembly
Posted: Sun May 29, 2016 4:39 am
by Neroku
Hello,
I've been having a look at the assembly code GCC generates when it finds a call to function that is supposed to return a C struct:
Code: Select all
struct st_foo function(void);
struct st_foo foo;
foo = function();
Before the function call, the caller pushes on the stack the address of the struct the return value will be assigned to. This pushed address is then used by the callee, so that it writes the return value at this address.
So far, so good. My question comes up when I observe how the stack is cleaned up:
The callee cleans up the stack by providing assembly instruction
ret with an immediate operand which specifies the numbers of bytes to be removed from the stack before returning. However, I expected the caller to clean up the stack, not the callee.
Isn't GCC using the
cdecl calling convention?
Isn't then the callee the one responsilbe for cleaning up the stack?
What wrong assumption am I making?
Thanks in advance
Re: [Stack Cleanup] returning a C struct from assembly
Posted: Sun May 29, 2016 4:54 am
by bluemoon
A quote from wiki:
https://en.wikipedia.org/wiki/X86_calling_conventions
There are some variations in the interpretation of cdecl,[2] particularly in how to return values. As a result, x86 programs compiled for different operating system platforms and/or by different compilers can be incompatible, even if they both use the "cdecl" convention and do not call out to the underlying environment. Some compilers return simple data structures with a length of 2 registers or less in the register pair EAX:EDX, and larger structures and class objects requiring special treatment by the exception handler (e.g., a defined constructor, destructor, or assignment) are returned in memory. To pass "in memory", the caller allocates memory and passes a pointer to it as a hidden first parameter; the callee populates the memory and returns the pointer, popping the hidden pointer when returning.
Re: [Stack Cleanup] returning a C struct from assembly
Posted: Sun May 29, 2016 6:12 am
by Neroku
bluemoon wrote:A quote from wiki:
https://en.wikipedia.org/wiki/X86_calling_conventions
There are some variations in the interpretation of cdecl,[2] particularly in how to return values. As a result, x86 programs compiled for different operating system platforms and/or by different compilers can be incompatible, even if they both use the "cdecl" convention and do not call out to the underlying environment. Some compilers return simple data structures with a length of 2 registers or less in the register pair EAX:EDX, and larger structures and class objects requiring special treatment by the exception handler (e.g., a defined constructor, destructor, or assignment) are returned in memory. To pass "in memory", the caller allocates memory and passes a pointer to it as a hidden first parameter; the callee populates the memory and returns the pointer, popping the hidden pointer when returning.
Is there then any way to ensure that a future GCC version will not break the current behavior?
Re: [Stack Cleanup] returning a C struct from assembly
Posted: Sun May 29, 2016 8:51 am
by bluemoon
I can't speak for the GCC community but in general there is no guarantee in future.
That's why people suggest to build your own toolchain, such that you can fallback to "tested version" if any future changes break your build.
Re: [Stack Cleanup] returning a C struct from assembly
Posted: Sun May 29, 2016 10:20 am
by Velko
Neroku wrote:Is there then any way to ensure that a future GCC version will not break the current behavior?
By doing it "manually"?
Code: Select all
void function(struct st_foo *foo);
struct st_foo foo;
function(&foo);
Re: [Stack Cleanup] returning a C struct from assembly
Posted: Sun May 29, 2016 12:26 pm
by Neroku
Velko wrote:Neroku wrote:Is there then any way to ensure that a future GCC version will not break the current behavior?
By doing it "manually"?
Code: Select all
void function(struct st_foo *foo);
struct st_foo foo;
function(&foo);
Yes, this should do the trick. I have to change the function prototype, though.
Thanks.