Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C calling convention: who cleans the stack in variadic functions vs normal functions?

There are some calling conventions (e.g pascal, stdcall) but as far as I am concerned, C does use cdecl (C-declared). Each of these conventions are slightly different in the way the caller loads the parameters onto the stack, respectively which (caller / callee) does the cleanup.

Talking about the cleanup, here is my question. I do not understand: are there three different things?

  1. stack clean
  2. moving the pointer back to the penultimate stack frame
  3. stack restoration

Or how should I see them?

Also, the target of this question is basically how could variadic function works in calling conventions like Pascal or stdcall where the callee should clear / clean / restore (I don't know which operation) the stack - but he doesn't know how many parameters it will receive.

EDIT

Why is it so important the order in which parameters are pushed on to the stack? You still have the first parameter (stable parameter not from ellipsis) which gives you the information about -for example- the number of variable arguments. And there is also the "guardian" which can be added into ellipsis punctuator and can be used as a marker for the variable part's end independent on the calling convention. In this link why both caller and callee should restore values of those register if they both save their state before messing them up? Shouldn't only one of them (e.g caller) save them on the stack before calling the function and that's all ? Also, on the same link

"So, the stack pointer ESP might go up and down, but the EBP register remains fixed. This is convenient because it means we can always refer to the first argument as [EBP + 8] regardless of how much pushing and popping is done in the function."

The pushed variables and the local variables are consecutive in memory. Where is the advantage of referring them using the EBP ? They will never have some dynamic offset between them, even if the stack changes in size.

One of the materials I've read is this site (only the beginning) for a better understanding on what is exactly the stack frame. Then I went on yt and found these stack overview and call stack tutorials but they somehow missed the part I needed. What does exactly happends when you call the function (I don't understand the instruction "call address" followed by the next instruction a push value on to the stack that means the return value). Who controls what the return address will be ? The caller? the callee? When the callee returns, the program contiunes by executing an instruction which is a reading operation from a register or what ?

like image 279
Cătălina Sîrbu Avatar asked Nov 02 '20 07:11

Cătălina Sîrbu


People also ask

When calling a function using the x86 64 C language calling convention the stack pointer RSP must be aligned in what way?

As mentioned above, before executing call the stack pointer must be aligned to a multiple of 16 + 8, so if you are calling a function which has any stack-based arguments, you should add up the total size of these arguments and then, if necessary, adjust the rsp (by subtraction) so that it is aligned.

Which are function calling conventions in C?

There are three major calling conventions that are used with the C language on 32-bit x86 processors: STDCALL, CDECL, and FASTCALL. In addition, there is another calling convention typically used with C++: THISCALL.

What is the difference between Stdcall and cdecl?

In CDECL arguments are pushed onto the stack in revers order, the caller clears the stack and result is returned via processor registry (later I will call it "register A"). In STDCALL there is one difference, the caller doeasn't clear the stack, the calle do. You are asking which one is faster.

Is RDI caller saved or callee saved?

The registers RAX, RCX, RDX, R8, R9, R10, R11 are considered volatile (caller-saved). The registers RBX, RBP, RDI, RSI, RSP, R12, R13, R14, and R15 are considered nonvolatile (callee-saved).


1 Answers

as far as I am concerned, C does use cdecl

Its name notwithstanding, the cdecl convention is not universal for C code, not even on the x86 architecture. It has the advantage of being simple to define and implement, but it makes no use of CPU registers for argument passing, which is more efficient. That makes a difference even on register-starved x86, but it makes a lot more difference on architectures with more available registers, such as x86_64.

Talking about the cleanup, here is my question. I do not understand: are there three different things?

  1. stack clean
  2. moving the pointer back to the penultimate stack frame
  3. stack restoration

Or how should I see them?

I would be inclined to interpret (1) and (3) as different ways of saying the same thing, but it is conceivable that someone would draw distinctions between them. (3) and related wording is what I encounter most frequently. (2) is not necessarily the same thing, because there may be two relevant stack parameters to be restored: the base of the stack frame (see below), and the top of the stack. The stack frame base is important in the event that the stack frame contains more information than argument and local variable values, such as the base of the previous stack frame.

Also, the target of this question is basically how could variadic function works in calling conventions like Pascal or stdcall where the callee should clear / clean / restore (I don't know which operation) the stack - but he doesn't know how many parameters it will receive.

The stack is not necessarily the whole picture.

The callee cannot restore the stack if it does not know how to find the top of its caller's stack, and, if necessary, the base of its caller's stack frame. But in practice, this is usually hardware assisted.

Taking x86 (for which cdecl was designed) as an example, the CPU has registers for both the stack (frame) base and the current stack pointer. The caller's stack base is stored on the stack at a known offset (0) from the callee's stack base. Regardless of the number of arguments, the callee restores the stack by moving the top of the stack to its own stack base, and popping the value there to obtain the caller's stack base.

It is conceivable, however, that there is a calling convention in use somewhere that affords no way to restore the stack to a chosen previous state other than to pop elements one at a time, that does not explicitly convey the number of arguments to the called function, and that requires the callee to restore the caller's stack. Such a calling convention would not support variadic functions.

Why is it so important the order in which parameters are pushed on to the stack?

The order is not important in any general sense, but it is essential for caller and callee, which may be compiled separately, to agree about it. Otherwise, the callee cannot match the passed values with the parameters they are intended for. Thus, to whatever extent a calling convention relies on the stack, it must specify precisely which arguments are passed there, and in which order.

Regarding stack frames: this is more material that is not specified by C and that varies, at least to some extent. Conceptually, though, the stack frame of a function call is the portion of the stack that provides execution context for that call. It typically supplies storage for local variables, and it may contain additional information, such as a return address and / or the value of the caller's stack frame pointer. It might also contain other per-function-call information appropriate for the execution environment. Details are part of the calling convention in use.

like image 85
John Bollinger Avatar answered Oct 23 '22 04:10

John Bollinger