3

In the cdecl calling convention, it states that:

Arguments are pushed in the reverse order (right to left)

My question is: in the reverse order relative to what non-reverse order? Is it relative to the function documentation? So for example if I have the following function documentation:

void __cdecl foo (int arg1, int arg2, int arg3)

So should I just look at the order of parameters in the function documentation and reverse the augments pushed onto the stack?

John
  • 1,049
  • 1
  • 14
  • 34
  • 1
    Reverse relative to the order in which they are written in your function declaration. – Michael Jan 16 '15 at 07:58
  • @Michael But there are no function declarations in Assembly! – John Jan 16 '15 at 08:02
  • There are with certain assemblers. And even if you don't explicitly write any function declarations you can still imagine what that declaration would look like if you _had_ written one. – Michael Jan 16 '15 at 08:14
  • @Michael Yes this is what I meant, If someone have written a function in Assembly using a label, there will be no function declaration provided. But he'll probably provide a function documentation that contains what is the order of parameters. So I just reverse this order when pushing the arguments onto the stack. – John Jan 16 '15 at 08:18
  • The point of a calling convention is that you get interoperability between code written by different people, and in different programming languages. You shouldn't really think of these conventions as being defined in terms of assembly language constructs. – Michael Jan 16 '15 at 08:20
  • I find it confusing that they define the convention using `push`. They should have said how the arguments must be laid out on the stack, and leave it to the programmer to achieve that goal. Especially since the order is only reversed due to the `push`, otherwise it's the normal expected order on the stack. – Jester Jan 16 '15 at 10:33
  • @Jester: When you say you are going to "pass arguments in a stack", defining the order in terms of stack operations ("push") makes perfect sense. – Ira Baxter Jan 17 '15 at 11:30

2 Answers2

5

The first function argument is pushed last, in terms of asm instruction order and execution order. However since stacks grows downward, the fist argument has the lowest address.

This scheme means the first argument (and second etc) can always be accessed as a constant offset to the current stack pointer-it is found just after the return address.

And yes, you should do exactly what you suggest at the end of your question, although a common technique is to reserve space on the stack at the head of your function that is large enough to support all your stack variables plus any required function call arguments (but not the return address) and simply do stack relative stores to set up the arguments.

Note that in many ISA ABIs the first few arguments are passed in registers, not on the stack, but stack space is reserved anyway, for possible storage across nested function calls.

Alex Brown
  • 41,819
  • 10
  • 94
  • 108
5

If your function call was:

  foo( arg1, arg2, ... argN )

then "argument order" would be left to right (arg1 to argN). Function calling code would be:

  push  arg1
  push  arg2
  ...
  push  argN
  call  foo

"Reverse order (right to left)" would be argN to arg1:

  push  argN
  ...
  push  arg1
  call  foo

The args of course may not be single DWORDS, and thus the push operations might not be single instructions. The compiler might also be very clever, and preallocate the stack space for the args, and then accomplish filling the arguments via various MOV instructions to offsets in the allocated stack space, possibly not in any specific order. So interpret the "pushes arguments" according to the idea "as if" pushes had been done.

Ira Baxter
  • 93,541
  • 22
  • 172
  • 341
  • Just to be clear, **`cdecl` and `stdcall`**, and most major x86 calling conventions like i386 System V, **push the left-most arg last (right to left)**, so it's at the lowest address where the callee can find it without knowing how many args the function took. (So they're usable for calling printf or other variadic functions, unless there are other reasons why not like being callee-pops: [Why can't stdcall handle varying amounts of arguments?](https://stackoverflow.com/a/73055218)) – Peter Cordes Aug 06 '22 at 09:54