I wrote a printf myselef that use va_list/va_arg/va_start/va_end/va_arg.
typedef char *va_list;
#define _AUPBND (sizeof (acpi_native_int) - 1)
#define _ADNBND (sizeof (acpi_native_int) - 1)
#define _bnd(X, bnd) (((sizeof (X)) + (bnd)) & (~(bnd)))
#define va_arg(ap, T) (*(T *)(((ap) += (_bnd (T, _AUPBND))) - (_bnd (T,_ADNBND))))
#define va_end(ap) (void) 0
#define va_start(ap, A) (void) ((ap) = (((char *) &(A)) + (_bnd (A,_AUPBND))))
At first,I copy these macros from linux kernel and the printf can print 32-bit integer correct but cannot print 64-bit integer and print double/float may fail or collapse.Then I check the code and I guess the va_* may have errors,so I use __builtin_va_* instead of kernel's va_*.
typedef __builtin_va_list va_list;
#define va_start(v,l) __builtin_va_start(v,l)
#define va_end(v) __builtin_va_end(v)
#define va_arg(v,l) __builtin_va_arg(v,l)
But gcc prompt "undefined reference to `abort'",so I write a empty abort() and myprintf works corretly. My questions are:
va_list/va_arg/va_start/va_end/va_arg
can not used for printf
64-bit integer and double/float?__builtin_va_start/__builtin_va_arg/__builtin_va_end/__builtin_va_list
, why gcc prompt "undefined reference to abort'"? But I can not find the definition of
__builtin_va_*`, where're their definition?Don't cut and paste things from the Linux headers. Instead, put this at the top of your source file:
#include <stdarg.h>
This will give you everything you need in order to use va_list
and va_arg
. However, it won't pull in printf
or any of the standard I/O stuff (which lives in <stdio.h>
).
gcc's __builtin_va_arg()
apparently will call abort()
(at least on some platforms or situations) if it's invoked with a type argument cannot have been passed in the ...
part of the argument list for a function call.
For example, due to promotions a char
or float
passed as such an argument will have been promoted to int
or double
. Accessing those arguments as va_arg(ap,char)
or va_arg(ap,float)
is undefined behavior and gcc may call abort()
in that situation - or it may do something else (my MinGW compiler will execute an invalid instruction to cause a crash).
You might see something like this when compiled:
In file included from D:\temp\test.c:2:0:
D:\temp\test.c: In function 'foo':
D:\temp\test.c:12:16: warning: 'char' is promoted to 'int' when passed through '...' [enabled by default]
c = va_arg(ap,char);
^
D:\temp\test.c:12:16: note: (so you should pass 'int' not 'char' to 'va_arg')
D:\temp\test.c:12:16: note: if this code is reached, the program will abort
The 'definition' of __builtin_va_*
is compiled into the compiler (that's why 'builtin' is part of the name).
As far as the Linux macros for varargs access: while the definitions you took from a linux kernel header do exist in include/acpi/platform/acenv.h, if you look carefully at the conditional compilation in effect, you'll see that those macros aren't used when building the linux kernel. I'm not exactly sure when those macros are in effect, but they won't work with x64/x86-64/amd64 builds because the ABI on that platform isn't entirely stack-based. See section 3.5.6 of the "System V Application Binary Interface - AMD64 Architecture Processor Supplement" for details.
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