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()<
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])
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).
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.