2

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.

André Puel
  • 8,741
  • 9
  • 52
  • 83
  • There was another question with the exact same title than mine, but it was concerning GTK. Mine concerns C functions. – André Puel Apr 09 '14 at 18:41
  • It wouldn't compile. What exactly are you talking about? – Engineer2021 Apr 09 '14 at 18:44
  • Interesting question, but which ill-behaving compiler would accept this? – Andreas Wiese Apr 09 '14 at 18:44
  • @staticx: Sometimes, through ugly type-casts, or cross-language interfaces, it is possible to push too many parameters on the stack. – abelenky Apr 09 '14 at 18:46
  • 1
    @staticx It will compile if you'll make a cast. – Cruel_Crow Apr 09 '14 at 18:46
  • @AndreasWiese I am making a dynamic object interface that by default will always pass one parameters (void*) which the called function may use or not. – André Puel Apr 09 '14 at 18:48
  • 1
    I didn't think this through deeply, but the first thought that came to my mind was… Eeeek. Do you really think this is a favorable design and couldn't be done in a more… conservative way, not making crude assumptions about which way abusing the calling convention would be safe? – Andreas Wiese Apr 09 '14 at 18:50
  • Are you specifically talking about `cdecl` on x86? In general it is wise to describe the architecture that you're interested in when discussing calling conventions. – Eric Lippert Apr 09 '14 at 18:57
  • You appear to be conflating "C functions" and "`cdecl` functions". – user253751 Jan 28 '16 at 03:54

4 Answers4

6

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.

Andreas Wiese
  • 718
  • 3
  • 8
  • Does not `extern "C"` in C++ force the calling convention? – André Puel Apr 09 '14 at 19:01
  • This at most forces _a_ »C Calling Convention« your environment uses for C. But there is not _the_ »C Calling Convention«. It may differ across operating systems (and its versions), compilers (and its versions), hardware architectures… It's not even safe to assume that the calling convention for C programs on Linux/arm is the same as the one used on Linux/x86 (for example). – Andreas Wiese Apr 09 '14 at 19:05
  • In fact, the standard explicitly allows calling a variadic function with more arguments than it might expect based on, say, the format string also passed to it (but the question is not about variadic functions). – Pascal Cuoq Apr 09 '14 at 19:22
  • Yeah, but @AndréPuel explicitly did _not_ ask for variadic functions. – Andreas Wiese Apr 09 '14 at 19:23
5

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.

Deduplicator
  • 44,692
  • 7
  • 66
  • 118
  • Interesting. Where can I read more about this? And how can I see that, say, in Visual Studio? As far as I remember, when I look on assembly code generated with VS, it gives me a good old calling code, as abelenky described it. – Cruel_Crow Apr 09 '14 at 18:54
  • For the details, the compiler manuals and referenced platform abis. For the requirements, the C standard itself. BTW: Microsoft likes to use callee-cleaned `__stdcall`, which has a fallback to callee-cleaned for var-arg/no prototype. – Deduplicator Apr 09 '14 at 18:57
  • Does not `extern "C"` in C++ force the calling convention? – André Puel Apr 09 '14 at 19:02
  • No, it only should make the compiler output functions callable from C, if possible. But see above, the so-called C calling convention is not contractual. – Deduplicator Apr 09 '14 at 19:06
4

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.

imreal
  • 10,178
  • 2
  • 32
  • 48
  • 3
    `void main()`. A classic. – Deduplicator Apr 09 '14 at 19:22
  • `int main()` same result :) – imreal Apr 09 '14 at 19:26
  • 1
    And quite as classic! It's `int main(void)` or `int main(int argc, char *argv[])` since 1989. – Andreas Wiese Apr 09 '14 at 19:26
  • You should have `int main(void)` to make it standards compliant. – ajay Apr 09 '14 at 19:27
  • No, this is a quote from C99 standard: `(5.1.2.2.1) It shall be defined with a return type of int and with no parameters ... or with two parameters ... or in some other implementation-defined manner` – imreal Apr 09 '14 at 19:28
  • No reason to go for implementation defined in this example found. Would much dilute the POC applicability. – Deduplicator Apr 09 '14 at 19:30
  • @Deduplicator, tried it with `int main()`, got the same result, edited the answer. – imreal Apr 09 '14 at 19:31
  • @Nick and just below that line it mentions this `int main(void) { /* ... */ }`. This is what is meant by no parameters in the standard. Empty parameter list means no information is provided about the number or type of parameters unlike in C++ where `int main();` and `int main(void);` are the same and the former is preferred. – ajay Apr 09 '14 at 19:31
  • @ajay I meant this part: `or in some other implementation-defined manner`. Implementation-defined is allowed by the standard. In fact, you could write a C standard compliant well formed program without the main. – imreal Apr 09 '14 at 19:34
  • Okay, surprisingly (at least for me) it's technically correct. But it causes stomach cramps inside me anyways. ;) – Andreas Wiese Apr 09 '14 at 19:35
  • @AndreasWiese, can't imagine what's so terrible about it, anyway, it surprises me why everybody focuses their misconception and deviates from the point. – imreal Apr 09 '14 at 19:40
  • 1
    @Nick: In my perception `foo()` vs. `foo(void)` always was K&R vs. C89 (resp. K&R, 2. ed). K&R syntax always seemed horribly horrible to me, but it's just a personal opinion. Apart from that the intention of my last comment was primarily conceding a point to you. – Andreas Wiese Apr 09 '14 at 19:47
  • It is not clear on my question, but by "C Calling Convention" I was mentioning "cdecl", not "stdcall". – André Puel Apr 09 '14 at 19:47
  • This doesn't make it more portable or even predictable either. »The `cdecl` (which stands for C declaration) is a calling convention that originates from the C programming language and is used by **many** C compilers for the **x86** architecture.« – Andreas Wiese Apr 09 '14 at 19:48
  • @AndréPuel I saw the question before you added `cdecl` which is platform (x86 mostly) dependent, not standard C. I guess the best answer is, it is safe only on specific platforms, and specific calling conventions. – imreal Apr 09 '14 at 19:54
0

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.

abelenky
  • 63,815
  • 23
  • 109
  • 159
  • If the parameters were pushed on the right order the called function would access the parameters shifted. But thank you for your answer. – André Puel Apr 09 '14 at 18:46
  • 1
    There is no "C Calling Convention" there are only platform conventions. – Zan Lynx Apr 09 '14 at 18:52
  • @ZanLynx: fair enough, but it is typically written as `cdecl`, and is a calling convention that [originated from the C language](http://en.wikipedia.org/wiki/X86_calling_conventions#cdecl). Describing it as the *"C Calling Convention"* is pretty fair. – abelenky Apr 09 '14 at 19:00
  • @abelenky: Pretty much only on x86 CPUs and some others, before CPUs got a reasonable number of registers. – Zan Lynx Apr 09 '14 at 19:01
  • 1
    I seriously doubt this answer. C does not know anything about a calling convention. – Andreas Wiese Apr 09 '14 at 19:09
  • The thing about calling convention is that it's only convention. You can always make up your own. And it may be defined like this (which is actually sane and used in some cases: "If you want to pass no more than X arguments, use first X CPU registers. If you want to pass more than X arguments, use stack". But of course, since again it's only convention, you may invent literally any rule you like, making passing more parameters than required impossible. – Krzysztof Adamski Apr 09 '14 at 19:44
  • 1
    why is anyone questioning this answer. this is the reason printf() worked in K&R C despite only "expecting" 1 argument – Steve Cox Apr 09 '14 at 19:56
  • I think people are confusing the *"C Calling Convention"* with the *"C Language"*. It is true that the C language does not specify how calling happens. But the [C Calling Convention](http://software.intel.com/sites/products/documentation/doclib/iss/2013/compiler/cpp-lin/GUID-011A435D-F8D0-46D7-B973-9B704CA5B54E.htm) is real (so says Intel), and behaves this way. – abelenky Apr 09 '14 at 20:00
  • 1
    But the times of K&R are gone and this is a good thing, since with C89 C got a way lot more portable. Either the question is too vague and it _might_ be applicable to expect certain behavior (we don't know since the author didn't tell anything about target platform, OS, compiler), or this answer is to vague since it only represents a vague subset of C implementations (what's bad for a question about _C_, isn't it?) – Andreas Wiese Apr 09 '14 at 20:01