cdecl calling convention requires to push arguments on the stack in a
reverse order to easily support variadic arguments. Thus, instead of using the proper stdarg.h macros (that nowadays are compiler-dependent), it may be tempting to directly take the address of the last argument and considering it as the start of an array. This is a shortcut that avoid looping to get all the arguments as the CPU already pushed them on the stack before the call to the function. Unfortunately, such an assumption is strictly compiler-dependent and compilers are free to move the last argument on the stack, as a local variable, and return the address of the location where the argument was stored, if asked for. This will break things as the rest of the array's argument are stored elsewhere (typically, a couple of words above the location where the argument was stored). This patch fixes the issue by allowing ACK to take the shortcut and enabling gcc/llvm-gcc to follow the right way.
This commit is contained in:
@@ -6,4 +6,15 @@
|
||||
/* ACK expects the caller to pop the hidden pointer on struct return. */
|
||||
#define BYTES_TO_POP_ON_STRUCT_RETURN
|
||||
|
||||
/*
|
||||
* ACK doesn't move the last argument of a variadic arguments function
|
||||
* anywhere, once it's on the stack as a function parameter. Thus, it is
|
||||
* possible to make strong assumption on the immutability of the stack
|
||||
* layout and use the address of that argument as the start of an array.
|
||||
*
|
||||
* If you're curious, just look at lib/libc/posix/_execl*.c ;-)
|
||||
*/
|
||||
|
||||
#define FUNC_ARGS_ARRAY 1
|
||||
|
||||
#endif /* _MINIX_COMPILER_ACK_H */
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#define _MINIX_COMPILER_H
|
||||
|
||||
/*===========================================================================*
|
||||
* Compiler overrides *
|
||||
* Compiler overrides *
|
||||
*===========================================================================*/
|
||||
/* ACK */
|
||||
#ifdef __ACK__
|
||||
@@ -12,7 +12,7 @@
|
||||
#endif
|
||||
|
||||
/*===========================================================================*
|
||||
* Default values *
|
||||
* Default values *
|
||||
*===========================================================================*/
|
||||
/*
|
||||
* cdecl calling convention expects the callee to pop the hidden pointer on
|
||||
@@ -22,4 +22,28 @@
|
||||
#define BYTES_TO_POP_ON_STRUCT_RETURN $4
|
||||
#endif
|
||||
|
||||
/*
|
||||
* cdecl calling convention requires to push arguments on the stack in a
|
||||
* reverse order to easily support variadic arguments. Thus, instead of
|
||||
* using the proper stdarg.h macros (that nowadays are
|
||||
* compiler-dependant), it may be tempting to directly take the address of
|
||||
* the last argument and considering it as the start of an array. This is
|
||||
* a shortcut that avoid looping to get all the arguments as the CPU
|
||||
* already pushed them on the stack before the call to the function.
|
||||
*
|
||||
* Unfortunately, such an assumption is strictly compiler-dependant and
|
||||
* compilers are free to move the last argument on the stack, as a local
|
||||
* variable, and return the address of the location where the argument was
|
||||
* stored, if asked for. This will break things as the rest of the array's
|
||||
* argument are stored elsewhere (typically, a couple of words above the
|
||||
* location where the argument was stored).
|
||||
*
|
||||
* Conclusion: if unsure on what the compiler may do, do not make any
|
||||
* assumption and use the right (typically compiler-dependant) macros.
|
||||
*/
|
||||
|
||||
#ifndef FUNC_ARGS_ARRAY
|
||||
#define FUNC_ARGS_ARRAY 0
|
||||
#endif
|
||||
|
||||
#endif /* _MINIX_COMPILER_H */
|
||||
|
||||
@@ -53,20 +53,10 @@ void va_end (va_list); /* Defined in gnulib */
|
||||
|
||||
#else /* __GNUC__ >= 2 */
|
||||
|
||||
#ifndef __sparc__
|
||||
#define va_start(AP, LASTARG) \
|
||||
(AP = ((char *) __builtin_next_arg ()))
|
||||
#else
|
||||
#define va_start(AP, LASTARG) \
|
||||
(__builtin_saveregs (), AP = ((char *) __builtin_next_arg ()))
|
||||
#endif
|
||||
|
||||
void va_end (va_list); /* Defined in libgcc.a */
|
||||
#define va_end(AP)
|
||||
|
||||
#define va_arg(AP, TYPE) \
|
||||
(AP = ((char *) (AP)) += __va_rounded_size (TYPE), \
|
||||
*((TYPE *) ((char *) (AP) - __va_rounded_size (TYPE))))
|
||||
#define va_start(ap, last) __builtin_va_start((ap), (last))
|
||||
#define va_arg(ap, type) __builtin_va_arg((ap), type)
|
||||
#define va_end(ap) __builtin_va_end(ap)
|
||||
#define va_copy(dest, src) __builtin_va_copy((dest), (src))
|
||||
|
||||
#endif /* __GNUC__ >= 2 */
|
||||
|
||||
|
||||
Reference in New Issue
Block a user