Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to implement stdarg in C

Tags:

c

header-files

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.

like image 782
sadljkfhalskdjfh Avatar asked Aug 29 '15 23:08

sadljkfhalskdjfh


People also ask

How do you use Stdarg H?

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.

What is Stdarg H header file used?

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 .

What is Va_list in C?

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.


4 Answers

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 typedefs 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

like image 144
icktoofay Avatar answered Oct 19 '22 19:10

icktoofay


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

like image 35
R.. GitHub STOP HELPING ICE Avatar answered Oct 19 '22 19:10

R.. GitHub STOP HELPING ICE


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 Xs.

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])
like image 2
luser droog Avatar answered Oct 19 '22 20:10

luser droog


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.

like image 2
Manos Nikolaidis Avatar answered Oct 19 '22 19:10

Manos Nikolaidis