How do vararg functions detect the number of arguments in machine codes?

How do various variational functions, such as printf, detect the number of arguments they received?

The number of arguments is obviously not passed as a (hidden) parameter (see the printf call in the asm example here ).

What trick?

+11
source share
4 answers

The trick is that you tell them something different. For printf you need to specify a format string that even contains type information (which may be incorrect, though). The way this information is provided is largely user dependent and often error prone.

As for calling conventions: Typically, arguments are pushed onto the stack from left to right and then finally the return address. The calling procedure clears the stack. Therefore, there is no technical need for the called routine to know the number of parameters.

EDIT: in C ++ 0x there is a safe way (even typafe!) To call variable functions!

+12
source

Implicitly, from a format string. Note that stdarg.h does not contain macros to get the total number of "variables" of arguments passed. This is also one of the reasons the C calling convention requires the caller to clear the stack, although this increases the size of the code.

+9
source

This is why arguments are preempted in reverse order in a C call, for example:

If you call:

 printf("%s %s", foo, bar); 

The stack ends like this:

  ... +-------------------+ | bar | +-------------------+ | foo | +-------------------+ | "%s %s" | +-------------------+ | return address | +-------------------+ | old frame pointer | <- frame pointer +-------------------+ ... 

The arguments are indirectly related to its offset from the frame pointer (the frame pointer can be omitted by intelligent compilers that can calculate things from the stack pointer). The first argument is always at a well-known address in this scheme; the function accesses the set of arguments that its first arguments talk about.

Try the following:

 printf("%x %x %x %x %x %x\n"); 

This will delete part of the stack.

+8
source
  • The AMD64 System V ABI (Linux, Mac OS X) does indeed pass numeric vector variables (SSE / AVX) to al (low byte RAX), unlike any standard IA-32 calling convention. See also: Why is% eax nullified before calling printf?

    But only up to 8 (maximum number of registers to use). And IIRC, ABI allows al be more than the actual number of arguments XMM / YMM / ZMM, but it should not be less. So, as a rule, it does not always show the number of arguments FP; You cannot say how many are over 8 and al allowed to cross.

    This is only possible for performance reasons, to skip storing unnecessary vector registers in the "Register Preservation Area" mentioned in "3.5.7 Variable Argument Lists". For example, GCC creates code that tests al!=0 , and then pushes XMM0..7 onto the stack or nothing. (Or if a function somewhere uses VA_ARG with __m256 , then YMM0..7.)

  • At the C level, there are other methods besides parsing the format string, as mentioned by others. You also can:

    • pass sentinel (void *)0 to indicate the last argument, like execl .

      You will want to use the sentinel function attribute to help GCC force it at compile time: C warning There is no guard in the function call

    • pass it as an optional integer argument with varargs number

    • use the format function attribute to help GCC use format strings of known types, such as printf or strftime

Related: How are variable arguments implemented in gcc?

+5
source

All Articles