Why can't stdcall handle varying amounts of arguments? - c

My understanding is that for the cdecl calling convention, the caller is responsible for cleaning the stack and therefore can pass any number of arguments.
On the other hand, stdcall callees clean the stack and therefore cannot receive varying amounts of arguments.
My question is twofold:
Couldn't stdcall functions also get a parameter about how many variables there are and do the same?
How do cdecl functions know how many arguments they've received?

Couldn't stdcall functions also get a parameter of how many variables are there and do the same?
Yes, sure. You could invent any calling convention. But then that wouldn't be stdcall anymore.
How do cdecl functions know how many arguments they've received?
They don't. They assume to find the required number of arguments in the locations specified by the calling convention. If they are missing, then that's a bug which the code cannot observe. The following code compiles:
printf("%s");
even though it is missing an argument. The result is undefined. For printf-style functions compilers generally issue warnings (if they can) due to knowledge of the functions' internals, but that's not a solution that can be generically applied.
If a caller provides the wrong number or types of arguments, then the behavior is undefined.

Couldn't stdcall functions also get a parameter of how many variables are there and do the same?
If the caller has to pass a separate arg with the number of bytes to be popped, that's more work than just doing add esp, 16 or whatever after the call (cdecl style caller-pops). It would totally defeat the purpose of stdcall, which is to save a few bytes of space at each call site, especially for naive code-gen that wouldn't defer popping args across a couple calls, or reuse the space allocated by a push with mov stores. (There are often multiple call-sites for each function, so the extra 2 bytes for ret imm16 vs. ret is amortized over that.)
Even worse, the callee can't use a variable number efficiently on x86 / x86-64. ret imm16 only works with an immediate (constant embedded in the machine code), so to pop a variable number of bytes above the return address, a function would have to copy the return address high up in the stack and do a plain ret from there. (Or defeat branch return-address branch prediction by popping the return address into a register.)
See also:
Stack cleanup in stdcall (callee-pops) for variable arguments (x86 asm)
What calling convention does printf() in C use? (why stdcall is unusable)
How do cdecl functions know how many arguments they've received?
They don't.
C is designed around the assumption that variadic functions don't know how many args they received, so functions need something like a format string or sentinel to know how many to iterate. For example, the POSIX execl(3) (wrapper for the execve(2) system call) takes a NULL-terminated list of char* args.
Thus calling conventions in general don't waste code-size and cycles on providing a count as a side-channel; whatever info the function needs will be part of the real C-level args.
Fun fact: printf("%d", 1, 2, 3) is well-defined behaviour in C, and is required to safely ignore args beyond the ones referenced by the format string.
So using stdcall and calculating based on the format-string can't work. You're right, if you wanted to make a callee-pops convention that worked for variadic functions, you would need to pass a size somewhere, e.g. in a register. But like I said earlier, the caller knows the right number, so it would be vastly easier to let the caller manage the stack, instead of making the callee dig up this extra arg later. That's why no real-world calling conventions work this way, AFAIK.

Passing the number of arguments in a callee cleans the stack convention would be possible but the additional overhead of the extra parameter outweighs its usefulness. It wastes stack space with the extra parameter and complicates the callees stack handling.
The reason stdcall was invented is because it makes the code smaller. One adjustment in the callee vs adjusting every place it is called (on x86 or on another architecture when there are more parameters than you can pass in registers). The x86 even has a retn # instruction where # is the number of bytes to adjust. Windows NT switched from cdecl to stdcall early in its development and it supposedly reduced the size and improved speed (I believe Larry Osterman blogged about this (mini answer here)).
cdecl functions do not know how many parameters there are. You are allowed (on the ABI level) to pass more arguments than the function will actually use. A printf style function will use the format parameter as a "guide" to access the parameters one by one. When this is done the callee also has to be informed of the type of each parameter (so it knows the size which in turn, in an implementation defined manner, allows it to walk the list of parameters. On Windows x86 the parameters are on the stack, all you need is the parameter size to calculate their offset as you walk the stack). The va_list and its macros in stdarg.h provides the helping glue for C functions to access these parameters.

My summary, based on #IInspectable's answer.
stdcall functions could also get a parameter of how many variables there are, but then that wouldn't be stdcall anymore.
cdecl don't know how many arguments to read. It is assumed that the function will be able to derive the number of arguments based on a pre-determined amount of arguments, like a format string for printf.
If a caller provides the less arguments than could be derived, or of an unexpected type, then the behavior is undefined. (Thanks for the correction #Peter Cordes)

Related

Does the C ABI require arguments to be passed to functions using the stack?

Does the C ABI require arguments to always be passed to functions using only the stack?
ABI or Application Binary Interface covers various details in the contract between pieces of binary code.
(A broad definition) - It defines the mechanisms by which functions are invoked, how parameters are passed between caller and callee, how return values are provided to callers, how libraries are implemented, and how programs are loaded into memory.
(Specifically) the calling convention, which controls how functions' arguments are passed and return values retrieved; for example, whether all parameters are passed on the stack or some are passed in registers, which registers are used for which function parameters, and whether the first function parameter passed on the stack is pushed first or last onto the stack.
A live example - refer to calling convention mentioned by ARM ABI Procedure Call Standard for ARM Architecture - you may refer to section on Stack (end of page 16) - The stack is a contiguous area of memory that may be used for storage of local variables and for passing
additional arguments to subroutines when there are insufficient argument registers available
Yes, arguments to functions are always passed using the stack. This is why if you are going to be passing something large, it is advisable to pass a pointer to avoid a stack overflow.

Is it safe to invoke a C function with more parameters than it expects?

I was reading on wikipedia about the cdecl calling convention. Since the parameters are pushed on the stack in reverse order, I believe it is safe to call a C function with more parameters than it expects.
Am I right or did I miss something?
Note: I am not talking about variadic functions.
I just had a quick look into ISO/IEC 9899 (a.k.a. C99): There's no word about calling conventions anywhere, thus (as suggested in the comments) you should clearly not do this. Even if it might work on a certain architecture, a certain operating system, and a certain version of a certain compiler, there is absolutely no guarantee that it will still work when only one of those parameters changes.
You are making one big wrong assumption: The so-called C calling convention is not contractual for C.
While old C compilers were forced to use such a calling convention (even if it was suboptimal), due to there being no function prototypes, modern compilers can (and are allowed to) use more efficient callee-clean calling conventions for all but old-style and vararg functions. Most compilers have a switch to select the standard calling convention used.
It is not!!!
This code causes a segmentation fault:
#include <stdio.h>
#define stdcall __attribute__((stdcall))
stdcall void func(int param1, int param2)
{
printf("%d, %d\n", param1, param2);
}
int main()
{
void(*f)(int, int, int) = func;
f(66, 67, 666);
f(1, 2, 3);
return 0;
}
This is just an elaboration to what other people have pointed out about calling conventions. I believe a POC helps making a point.
Yes, it is safe, partially for the reason you gave (params pushed in reverse order), and also partially due to the calling convention.
C Calling Convention
The C calling convention is that the Caller cleans up parameters.
(the alternative is that the Callee cleans up).
Because the caller knows how many params it pushed, it will know how many to properly clean up, regardless of how many params the Callee used or expected.
Pushing args in reverse order
When parameters are pushed onto the stack in reverse order, the 1st parameter gets pushed on last. Regardless of how many params were pushed, the Callee always knows where to find param #1, at the top of the stack. (and also param #2, #3, etc).
If the stack convention were reversed, param 1 would be put on the stack first, and could be "buried" by an arbitrary number of subsequent parameters; the Callee would not know how far into the stack to look.

Call C address as function without prototype

I need to call a function in C by just knowing it address, and no information
on it prototype (I can't cast it to a C function pointer).
The information I have on this function is it address.
I also know the parameters I want to pass to it (Thanks to a void pointer) and
the size of the arguments array (accessed trough the void pointer).
I also want to respect the C calling convention. For x86 version, I pretty much
know how to do it (allocate the space on the stack, copy the parameters to
that space and finally call the function).
The problem is with x64 convention (Linux one for now) where parameters are
passed through registers. I have no idea of the size of each parameter to fill
appropriately registers, I only know the size of the parameter array.
Also, I don't want to depend on gcc so I can't use __builtin_apply that seems
to be not standard and also be pretty dark.
I want to write my own piece of code to support multi compiler and also to
learn interesting stuff.
So basically, the function I want to write as the same prototype as
__builtin_apply which is:
void *call_ptr(void (*fun)(), void *params, size_t size);
I want also the code to write it in C (thanks to asm inline) or pure x64 asm.
So is there a way to do this properly and with respect of the calling
convention ? Or is this impossible with the x64 convention without knowing
exactly the prototype of the function called ?
Especially for x64 calling convention on Linux this will not work at all.
The reason is the very complicated calling convention.
Some examples:
void funcA(float64 x);
void funcB(int64 x);
In these two cases the value "x" is passed to the functions differently because floating point and integer are passed to the functions in different registers.
void funcC(float64 x,int64 y);
void funcD(int64 y,float64 x);
In these two cases the arguments "x" and "y" are in different order. However they are passed to the function in the same way (both functions use the same register for "x" and the same register for "y").
Conclusion: To create a function that does what you want you'd have to pass a string containing the argument types of each argument to the assembler function. The number/size of arguments is definitely not enough. However it would definitely be possible - as long as it must work only on Linux.
I think, all of your decision will not be supported multi-compiler, because the mechanism of passing arguments to function (registers, their order, stack, memory) - it's compiler dependence feature...

C function call with too few arguments

I am working on some legacy C code. The original code was written in the mid-90s, targeting Solaris and Sun's C compiler of that era. The current version compiles under GCC 4 (albeit with many warnings), and it seems to work, but I'm trying to tidy it up -- I want to squeeze out as many latent bugs as possible as I determine what may be necessary to adapt it to 64-bit platforms, and to compilers other than the one it was built for.
One of my main activities in this regard has been to ensure that all functions have full prototypes (which many did not have), and in that context I discovered some code that calls a function (previously un-prototyped) with fewer arguments than the function definition declares. The function implementation does use the value of the missing argument.
Example:
impl.c:
int foo(int one, int two) {
if (two) {
return one;
} else {
return one + 1;
}
}
client1.c:
extern foo();
int bar() {
/* only one argument(!): */
return foo(42);
}
client2.c:
extern int foo();
int (*foop)() = foo;
int baz() {
/* calls the same function as does bar(), but with two arguments: */
return (*foop)(17, 23);
}
Questions: is the result of a function call with missing arguments defined? If so, what value will the function receive for the unspecified argument? Otherwise, would the Sun C compiler of ca. 1996 (for Solaris, not VMS) have exhibited a predictable implementation-specific behavior that I can emulate by adding a particular argument value to the affected calls?
EDIT: I found a stack thread C function with no parameters behavior which gives a very succinct and specific, accurate answer. PMG's comment at the end of the answer taks about UB. Below were my original thoughts, which I think are along the same lines and explain why the behaviour is UB..
Questions: is the result of a function call with missing arguments defined?
I would say no... The reason being is that I think the function will operate as-if it had the second parameter, but as explained below, that second parameter could just be junk.
If so, what value will the function receive for the unspecified argument?
I think the values received are undefined. This is why you could have UB.
There are two general ways of parameter passing that I'm aware of... (Wikipedia has a good page on calling conventions)
Pass by register. I.e., the ABI (Application Binary Interface) for the plat form will say that registers x & y for example are for passing in parameters, and any more above that get passed via stack...
Everything gets passed via stack...
Thus when you give one module a definition of the function with "...unspecified (but not variable) number of parameters..." (the extern def), it will not place as many parameters as you give it (in this case 1) in either the registers or stack location that the real function will look in to get the parameter values. Therefore the second area for the second parameter, which is missed out, essentially contains random junk.
EDIT: Based on the other stack thread I found, I would ammended the above to say that the extern declared a function with no parameters to a declared a function with "unspecified (but not variable) number of parameters".
When the program jumps to the function, that function assumes the parameter passing mechanism has been correctly obeyed, so either looks in registers or the stack and uses whatever values it finds... asumming them to be correct.
Otherwise, would the Sun C compiler of ca. 1996 (for Solaris, not VMS) have exhibited a >> predictable implementation-specific behavior
You'd have to check your compiler documentation. I doubt it... the extern definition would be trusted completely so I doubt the registers or stack, depending on parameter passing mechanism, would get correctly initialised...
If the number or the types of arguments (after default argument promotions) do not match the ones used in the actual function definition, the behavior is undefined.
What will happen in practice depends on the implementation. The values of missing parameters will not be meaningfully defined (assuming the attempt to access missing arguments will not segfault), i.e. they will hold unpredictable and possibly unstable values.
Whether the program will survive such incorrect calls will also depend on the calling convention. A "classic" C calling convention, in which the caller is responsible for placing the parameters into the stack and removing them from there, will be less crash-prone in presence of such errors. The same can be said about calls that use CPU registers to pass arguments. Meanwhile, a calling convention in which the function itself is responsible for cleaning the stack will crash almost immediately.
It is very unlikely the bar function ever in the past would give consistent results. The only thing I can imagine is that it is always called on fresh stack space and the stack space was cleared upon startup of the process, in which case the second parameter would be 0. Or the difference between between returning one and one+1 didn't make a big difference in the bigger scope of the application.
If it really is like you depict in your example, then you are looking at a big fat bug. In the distant past there was a coding style where vararg functions were implemented by specifying more parameters than passed, but just as with modern varargs you should not access any parameters not actually passed.
I assume that this code was compiled and run on the Sun SPARC architecture. According to this ancient SPARC web page: "registers %o0-%o5 are used for the first six parameters passed to a procedure."
In your example with a function expecting two parameters, with the second parameter not specified at the call site, it is likely that register %01 always happened to have a sensible value when the call was made.
If you have access to the original executable and can disassemble the code around the incorrect call site, you might be able to deduce what value %o1 had when the call was made. Or you might try running the original executable on a SPARC emulator, like QEMU. In any case this won't be a trivial task!

Who is responsible for cleanup?

I wish to know which one is responsible for cleanup of the stack
Suppose you have a function fun lets say like this:
var = fun(int x, int y, float z, char x);
when fun will get called it will go into the stack along with the parameters then when the function returns who is responsible for cleanup of the stack is it the function it self or the "var" which will hold the return value.
One more thing, can anyone explain the concepts of calling conventions?
You referred to the answer yourself: calling conventions.
A calling convention is similar to a contract. It decides the following things:
Who is responsible to cleanup the parameters.
How and in which order the parameters are passed to the called function.
Where the return value is stored.
There are many different calling conventions, depending on the platform and the programming environment. Two common calling conventions on the x86 platforms are:
stdcall
The parameters are passed onto the stack from right to left. The called function cleans up the stack.
cdecl
The parameters are passed onto the stack from right to left. The calling function cleans up the stack.
In both cases the return value is in the EAX register (or ST0 for floating point values)
Many programming languages for the x86 platform allow to specify the calling convention, for example:
Delphi
function MyFunc(x: Integer): Integer; stdcall;
Microsoft C/C++
int __stdcall myFunc(int x)
Some usage notes:
When creating a simple application it's rarely necessary to change or to know about the calling convention, but there are two typical cases where you need to concern yourself with calling conventions:
When calling external libraries, Win32 API for example: You have to use compatible calling conventions, otherwise the stack might get corrupted.
When creating inline assembler code: You have to know in which registers and where on the stack you find the variables.
For further details I recommend these Wikipedia articles:
Calling convention
x86 calling conventions
calling convention refers to who is doing the cleanup of the stack; caller or callee.
Calling conventions can differ in:
where parameters and return values are placed (in registers; on the call
stack; a mix of both)
the order in which parameters are passed (or parts of a single
parameter)
how the task of setting up and cleaning up a function call is divided
between the caller and the callee.
which registers that may be directly used by the callee may sometimes also
be included
Architectures almost always have more
than one possible calling convention.
By the time that line is complete var will hold the value returned by fun() and any memory on the stack used by fun will be gone: "push", "pop" all tidy.
Calling conventions: everything that the compiler organises so that fun can do its work. Consider those parameters x, y, z. What order do they get pushed onto the stack (indeed do they get passed via the stack)? Doesn't matter so long as the caller and callee agree! It's a convention.

Resources