views:

363

answers:

4

When making a function call in Linux (or OS X for that matter), can the callee modify the values of the arguments on the stack? I was under the assumption that since the caller is the one that cleans them up, that they should contain the same values after the function call. However I found that GCC with -O2 was modifying parameters that were passed to it on the stack. I have also looked for documentation including the System V i386 calling conventions, but was unable to find a definitive answer to this.

Here is some sample code I was debugging.

pushl %eax       # %eax = 0x28
call _print_any
popl %eax
                 # %eax is now 0x0a

I would assume that GCC modifying that parameter on the stack is fine, but I want to know where it is specified that it can do so.

+2  A: 

Although the caller (in some calling conventions) is the one that cleans up the arguments, all it's really doing is deallocating the space previously allocated on the stack to hold the argument values. The callee is free to modify the values during execution of the function, because the caller isn't going to look at their values later.

In the example you posted, GCC has emitted the popl %eax instruction to deallocate the space taken by the parameter on the stack. All it really needs to do is add 4 to %esp (the stack on x86 grows downwards in memory), and executing the popl %eax instruction is the shortest and fastest way to do this. If the compiler needed to deallocate 20 values, it would probably modify %esp directly instead of emitting 20 popl instructions.

You will probably find that the new value of %eax is not used in the following code.

Greg Hewgill
Actually, this was from the compiler I was working on, but I was stashing a value that is live later on in the program. I assumed that it was safe and wanted to avoid putting its value on the stack twice (once to save and once for argument passing).
Adam K. Johnson
Right, in that case you can definitely *not* assume that the popped value will always equal what you previously pushed. The callee is free to modify its arguments on the stack (they are treated the same as local variables).
Greg Hewgill
+1  A: 

Yes, the callee can modify the arguments on the stack. As far as the callee is concerned, they are the same as local variables. The caller does clean them up but ignores the value.

If you're talking C or C++ POD, clean up is simply modifying the stack pointer.

If you're talking about C++ with a destructor, the caller is responsible for invoking the destructor but destructors for generic classes need to be written to cleanup any value.

R Samuel Klatchko
+1  A: 

In standard C, the callee can modify the values of its arguments all it wants, but the caller will never see the changes.

What may be confusing is that if one passes a POINTER to a value, then the callee can change that value by dereferencing the pointer, but if the callee actually changes the pointer itself the caller will not see that change.

A small nit: the C standard does not require that the implementation even HAVE a stack.

Berry
I don't think this is quite addressing OP's concerns. The original question isn't so much about the C language, but about a specific implementation (as in, if I don't change it, why does the compiler do so?). I won't downvote, but I think you're headed the wrong way with this.
San Jacinto
Yes in practice an arguments might never actually be in any kind of stack, it would be in a named register (this is especially true in platforms with a lot of registers - like MIPS).
joemoe
I'm not talking about the arguments in C, I am talking about calling C functions from assembly and at the machine level.
Adam K. Johnson
Where does the C standard make any projections on what the machine should do to accomplish standard compliance? Imagine this scenario: you are passing a struct by value. Inside the function, you decide that for clarity, you will create another structure and set it "equal" to the first one, and only operate on the new one from now on. If you are passing by value, and you only use the initial, passed-in struct one time, why shouldn't the compiler recognize this and use the original struct rather than creating a new one?
San Jacinto
A compiler implements a set of calling conventions for other code and languages to operate with it in compiled code that is the essence of a calling convention and it is distinct from the C language itself, as it is a specific architecture implementation. An implementation can do many things as long as it acts the same for C code, but it could be doing any number of things in machine code to get it done. That is where the calling convention comes in. For i386, Linux uses the System V C calling convention, and OS X uses a modified version of it (mostly alignment restrictions).
Adam K. Johnson
A: 

If you pass by value:

call do_it(to_it);

The argument is copied (probably to the begining of the stack but maybe not depending on your compiler) the celled program can mess with this copy as much as it wants but the variable in the clling program will not be changed.

If you pass by reference:

call do_it(&to_it);

Then the address of the variable is passed. Any changes the called variable makes will be to the original variable in the calling program.

James Anderson
I am not talking about within a language like C or C++ (nor passing by reference/value). I am talking about the actual, runtime stack parameters at the machine level and whether or not those stack locations can be modified be the callee.
Adam K. Johnson