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:
Lorenzo Cavallaro 2010-03-30 09:36:46 +00:00
parent 63e2d73d1b
commit 8dfc7699a6
8 changed files with 249 additions and 39 deletions

View file

@ -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 */

View file

@ -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 */

View file

@ -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 */

View file

@ -1,18 +1,85 @@
/* execl() - execute Author: Kees J. Bot
* 21 Jan 1994
/*-
* Copyright (c) 1991, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#define execl _execl
#define execve _execve
#include <unistd.h>
#include <minix/compiler.h>
#include <errno.h>
#include <stdarg.h>
#include <sys/types.h>
#include <stdlib.h>
#include <alloca.h>
#include <lib.h>
extern char * const **_penviron; /* The default environment. */
int execl(const char *path, const char *arg1, ...)
int
execl(const char *path, const char *arg, ...)
/* execl("/bin/sh", "sh", "-c", "example", (char *) 0); */
{
/* Assumption: The C-implementation for this machine pushes
#if FUNC_ARGS_ARRAY
/*
* Assumption: The C-implementation for this machine pushes
* function arguments downwards on the stack making a perfect
* argument array. Luckily this is almost always so.
*
* execl() - execute Author: Kees J. Bot, 21 Jan 1994
*/
return execve(path, (char * const *) &arg1, *_penviron);
return execve(path, (char * const *) &arg, *_penviron);
#else
/*
* Indeed, GCC (4.4.1) behaves similarly to ACK. Nonetheless, let's
* stay the safe side. LLVM (llvm-gcc) doesn't make the aforementioned
* assumption.
*/
va_list ap;
char **argv;
int i;
va_start(ap, arg);
for (i = 2; va_arg(ap, char *) != NULL; i++)
continue;
va_end(ap);
if ((argv = alloca(i * sizeof (char *))) == NULL) {
errno = ENOMEM;
return -1;
}
va_start(ap, arg);
argv[0] = __UNCONST(arg);
for (i = 1; (argv[i] = va_arg(ap, char *)) != NULL; i++)
continue;
va_end(ap);
return execve(path, (char * const *) argv, *_penviron);
#endif
}

View file

@ -1,25 +1,83 @@
/* execle() - execute with a custom environment Author: Kees J. Bot
* 21 Jan 1994
/*-
* Copyright (c) 1991, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#define nil 0
#define execle _execle
#define execve _execve
#include <unistd.h>
#include <minix/compiler.h>
#include <errno.h>
#include <stdarg.h>
#include <sys/types.h>
#include <stdlib.h>
#include <alloca.h>
#include <lib.h>
int execle(const char *path, const char *arg1, ...)
/* execle("/bin/sh", "sh", "-c", "example", (char *) 0, my_env_array); */
{
char * const * envp;
va_list ap;
char * const * envp;
va_list ap;
#if FUNC_ARGS_ARRAY
/*
* execle() - execute with a custom environment
* Author: Kees J. Bot, 21 Jan 1994
*/
va_start(ap, arg1);
/* Find the end of the argument array. */
if (arg1 != nil) while (va_arg(ap, const char *) != nil) {}
if (arg1 != NULL) while (va_arg(ap, const char *) != NULL) {}
envp = va_arg(ap, char * const *);
va_end(ap);
return execve(path, (char * const *) &arg1, envp);
#else
char **argv;
int i;
va_start(ap, arg1);
for (i = 2; va_arg(ap, char *) != NULL; i++)
continue;
va_end(ap);
if ((argv = alloca(i * sizeof (char *))) == NULL) {
errno = ENOMEM;
return -1;
}
va_start(ap, arg1);
argv[0] = __UNCONST(arg1);
for (i = 1; (argv[i] = va_arg(ap, char *)) != NULL; i++)
continue;
envp = va_arg(ap, char **);
va_end(ap);
return execve(path, (char * const *)argv, envp);
#endif
}

View file

@ -1,12 +1,73 @@
/* execlp() - execute with PATH search Author: Kees J. Bot
* 22 Jan 1994
/*-
* Copyright (c) 1991, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#define execlp _execlp
#define execvp _execvp
#include <unistd.h>
#include <minix/compiler.h>
#include <errno.h>
#include <stdarg.h>
#include <sys/types.h>
#include <stdlib.h>
#include <alloca.h>
#include <lib.h>
int execlp(const char *file, const char *arg1, ...)
/* execlp("sh", "sh", "-c", "example", (char *) 0); */
{
#if FUNC_ARGS_ARRAY
/*
* execlp() - execute with PATH search
* Author: Kees J. Bot, 22 Jan 1994
*/
return execvp(file, (char * const *) &arg1);
#else
va_list ap;
char **argv;
int i;
va_start(ap, arg1);
for (i = 2; va_arg(ap, char *) != NULL; i++)
continue;
va_end(ap);
if ((argv = alloca(i * sizeof (char *))) == NULL) {
errno = ENOMEM;
return -1;
}
va_start(ap, arg1);
argv[0] = __UNCONST(arg1);
for (i = 1; (argv[i] = va_arg(ap, char *)) != NULL; i++)
continue;
va_end(ap);
return execvp(file, (char * const *)argv);
#endif
}

View file

@ -4,7 +4,6 @@
#define _MINIX_SOURCE
#define nil 0
#define execve _execve
#define sbrk _sbrk
#include <lib.h>
@ -37,7 +36,7 @@ int execve(const char *path, char * const *argv, char * const *envp)
string_off= 0; /* Offset to start of the strings. */
argc= 0; /* Argument count. */
for (ap= argv; *ap != nil; ap++) {
for (ap= argv; *ap != NULL; ap++) {
n = sizeof(*ap) + strlen(*ap) + 1;
frame_size+= n;
if (frame_size < n) ov= 1;
@ -45,7 +44,7 @@ int execve(const char *path, char * const *argv, char * const *envp)
argc++;
}
for (ep= envp; *ep != nil; ep++) {
for (ep= envp; *ep != NULL; ep++) {
n = sizeof(*ep) + strlen(*ep) + 1;
frame_size+= n;
if (frame_size < n) ov= 1;
@ -77,22 +76,22 @@ int execve(const char *path, char * const *argv, char * const *envp)
sp = frame + string_off;
/* Load the argument vector and strings. */
for (ap= argv; *ap != nil; ap++) {
for (ap= argv; *ap != NULL; ap++) {
*vp++= (char *) (sp - frame);
n= strlen(*ap) + 1;
memcpy(sp, *ap, n);
sp+= n;
}
*vp++= nil;
*vp++= NULL;
/* Load the environment vector and strings. */
for (ep= envp; *ep != nil; ep++) {
for (ep= envp; *ep != NULL; ep++) {
*vp++= (char *) (sp - frame);
n= strlen(*ep) + 1;
memcpy(sp, *ep, n);
sp+= n;
}
*vp++= nil;
*vp++= NULL;
/* Padding. */
while (sp < frame + frame_size) *sp++= 0;

View file

@ -5,7 +5,6 @@
#define _MINIX_SOURCE
#define nil 0
#define execve _execve
#define execvp _execvp
#define sbrk _sbrk
@ -15,6 +14,7 @@
#include <unistd.h>
#include <errno.h>
#include <sys/stat.h>
#include <lib.h>
extern char * const **_penviron; /* The default environment. */
@ -33,7 +33,7 @@ int execvp(const char *file, char * const *argv)
size_t full_size;
int err= ENOENT; /* Error return on failure. */
if (strchr(file, '/') != nil || (path= getenv("PATH")) == nil)
if (strchr(file, '/') != NULL || (path= getenv("PATH")) == NULL)
path= "";
/* Compute the maximum length the full name may have, and align. */