How do vararg functions find out the number of arguments in machine code?

不问归期 提交于 2019-11-28 09:20:33

The trick is that you tell them somehow else. For printf you have to supply a format string which even contains type information (which might be incorrect though). The way to supply this information is mainly user-contract and often error-prone.

As for calling conventions: Usually the arguments are pushed onto the stack from left to right and then the backjump address at last. The calling routine clears the stack. So 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 typesafe!) to call variadic functions!

Implicitly, from the format string. Note that stdarg.h doesn't contain any macros to retrieve the total "variable" number of arguments passed. This is also one of the reasons the C calling convention requires the caller to clean the stack, even though this increases code size.

This is the reason why arguments are pushed on reverse order on the C calling convention, e.g:

If you call:

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

The stack ends up like:

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

Arguments are accesed indirectly using its offset from the frame pointer (the frame pointer can be omitted by smart compilers that know how to calculate things from the stack pointer). The first argument is always at a well-known address in this scheme, the function accesses as many arguments as its first arguments tell it to.

Try the following:

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

This will dump part of the stack.

Ciro Santilli 新疆改造中心996ICU六四事件
  • The AMD64 System V ABI (Linux, Mac OS X) does pass the number vector (SEE / AVX) varargs in rax, unlike IA-32. See also: Why is %eax zeroed before a call to printf?

    TODO why is this required? I think it is only for performance reasons, to avoid saving unneeded SSE registers to the "Register Save Area" mentioned in "3.5.7 Variable Argument Lists".

  • On the C level, there are also other techniques besides parsing the format string as mentioned by others. You could also:

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

      You will want to use the sentinel function attribute to help GCC enforce that at compile time: C warning Missing sentinel in function call

    • pass it as an extra integer argument with the number of varargs

    • use the format function attribute to help GCC enforce format strings of known types like printf or strftime

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!