For curiosity, I'm looking to write minimal replacements for some of the functions in the standard C library. So far, I have finished printf()
, strlen()
, strcpy()
, memcpy()
, memset()
, etc... but when I try to use the printf function, I don't know how to implement stdarg.h
! What is a way that I could do that?
Does it use macros or actual functions?
I am using gcc
OR clang
on 32-bit x86, if it helps to make this any easier to answer.
The stdarg. h header defines a variable type va_list and three macros which can be used to get the arguments in a function when the number of arguments are not known i.e. variable number of arguments. A function of variable arguments is defined with the ellipsis (,...) at the end of the parameter list.
The stdarg. h header file defines macros used to access arguments in functions with variable-length argument lists. The stdarg. h header file also defines the structure va_list .
va_list is a complete object type suitable for holding the information needed by the macros va_start, va_copy, va_arg, and va_end. If a va_list instance is created, passed to another function, and used via va_arg in that function, then any subsequent use in the calling function should be preceded by a call to va_end.
On 32-bit x86 with the cdecl calling convention, parameters are passed on the stack:
^ higher addresses (lower on the stack)
|
| caller local variables
| ...
| argument 3
| argument 2
| argument 1
| return address
| saved EBP (usually)
| callee local variables
|
v lower addresses (higher on the stack)
You can implement va_list
as a pointer. va_start
can take the address of the argument passed to it and add the size of that argument to move to the next argument. va_arg
can access the pointer and bump it to the next argument. va_copy
can just copy the pointer value. va_end
doesn’t need to do anything.
If, on the other hand, you’re not using cdecl (maybe you’re using fastcall), you’re not 32-bit, you’re not x86, this won’t work; you might need to muck with registers rather than just pointer values. And even still it’s not guaranteed to work, since you’re relying on undefined behavior; as as example of only one potential problem, inlining could ruin everything. And that’s why the header file just typedef
s it to a compiler built-in—implementing this in C is hopeless, you need compiler support. And don’t even get me started on implementing setjmp
and longjmp
…
There is no way to implement stdarg.h
macros in C; you need compiler builtins like __builtin_va_arg
, etc. which GCC and compatible compilers provide, or the equivalent for your compiler.
Even if you know the argument passing convention for the particular target you're working with (like i386 in icktoofay's answer), there is no way in C to get access to this memory. Simply performing pointer arithmetic on the address passed to va_start
is not valid; it results in undefined behavior. But even if C did allow that arithmetic, there's no guarantee that the address of the last named argument actually corresponds to the place it was passed on the stack as part of the calling convention; the compiler could have chosen to move it to a different location in its stack frame (perhaps for the sake of obtaining additional alignment or data locality).
There's an implementation in the CALC source code posted to comp.sources.unix in 1992.
This is from a shar archive, so ignore the X
s.
X * Copyright (c) 1992 David I. Bell
X * Permission is granted to use, distribute, or modify this source,
X * provided that this copyright notice remains intact.
X/*
X * SIMULATE_STDARG
X *
X * WARNING: This type of stdarg makes assumptions about the stack
X * that may not be true on your system. You may want to
X * define STDARG (if using ANSI C) or VARARGS.
X */
X
Xtypedef char *va_list;
X#define va_start(ap,parmn) (void)((ap) = (char*)(&(parmn) + 1))
X#define va_end(ap) (void)((ap) = 0)
X#define va_arg(ap, type) \
X (((type*)((ap) = ((ap) + sizeof(type))))[-1])
You can see examples of how to implement the va macros here. This header is used in VC++ and there are different implementations for each processor architecture. The macros don't seem to be specific to the Microsoft compiler. In both GCC and Clang the va macros refer to compiler built-in functions.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With