This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Make collect2 be buildable when host=i386-mingw, target=somethingelse


If you try to build a cross compiler from i386-mingw to just about
anything, you'll trip over a fair amount of unrepentant Unixisms in
collect2.c.  These are not a problem when host=target=mingwin, because
they are #ifdefed out, but that's not the case when the target is,
say, powerpc-eabisim.

This patch is a first draft attempt to fix that.  It compiles using my
i386-linux -> i386-mingw cross compiler.  I have not made the
slightest attempt to test its execution, although I will tomorrow
afternoon.  Also, I do not know what I am doing when it comes to
Windows system programming - the code you see here is the result of me
poring over the online API reference at msdn.microsoft.com all
afternoon.  (Particular areas of concern are the command line quoting,
executable search, and what collect2's install_handlers() does.)

I've introduced three new libiberty APIs, pexec() pmkpipe() and
pexec_set_program_name() -- the latter is slated to go away in a
global cleanup Real Soon, but right now I need something that works.
I'm happy enough with the other two.

Minor Unix portability notes -- dup2() was in V7, therefore there is
no point avoiding it or bothering to autoconf-probe for it, in code
destined for use on Unix (such as pex-unix.c)  I have done so.  There
is now nothing using HAVE_DUP2 that is defined by gcc's configure
script; I would remove the test but the machine this patch was
developed on doesn't have the right version of autoconf installed.
fcntl() has been around since 4.3BSD, which is old enough that I
propose not to bother checking for its presence unless and until
someone complains.

Feedback would be appreciated.

zw

include:
        * libiberty.h: Prototype new functions pmkpipe, pexec, and
        pexec_set_program_name.
libiberty:
        * pex-common.h: Adjust install_error_msg and comments.
        * pex-unix.c, pex-win32.c: Implement pmkpipe, pexec, and
        pexec_set_program_name.  Reimplement pexecute in terms of
        pmkpipe and pexec.  Adjust pwait as necessary.
gcc:
        * collect2.c (clean_up_temp_files, install_handlers, disable_break,
        enable_break):  New functions to abstract out differences
        between Unix and Windows.
        (handler): Create Windows version of this routine.
        (unhandled_filter): New (Windows only)
        (dup2): Delete - no longer used.
        (main): Use install_handlers.  Call pexec_set_program_name.
        (collect_execute, scan_prog_file, scan_libraries): Use
        disable_break/enable_break and new pexec/pmkpipe API instead
        of raw Unix system calls.

        * gcc.c, protoize.c: Call pexec_set_program_name from main.

===================================================================
Index: gcc/collect2.c
--- gcc/collect2.c	20 Jan 2003 20:41:55 -0000	1.145
+++ gcc/collect2.c	29 Jan 2003 07:06:05 -0000
@@ -30,23 +30,16 @@ Software Foundation, 59 Temple Place - S
 #include "system.h"
 #include "coretypes.h"
 #include "tm.h"
-#include <signal.h>
-#if ! defined( SIGCHLD ) && defined( SIGCLD )
-#  define SIGCHLD SIGCLD
-#endif
 
-#ifdef vfork /* Autoconf may define this to fork for us.  */
-# define VFORK_STRING "fork"
+#ifdef _WIN32
+# define WIN32_LEAN_AND_MEAN
+# include <windows.h>
 #else
-# define VFORK_STRING "vfork"
-#endif
-#ifdef HAVE_VFORK_H
-#include <vfork.h>
-#endif
-#ifdef VMS
-#define vfork() (decc$$alloc_vfork_blocks() >= 0 ? \
-               lib$get_current_invo_context(decc$$get_vfork_jmpbuf()) : -1)
-#endif /* VMS */
+# include <signal.h>
+# if ! defined( SIGCHLD ) && defined( SIGCLD )
+#  define SIGCHLD SIGCLD
+# endif
+#endif 
 
 #ifndef LIBRARY_PATH_ENV
 #define LIBRARY_PATH_ENV "LIBRARY_PATH"
@@ -264,7 +257,17 @@ static struct path_prefix *libpaths[3] =
 static const char *const libexts[3] = {"a", "so", NULL};  /* possible library extensions */
 #endif
 
+static void clean_up_temp_files	PARAMS ((void));
+#ifdef _WIN32
+static BOOL WINAPI handler	PARAMS ((DWORD));
+static LONG unhanded_filter	PARAMS ((LPEXCEPTION_POINTERS));
+#else
 static void handler		PARAMS ((int));
+#endif
+static void install_handlers	PARAMS ((void));
+static void disable_break	PARAMS ((void));
+static void enable_break	PARAMS ((void));
+
 static int is_ctor_dtor		PARAMS ((const char *));
 static char *find_a_file	PARAMS ((struct path_prefix *, const char *));
 static void add_prefix		PARAMS ((struct path_prefix *, const char *));
@@ -305,29 +308,6 @@ static char *resolve_lib_name	PARAMS ((c
 #endif
 static char *extract_string	PARAMS ((const char **));
 
-#ifndef HAVE_DUP2
-static int dup2 PARAMS ((int, int));
-static int
-dup2 (oldfd, newfd)
-     int oldfd;
-     int newfd;
-{
-  int fdtmp[256];
-  int fdx = 0;
-  int fd;
- 
-  if (oldfd == newfd)
-    return oldfd;
-  close (newfd);
-  while ((fd = dup (oldfd)) != newfd && fd >= 0) /* good enough for low fd's */
-    fdtmp[fdx++] = fd;
-  while (fdx > 0)
-    close (fdtmp[--fdx]);
-
-  return fd;
-}
-#endif /* ! HAVE_DUP2 */
-
 /* Delete tempfiles and exit function.  */
 
 void
@@ -426,9 +406,9 @@ fancy_abort ()
   fatal ("internal error");
 }
 
+
 static void
-handler (signo)
-     int signo;
+clean_up_temp_files (void)
 {
   if (c_file != 0 && c_file[0])
     maybe_unlink (c_file);
@@ -443,11 +423,120 @@ handler (signo)
   if (export_file != 0 && export_file[0])
     maybe_unlink (export_file);
 #endif
+}  
+
+/* Signal handling needs to be drastically different between Windows
+   and Unix.  */
+#ifdef _WIN32
 
+static BOOL WINAPI
+handler (signo)
+     DWORD signo ATTRIBUTE_UNUSED;
+{
+  clean_up_temp_files ();
+  return FALSE; /* not handled - default handler will terminate process */
+}
+
+static LONG
+unhandled_filter (pointers)
+     LPEXCEPTION_POINTERS pointers ATTRIBUTE_UNUSED;
+{
+  clean_up_temp_files ();
+  return EXCEPTION_CONTINUE_SEARCH;   /* carry on and terminate process */
+}
+
+
+static void
+install_handlers ()
+{
+  SetConsoleCtrlHandler (handler, TRUE);
+  /* This is not really the right way to do this; in fact I'm not
+     sure it will work.  But it might.  */
+  SetUnhandledExceptionFilter (unhandled_filter);
+}
+
+static void
+disable_break ()
+{
+  /* This only works on Windows NT, and only disables CTRL-C;
+     CTRL-BREAK is not disabled (but does get caught by the handler).  */
+  if (!(GetVersion() & 0x80000000))
+    SetConsoleCtrlHandler (NULL, TRUE);
+}
+
+static void
+enable_break ()
+{
+  if (!(GetVersion() & 0x80000000))
+    SetConsoleCtrlHandler (NULL, FALSE);
+}
+
+#else /* not _WIN32 */
+
+static void
+handler (signo)
+     int signo;
+{
+  clean_up_temp_files ();
   signal (signo, SIG_DFL);
   kill (getpid (), signo);
 }
 
+static void
+install_handlers ()
+{
+#ifdef SIGQUIT
+  if (signal (SIGQUIT, SIG_IGN) != SIG_IGN)
+    signal (SIGQUIT, handler);
+#endif
+  if (signal (SIGINT, SIG_IGN) != SIG_IGN)
+    signal (SIGINT, handler);
+#ifdef SIGALRM
+  if (signal (SIGALRM, SIG_IGN) != SIG_IGN)
+    signal (SIGALRM, handler);
+#endif
+#ifdef SIGHUP
+  if (signal (SIGHUP, SIG_IGN) != SIG_IGN)
+    signal (SIGHUP, handler);
+#endif
+  if (signal (SIGSEGV, SIG_IGN) != SIG_IGN)
+    signal (SIGSEGV, handler);
+#ifdef SIGBUS
+  if (signal (SIGBUS, SIG_IGN) != SIG_IGN)
+    signal (SIGBUS, handler);
+#endif
+#ifdef SIGCHLD
+  /* We *MUST* set SIGCHLD to SIG_DFL so that the wait4() call will
+     receive the signal.  A different setting is inheritable */
+  signal (SIGCHLD, SIG_DFL);
+#endif
+}
+
+static void (*int_handler) PARAMS ((int));
+#ifdef SIGQUIT
+static void (*quit_handler) PARAMS ((int));
+#endif
+
+static void
+disable_break ()
+{
+  int_handler  = (void (*) PARAMS ((int))) signal (SIGINT,  SIG_IGN);
+#ifdef SIGQUIT
+  quit_handler = (void (*) PARAMS ((int))) signal (SIGQUIT, SIG_IGN);
+#endif
+}
+
+static void
+enable_break ()
+{
+  signal (SIGINT,  int_handler);
+#ifdef SIGQUIT
+  signal (SIGQUIT, quit_handler);
+#endif
+}
+
+#endif /* not _WIN32 */
+
 
 int
 file_exists (name)
@@ -865,6 +954,8 @@ main (argc, argv)
   int first_file;
   int num_c_args	= argc+9;
 
+  pexec_set_program_name (argv[0]);
+
   no_demangle = !! getenv ("COLLECT_NO_DEMANGLE");
 
   /* Suppress demangling by the real linker, which may be broken.  */
@@ -875,12 +966,6 @@ main (argc, argv)
   COLLECT2_HOST_INITIALIZATION;
 #endif
 
-#ifdef SIGCHLD
-  /* We *MUST* set SIGCHLD to SIG_DFL so that the wait4() call will
-     receive the signal.  A different setting is inheritable */
-  signal (SIGCHLD, SIG_DFL);
-#endif
-
   gcc_init_libintl ();
 
   /* Do not invoke xcalloc before this point, since locale needs to be
@@ -934,26 +1019,7 @@ main (argc, argv)
   if (argc < 2)
     fatal ("no arguments");
 
-#ifdef SIGQUIT
-  if (signal (SIGQUIT, SIG_IGN) != SIG_IGN)
-    signal (SIGQUIT, handler);
-#endif
-  if (signal (SIGINT, SIG_IGN) != SIG_IGN)
-    signal (SIGINT, handler);
-#ifdef SIGALRM
-  if (signal (SIGALRM, SIG_IGN) != SIG_IGN)
-    signal (SIGALRM, handler);
-#endif
-#ifdef SIGHUP
-  if (signal (SIGHUP, SIG_IGN) != SIG_IGN)
-    signal (SIGHUP, handler);
-#endif
-  if (signal (SIGSEGV, SIG_IGN) != SIG_IGN)
-    signal (SIGSEGV, handler);
-#ifdef SIGBUS
-  if (signal (SIGBUS, SIG_IGN) != SIG_IGN)
-    signal (SIGBUS, handler);
-#endif
+  install_handlers ();
 
   /* Extract COMPILER_PATH and PATH into our prefix list.  */
   prefix_from_env ("COMPILER_PATH", &cpath);
@@ -1539,11 +1605,7 @@ collect_execute (prog, argv, redir)
      char **argv;
      const char *redir;
 {
-  char *errmsg_fmt;
-  char *errmsg_arg;
   int redir_handle = -1;
-  int stdout_save = -1;
-  int stderr_save = -1;
 
   if (vflag || debug)
     {
@@ -1561,9 +1623,6 @@ collect_execute (prog, argv, redir)
       fprintf (stderr, "\n");
     }
 
-  fflush (stdout);
-  fflush (stderr);
-
   /* If we cannot find a program we need, complain error.  Do this here
      since we might not end up needing something that we could not find.  */
 
@@ -1571,40 +1630,12 @@ collect_execute (prog, argv, redir)
     fatal ("cannot find `%s'", prog);
 
   if (redir)
-    {
-      /* Open response file.  */
-      redir_handle = open (redir, O_WRONLY | O_TRUNC | O_CREAT);
-
-      /* Duplicate the stdout and stderr file handles
-	 so they can be restored later.  */
-      stdout_save = dup (STDOUT_FILENO);
-      if (stdout_save == -1)
-	fatal_perror ("redirecting stdout: %s", redir);
-      stderr_save = dup (STDERR_FILENO);
-      if (stderr_save == -1)
-	fatal_perror ("redirecting stdout: %s", redir);
-
-      /* Redirect stdout & stderr to our response file.  */
-      dup2 (redir_handle, STDOUT_FILENO);
-      dup2 (redir_handle, STDERR_FILENO);
-    }
-
-  pexecute_pid = pexecute (argv[0], argv, argv[0], NULL,
-			   &errmsg_fmt, &errmsg_arg,
-			   (PEXECUTE_FIRST | PEXECUTE_LAST | PEXECUTE_SEARCH));
-
-  if (redir)
-    {
-      /* Restore stdout and stderr to their previous settings.  */
-      dup2 (stdout_save, STDOUT_FILENO);
-      dup2 (stderr_save, STDERR_FILENO);
+    redir_handle = open (redir, O_WRONLY | O_TRUNC | O_CREAT);
 
-      /* Close response file.  */
-      close (redir_handle);
-    }
+  pexecute_pid = pexec (argv[0], argv, 1, -1, redir_handle, -1);
 
  if (pexecute_pid == -1)
-   fatal_perror (errmsg_fmt, errmsg_arg);
+   fatal_perror ("pexec");
 }
 
 static void
@@ -2066,11 +2097,8 @@ scan_prog_file (prog_name, which_pass)
      const char *prog_name;
      enum pass which_pass;
 {
-  void (*int_handler) PARAMS ((int));
-  void (*quit_handler) PARAMS ((int));
   char *real_nm_argv[4];
   const char **nm_argv = (const char **) real_nm_argv;
-  int pid;
   int argc = 0;
   int pipe_fd[2];
   char *p, buf[1024];
@@ -2090,7 +2118,7 @@ scan_prog_file (prog_name, which_pass)
   nm_argv[argc++] = prog_name;
   nm_argv[argc++] = (char *) 0;
 
-  if (pipe (pipe_fd) < 0)
+  if (pmkpipe (pipe_fd) < 0)
     fatal_perror ("pipe");
 
   inf = fdopen (pipe_fd[0], "r");
@@ -2109,38 +2137,14 @@ scan_prog_file (prog_name, which_pass)
       fprintf (stderr, "\n");
     }
 
-  fflush (stdout);
-  fflush (stderr);
-
   /* Spawn child nm on pipe */
-  pid = vfork ();
-  if (pid == -1)
-    fatal_perror (VFORK_STRING);
-
-  if (pid == 0)			/* child context */
-    {
-      /* setup stdout */
-      if (dup2 (pipe_fd[1], 1) < 0)
-	fatal_perror ("dup2 %d 1", pipe_fd[1]);
-
-      if (close (pipe_fd[0]) < 0)
-	fatal_perror ("close %d", pipe_fd[0]);
+  pexecute_pid = pexec (nm_file_name, real_nm_argv, 0, -1, pipe_fd[1], -1);
 
-      if (close (pipe_fd[1]) < 0)
-	fatal_perror ("close %d", pipe_fd[1]);
-
-      execv (nm_file_name, real_nm_argv);
-      fatal_perror ("execv %s", nm_file_name);
-    }
+  if (pexecute_pid == -1)
+    fatal_perror ("pexec");
 
   /* Parent context from here on.  */
-  int_handler  = (void (*) PARAMS ((int))) signal (SIGINT,  SIG_IGN);
-#ifdef SIGQUIT
-  quit_handler = (void (*) PARAMS ((int))) signal (SIGQUIT, SIG_IGN);
-#endif
-
-  if (close (pipe_fd[1]) < 0)
-    fatal_perror ("close %d", pipe_fd[1]);
+  disable_break ();
 
   if (debug)
     fprintf (stderr, "\nnm output with constructors/destructors.\n");
@@ -2219,10 +2223,7 @@ scan_prog_file (prog_name, which_pass)
 
   do_wait (nm_file_name);
 
-  signal (SIGINT,  int_handler);
-#ifdef SIGQUIT
-  signal (SIGQUIT, quit_handler);
-#endif
+  enable_break ();
 }
 
 #if SUNOS4_SHARED_LIBRARIES
@@ -2511,11 +2512,8 @@ scan_libraries (prog_name)
 {
   static struct head libraries;		/* list of shared libraries found */
   struct id *list;
-  void (*int_handler) PARAMS ((int));
-  void (*quit_handler) PARAMS ((int));
   char *real_ldd_argv[4];
   const char **ldd_argv = (const char **) real_ldd_argv;
-  int pid;
   int argc = 0;
   int pipe_fd[2];
   char buf[1024];
@@ -2532,7 +2530,7 @@ scan_libraries (prog_name)
   ldd_argv[argc++] = prog_name;
   ldd_argv[argc++] = (char *) 0;
 
-  if (pipe (pipe_fd) < 0)
+  if (pmkpipe (pipe_fd) < 0)
     fatal_perror ("pipe");
 
   inf = fdopen (pipe_fd[0], "r");
@@ -2551,38 +2549,14 @@ scan_libraries (prog_name)
       fprintf (stderr, "\n");
     }
 
-  fflush (stdout);
-  fflush (stderr);
-
   /* Spawn child ldd on pipe */
-  pid = vfork ();
-  if (pid == -1)
-    fatal_perror (VFORK_STRING);
-
-  if (pid == 0)			/* child context */
-    {
-      /* setup stdout */
-      if (dup2 (pipe_fd[1], 1) < 0)
-	fatal_perror ("dup2 %d 1", pipe_fd[1]);
-
-      if (close (pipe_fd[0]) < 0)
-	fatal_perror ("close %d", pipe_fd[0]);
+  pexecute_pid = pexec (ldd_file_name, real_ldd_argv, 0, -1, infpipe[1], -1);
 
-      if (close (pipe_fd[1]) < 0)
-	fatal_perror ("close %d", pipe_fd[1]);
-
-      execv (ldd_file_name, real_ldd_argv);
-      fatal_perror ("execv %s", ldd_file_name);
-    }
+  if (pexecute_pid == -1)
+    fatal_perror ("pexec");
 
   /* Parent context from here on.  */
-  int_handler  = (void (*) PARAMS ((int))) signal (SIGINT,  SIG_IGN);
-#ifdef SIGQUIT
-  quit_handler = (void (*) PARAMS ((int))) signal (SIGQUIT, SIG_IGN);
-#endif
-
-  if (close (pipe_fd[1]) < 0)
-    fatal_perror ("close %d", pipe_fd[1]);
+  disable_break ();
 
   if (debug)
     notice ("\nldd output with constructors/destructors.\n");
@@ -2625,11 +2599,7 @@ scan_libraries (prog_name)
 
   do_wait (ldd_file_name);
 
-  signal (SIGINT,  int_handler);
-#ifdef SIGQUIT
-  signal (SIGQUIT, quit_handler);
-#endif
-
+  enable_break ();
   /* now iterate through the library list adding their symbols to
      the list.  */
   for (list = libraries.first; list; list = list->next)
===================================================================
Index: gcc/gcc.c
--- gcc/gcc.c	27 Jan 2003 23:22:15 -0000	1.360
+++ gcc/gcc.c	29 Jan 2003 07:06:07 -0000
@@ -6107,6 +6107,7 @@ main (argc, argv)
   programname = p;
 
   xmalloc_set_program_name (programname);
+  pexec_set_program_name (programname);
 
 #ifdef GCC_DRIVER_HOST_INITIALIZATION
   /* Perform host dependent initialization when needed.  */
===================================================================
Index: gcc/protoize.c
--- gcc/protoize.c	16 Jan 2003 15:37:57 -0000	1.79
+++ gcc/protoize.c	29 Jan 2003 07:06:08 -0000
@@ -4536,6 +4536,7 @@ main (argc, argv)
 #endif
 
   gcc_init_libintl ();
+  pexec_set_program_name (pname);
 
   cwd_buffer = getpwd ();
   if (!cwd_buffer)
===================================================================
Index: include/libiberty.h
--- include/libiberty.h	24 Nov 2002 06:54:38 -0000	1.31
+++ include/libiberty.h	29 Jan 2003 07:06:10 -0000
@@ -266,9 +266,18 @@ extern void hex_init PARAMS ((void));
 extern int pexecute PARAMS ((const char *, char * const *, const char *,
 			    const char *, char **, char **, int));
 
-/* Wait for pexecute to finish.  */
+/* Slightly lower level routines which are more flexible than pexecute:  */
+extern int pmkpipe PARAMS ((int[2]));
+
+extern int pexec PARAMS ((const char *, char * const *, int, int, int, int));
+
+/* Wait for a process created by pexecute or pexec to finish.  */
 
 extern int pwait PARAMS ((int, int *, int));
+
+/* Set the program name used for error messages by pexec.  */
+extern void pexec_set_program_name PARAMS ((const char *));
+
 
 #if !HAVE_DECL_ASPRINTF
 /* Like sprintf but provides a pointer to malloc'd storage, which must
===================================================================
Index: libiberty/pex-common.h
--- libiberty/pex-common.h	24 Jan 2003 20:02:11 -0000	1.1
+++ libiberty/pex-common.h	29 Jan 2003 07:06:10 -0000
@@ -25,13 +25,15 @@ Boston, MA 02111-1307, USA.  */
 #include "config.h"
 #include "libiberty.h"
 
-#define install_error_msg "installation problem, cannot exec `%s'"
+/* Intended use:
+   fprintf (stderr, install_error_msg,
+            current_program, program_being_execed, xstrerror (errno)); */
+#define install_error_msg "%s: installation problem, cannot exec \"%s\": %s\n"
 
-/* stdin file number.  */
+/* File numbers for stdin, stdout, stderr.  */
 #define STDIN_FILE_NO 0
-
-/* stdout file number.  */
 #define STDOUT_FILE_NO 1
+#define STDERR_FILE_NO 2
 
 /* value of `pipe': port index for reading.  */
 #define READ_PORT 0
===================================================================
Index: libiberty/pex-unix.c
--- libiberty/pex-unix.c	24 Jan 2003 20:02:11 -0000	1.1
+++ libiberty/pex-unix.c	29 Jan 2003 07:06:10 -0000
@@ -24,6 +24,7 @@ Boston, MA 02111-1307, USA.  */
 
 #include <stdio.h>
 #include <errno.h>
+#include <fcntl.h>
 #ifdef NEED_DECLARATION_ERRNO
 extern int errno;
 #endif
@@ -44,53 +45,58 @@ extern int errno;
 #define waitpid(pid, status, flags) wait(status)
 #endif
 
-extern int execv ();
-extern int execvp ();
+#define xdup2(a, b) do {				\
+  if (a != -1)						\
+    if (dup2 (a, b) < 0)				\
+      {							\
+	fprintf (stderr, "%s: dup2(%d, %d): %s\n",	\
+	         this_program, a, b, xstrerror(errno));	\
+        exit(127);					\
+      }							\
+} while (0);
+
+#define xclose(fd) do {					\
+  if (fd != -1)						\
+    {							\
+      close (fd);					\
+      fd = -1;						\
+    }							\
+} while (0)
+
+static const char *this_program = "<unknown>";
+
+/* Execute PROGRAM with argument vector ARGV.  If SEARCH is false,
+   PROGRAM is expected to be a usable pathname already; otherwise it
+   is looked for in the locations specified by the PATH environment
+   variable (as per execvp).
+
+   STDIN_FD, STDOUT_FD, and STDERR_FD are file descriptors which
+   should be established as the child process's stdin, stdout, or
+   stderr, respectively, or -1 if the child should inherit the
+   parent's value of that descriptor.  If any of these is not -1, it
+   should be a valid file descriptor marked close-on-exec.  */
 
 int
-pexecute (program, argv, this_pname, temp_base, errmsg_fmt, errmsg_arg, flags)
+pexec (program, argv, search, stdin_fd, stdout_fd, stderr_fd)
      const char *program;
-     char * const *argv;
-     const char *this_pname;
-     const char *temp_base ATTRIBUTE_UNUSED;
-     char **errmsg_fmt, **errmsg_arg;
-     int flags;
+     char *const *argv;
+     int search;
+     int stdin_fd;
+     int stdout_fd;
+     int stderr_fd;
 {
-  int (*func)() = (flags & PEXECUTE_SEARCH ? execvp : execv);
   int pid;
-  int pdes[2];
-  int input_desc, output_desc;
   int retries, sleep_interval;
-  /* Pipe waiting from last process, to be used as input for the next one.
-     Value is STDIN_FILE_NO if no pipe is waiting
-     (i.e. the next command is the first of a group).  */
-  static int last_pipe_input;
 
-  /* If this is the first process, initialize.  */
-  if (flags & PEXECUTE_FIRST)
-    last_pipe_input = STDIN_FILE_NO;
-
-  input_desc = last_pipe_input;
-
-  /* If this isn't the last process, make a pipe for its output,
-     and record it as waiting to be the input to the next process.  */
-  if (! (flags & PEXECUTE_LAST))
-    {
-      if (pipe (pdes) < 0)
-	{
-	  *errmsg_fmt = "pipe";
-	  *errmsg_arg = NULL;
-	  return -1;
-	}
-      output_desc = pdes[WRITE_PORT];
-      last_pipe_input = pdes[READ_PORT];
-    }
-  else
-    {
-      /* Last process.  */
-      output_desc = STDOUT_FILE_NO;
-      last_pipe_input = STDIN_FILE_NO;
-    }
+  /* Canonicalize file descriptor arguments.  */
+  stdin_fd  = (stdin_fd == STDIN_FILE_NO)   ? -1 : stdin_fd;
+  stdout_fd = (stdout_fd == STDOUT_FILE_NO) ? -1 : stdout_fd;
+  stderr_fd = (stderr_fd == STDERR_FILE_NO) ? -1 : stderr_fd;
+
+  /* If exec fails, the child will write a message to stderr, using
+     fprintf.  It must therefore use exit(), not _exit().  So we have
+     to flush pending output here to avoid duplicate output.  */
+  fflush (NULL);
 
   /* Fork a subprocess; wait and retry if it fails.  */
   sleep_interval = 1;
@@ -98,59 +104,130 @@ pexecute (program, argv, this_pname, tem
   for (retries = 0; retries < 4; retries++)
     {
       pid = fork ();
-      if (pid >= 0)
+      if (__builtin_expect (pid >= 0, 1))
 	break;
       sleep (sleep_interval);
       sleep_interval *= 2;
     }
 
-  switch (pid)
+  if (pid != 0)
     {
-    case -1:
-      *errmsg_fmt = "fork";
-      *errmsg_arg = NULL;
+      /* Parent; fork either succeeded or failed.  In either case we
+	 close the file descriptors passed to the child, to make life
+	 easier for callers.  Don't clobber errno while doing so. */
+      int serrno = errno;
+      xclose (stdin_fd);
+      xclose (stdout_fd);
+      xclose (stderr_fd);
+      errno = serrno;
+      
+      return pid;
+    }
+
+  /* We are the child.  Set up I/O redirections and call exec.
+     FIXME: Report errors without calling fprintf() in the child
+     (so we can use vfork).  */
+  xdup2 (stdin_fd, STDIN_FILE_NO);
+  xdup2 (stdout_fd, STDOUT_FILE_NO);
+  xdup2 (stderr_fd, STDERR_FILE_NO);
+
+  (search ? execvp : execv) (program, argv);
+
+  /* If we got here exec failed.  */
+  fprintf (stderr, install_error_msg,
+	   this_program, program, xstrerror (errno));
+  exit (127);
+}
+
+/* Create a pipe suitable for use with pexec - in particular, both
+   ends of the pipe are set close-on-exec.  Returns 0 if successful,
+   -1 otherwise.  Returns the pipe in the array argument, just as
+   pipe(2) does.  */
+
+int
+pmkpipe (thepipe)
+     int thepipe[2];
+{
+  if (pipe (thepipe))
+    return -1;
+
+  if (fcntl (thepipe[0], F_SETFD, FD_CLOEXEC)
+      || fcntl (thepipe[1], F_SETFD, FD_CLOEXEC))
+    {
+      int serrno = errno;
+      close (thepipe[0]);
+      close (thepipe[1]);
+      errno = serrno;
       return -1;
+    }
+  return 0;
+}
 
-    case 0: /* child */
-      /* Move the input and output pipes into place, if necessary.  */
-      if (input_desc != STDIN_FILE_NO)
-	{
-	  close (STDIN_FILE_NO);
-	  dup (input_desc);
-	  close (input_desc);
-	}
-      if (output_desc != STDOUT_FILE_NO)
+int
+pexecute (program, argv, this_pname, temp_base, errmsg_fmt, errmsg_arg, flags)
+     const char *program;
+     char * const *argv;
+     const char *this_pname ATTRIBUTE_UNUSED;
+     const char *temp_base ATTRIBUTE_UNUSED;
+     char **errmsg_fmt, **errmsg_arg;
+     int flags;
+{
+  /* Pipe waiting from last process, to be used as input for the next
+     one.  Value is -1 if no pipe is waiting (i.e. the next command is
+     the first of a group).  */
+  static int last_pipe_input = -1;
+
+  int pid;
+  int pdesc[2];
+  int serrno;
+  int child_stdin = -2, child_stdout = -2;
+
+  /* If this is the first process, last_pipe_input ought to be -1.  */
+  if (flags & PEXECUTE_FIRST)
+    if (last_pipe_input != -1)
+      abort ();
+
+  child_stdin = last_pipe_input;
+
+  /* If this is the last process, don't do anything with its output
+     pipe.  */
+  if (flags & PEXECUTE_LAST)
+    child_stdout = -1;
+  else
+    {
+      /* Create a pipe to go between this process and the next one in
+	 the pipeline.  */
+      if (pmkpipe (pdesc))
 	{
-	  close (STDOUT_FILE_NO);
-	  dup (output_desc);
-	  close (output_desc);
+	  *errmsg_fmt = "pipe";
+	  *errmsg_arg = NULL;
+	  return -1;
 	}
+      last_pipe_input = pdesc[READ_PORT];
+      child_stdout = pdesc[WRITE_PORT];
+    }
 
-      /* Close the parent's descs that aren't wanted here.  */
-      if (last_pipe_input != STDIN_FILE_NO)
-	close (last_pipe_input);
-
-      /* Exec the program.  */
-      (*func) (program, argv);
-
-      fprintf (stderr, "%s: ", this_pname);
-      fprintf (stderr, install_error_msg, program);
-      fprintf (stderr, ": %s\n", xstrerror (errno));
-      exit (-1);
-      /* NOTREACHED */
-      return 0;
-
-    default:
-      /* In the parent, after forking.
-	 Close the descriptors that we made for this child.  */
-      if (input_desc != STDIN_FILE_NO)
-	close (input_desc);
-      if (output_desc != STDOUT_FILE_NO)
-	close (output_desc);
+  pid = pexec (program, argv, (flags & PEXECUTE_SEARCH),
+	       child_stdin, child_stdout, -1);
 
-      /* Return child's process number.  */
-      return pid;
+  serrno = errno;
+  xclose (child_stdin);
+  xclose (child_stdout);
+
+  /* To prevent a file descriptor leak, close last_pipe_input if pexec
+     failed.  */
+  if (pid == -1)
+    xclose (last_pipe_input);
+
+  errno = serrno;
+
+  if (pid == -1)
+    {
+      *errmsg_fmt = "fork";
+      *errmsg_arg = NULL;
     }
+
+  return pid;
 }
 
 int
@@ -163,4 +240,11 @@ pwait (pid, status, flags)
      Needed?  */
   pid = waitpid (pid, status, 0);
   return pid;
+}
+
+void
+pexec_set_program_name (name)
+     const char *name;
+{
+  this_program = name;
 }
===================================================================
Index: libiberty/pex-win32.c
--- libiberty/pex-win32.c	24 Jan 2003 20:02:11 -0000	1.1
+++ libiberty/pex-win32.c	29 Jan 2003 07:06:11 -0000
@@ -21,223 +21,340 @@ Boston, MA 02111-1307, USA.  */
 
 #include "pex-common.h"
 
-#ifdef HAVE_STRING_H
-#include <string.h>
-#endif
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-#ifdef HAVE_SYS_WAIT_H
-#include <sys/wait.h>
-#endif
-
-#include <process.h>
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
 #include <io.h>
 #include <fcntl.h>
-#include <signal.h>
+#include <string.h>
+#include <stdlib.h>
+
+#define xclose(fd) do {					\
+  if (fd != -1)						\
+    {							\
+      _close (fd);					\
+      fd = -1;						\
+    }							\
+} while (0)
+
+
+static char *
+argv_to_cmdline(argv)
+     char *const *argv;
+{
+  char *cmdline;
+  char *p;
+  size_t cmdline_len;
+  int i, j;
+
+  cmdline_len = 0;
+  for (i = 0; argv[i]; i++)
+    {
+      /* We quote every last argument.  This simplifies the problem;
+	 we need only escape embedded double-quote and backslash
+	 characters.  */
+      for (j = 0; argv[i][j]; j++)
+	if (argv[i][j] == '\\' || argv[i][j] == '"')
+	  cmdline_len++;
+      cmdline_len += j;
+      cmdline_len += 3;  /* for leading and trailing quotes and space */
+    }
+
+  cmdline = xmalloc (cmdline_len);
+  p = cmdline;
+  for (i = 0; argv[i]; i++)
+    {
+      *p++ = '"';
+      for (j = 0; argv[i][j]; j++)
+	{
+	  if (argv[i][j] == '\\' || argv[i][j] == '"')
+	    *p++ = '\\';
+	  *p++ = argv[i][j];
+	}
+      *p++ = '"';
+      *p++ = ' ';
+    }
+  p[-1] = '\0';
+  return cmdline;
+}
+
+static const char *const
+std_suffixes[] = {
+  ".com",
+  ".exe",
+  ".bat",
+  ".cmd",
+  0
+};
+static const char *const
+no_suffixes[] = {
+  "",
+  0
+};
 
-/* mingw32 headers may not define the following.  */
+static char *
+find_executable (program, search)
+     const char *program;
+     int search;
+{
+  char *full_executable;
+  char *e;
+  size_t fe_len;
+  const char *path = 0;
+  const char *const *ext;
+  const char *p, *q;
+  size_t proglen = strlen (program);
+  int has_extension = !!strchr (program, '.');
+  int has_slash = (strchr (program, '/') || strchr (program, '\\'));
+  HANDLE h;
+
+  if (has_slash)
+    search = 0;
+
+  if (search)
+    path = getenv ("PATH");
+  if (!path)
+    path = "";
 
-#ifndef _P_WAIT
-#  define _P_WAIT	0
-#  define _P_NOWAIT	1
-#  define _P_OVERLAY	2
-#  define _P_NOWAITO	3
-#  define _P_DETACH	4
-
-#  define WAIT_CHILD		0
-#  define WAIT_GRANDCHILD	1
-#endif
-
-/* This is a kludge to get around the Microsoft C spawn functions' propensity
-   to remove the outermost set of double quotes from all arguments.  */
-
-static const char * const *
-fix_argv (argvec)
-     char **argvec;
-{
-  int i;
-
-  for (i = 1; argvec[i] != 0; i++)
-    {
-      int len, j;
-      char *temp, *newtemp;
-
-      temp = argvec[i];
-      len = strlen (temp);
-      for (j = 0; j < len; j++)
-        {
-          if (temp[j] == '"')
-            {
-              newtemp = xmalloc (len + 2);
-              strncpy (newtemp, temp, j);
-              newtemp [j] = '\\';
-              strncpy (&newtemp [j+1], &temp [j], len-j);
-              newtemp [len+1] = 0;
-              temp = newtemp;
-              len++;
-              j++;
-            }
-        }
-
-        argvec[i] = temp;
-      }
-
-  for (i = 0; argvec[i] != 0; i++)
-    {
-      if (strpbrk (argvec[i], " \t"))
-        {
-	  int len, trailing_backslash;
-	  char *temp;
-
-	  len = strlen (argvec[i]);
-	  trailing_backslash = 0;
-
-	  /* There is an added complication when an arg with embedded white
-	     space ends in a backslash (such as in the case of -iprefix arg
-	     passed to cpp). The resulting quoted strings gets misinterpreted
-	     by the command interpreter -- it thinks that the ending quote
-	     is escaped by the trailing backslash and things get confused. 
-	     We handle this case by escaping the trailing backslash, provided
-	     it was not escaped in the first place.  */
-	  if (len > 1 
-	      && argvec[i][len-1] == '\\' 
-	      && argvec[i][len-2] != '\\')
-	    {
-	      trailing_backslash = 1;
-	      ++len;			/* to escape the final backslash. */
-	    }
-
-	  len += 2;			/* and for the enclosing quotes. */
-
-	  temp = xmalloc (len + 1);
-	  temp[0] = '"';
-	  strcpy (temp + 1, argvec[i]);
-	  if (trailing_backslash)
-	    temp[len-2] = '\\';
-	  temp[len-1] = '"';
-	  temp[len] = '\0';
+  fe_len = 0;
+  for (p = path; p; p = q + 1)
+    {
+      q = p;
+      while (*q != ';' && *q != '\0')
+	q++;
+      if ((size_t)(q - p) > fe_len)
+	fe_len = q - p;
+    }
+  fe_len = fe_len + 1 + proglen + (has_extension ? 1 : 5);
+  full_executable = xmalloc (fe_len);
 
-	  argvec[i] = temp;
+  for (p = path; p; p = q + 1)
+    {
+      q = p;
+      while (*q != ';' && *q != '\0')
+	q++;
+      memcpy (full_executable, p, q - p);
+      full_executable[q - p] = '\\';
+      strcpy (full_executable + (q - p) + 1, program);
+      for (e = full_executable; *e; e++)
+	if (*e == '/')
+	  *e = '\\';
+
+      for (ext = has_extension ? no_suffixes : std_suffixes; *ext; ext++)
+	{
+	  strcat (full_executable, *ext);
+
+	  /* Attempt to open this file.  */
+	  h = CreateFile (full_executable, GENERIC_READ,
+			  FILE_SHARE_READ | FILE_SHARE_WRITE,
+			  0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
+	  if (h != INVALID_HANDLE_VALUE)
+	    goto found;
 	}
     }
+  free (full_executable);
+  return 0;
 
-  return (const char * const *) argvec;
+ found:
+  CloseHandle (h);
+  return full_executable;
+}
+
+int
+pexec (program, argv, search, stdin_fd, stdout_fd, stderr_fd)
+     const char *program;
+     char *const *argv;
+     int search;
+     int stdin_fd;
+     int stdout_fd;
+     int stderr_fd;
+{
+  char *cmdline = argv_to_cmdline (argv);
+  char *executable = find_executable (program, search);
+  STARTUPINFO si;
+  PROCESS_INFORMATION pinf;
+  HANDLE me;
+
+  /* Canonicalize file descriptor arguments.  */
+  stdin_fd  = (stdin_fd == STDIN_FILE_NO)   ? -1 : stdin_fd;
+  stdout_fd = (stdout_fd == STDOUT_FILE_NO) ? -1 : stdout_fd;
+  stderr_fd = (stderr_fd == STDERR_FILE_NO) ? -1 : stderr_fd;
+
+  memset (&si, 0, sizeof si);
+  si.cb = sizeof si;
+  si.dwFlags = STARTF_USESTDHANDLES;
+
+  me = GetCurrentProcess();
+
+  if (!DuplicateHandle (me,
+		        (stdin_fd == -1)
+			? GetStdHandle (STD_INPUT_HANDLE)
+			: (HANDLE)_get_osfhandle (stdin_fd),
+			me, &si.hStdInput,
+			0, TRUE, DUPLICATE_SAME_ACCESS))
+    goto cleanup;
+  if (!DuplicateHandle (me,
+			(stdout_fd == -1)
+			? GetStdHandle (STD_OUTPUT_HANDLE)
+			: (HANDLE)_get_osfhandle (stdout_fd),
+			me, &si.hStdOutput,
+			0, TRUE, DUPLICATE_SAME_ACCESS))
+    goto cleanup;
+  if (!DuplicateHandle (me,
+			(stderr_fd == -1)
+			? GetStdHandle (STD_ERROR_HANDLE)
+			: (HANDLE)_get_osfhandle (stderr_fd),
+			me, &si.hStdError,
+			0, TRUE, DUPLICATE_SAME_ACCESS))
+    goto cleanup;
+
+ 
+  if (!CreateProcess (executable, cmdline,
+		     0, 0, TRUE, 0, 0, 0,
+		     &si, &pinf))
+    goto cleanup;
+
+  CloseHandle (pinf.hProcess);
+  CloseHandle (pinf.hThread);
+  
+ done:
+  /* Close file descriptors passed to the child (whether or not we
+     managed to create a child).  */
+  xclose (stdin_fd);
+  xclose (stdout_fd);
+  xclose (stderr_fd);
+
+  free (executable);
+  free (cmdline);
+
+  return pinf.dwProcessId;
+
+ cleanup:
+  CloseHandle (si.hStdInput);
+  CloseHandle (si.hStdOutput);
+  CloseHandle (si.hStdError);
+  pinf.dwProcessId = -1;
+  goto done;
+}
+
+/* MSVCRT's _pipe() creates pipes that can be inherited, which is not
+   what we want, so we go directly to CreatePipe().  */
+int
+pmkpipe (thepipe)
+     int thepipe[2];
+{
+  HANDLE read_port;
+  HANDLE write_port;
+
+  if (!CreatePipe (&read_port, &write_port, 0, 0))
+    return -1;
+
+  thepipe[0] = _open_osfhandle ((long)read_port, _O_RDONLY);
+  thepipe[1] = _open_osfhandle ((long)write_port, _O_WRONLY);
+  if (thepipe[0] == -1 || thepipe[1] == -1)
+    {
+      if (thepipe[0] == -1)
+	CloseHandle (read_port);
+      else
+	_close (thepipe[0]);
+      if (thepipe[1] == -1)
+	CloseHandle (write_port);
+      else
+	_close (thepipe[1]);
+      return -1;
+    }
+  return 0;
 }
 
-/* Win32 supports pipes */
 int
 pexecute (program, argv, this_pname, temp_base, errmsg_fmt, errmsg_arg, flags)
      const char *program;
      char * const *argv;
-     const char *this_pname;
-     const char *temp_base;
+     const char *this_pname ATTRIBUTE_UNUSED;
+     const char *temp_base ATTRIBUTE_UNUSED;
      char **errmsg_fmt, **errmsg_arg;
      int flags;
 {
+  /* Pipe waiting from last process, to be used as input for the next
+     one.  Value is -1 if no pipe is waiting (i.e. the next command is
+     the first of a group).  */
+  static int last_pipe_input = -1;
+
   int pid;
-  int pdes[2], org_stdin, org_stdout;
-  int input_desc, output_desc;
-  int retries, sleep_interval;
-
-  /* Pipe waiting from last process, to be used as input for the next one.
-     Value is STDIN_FILE_NO if no pipe is waiting
-     (i.e. the next command is the first of a group).  */
-  static int last_pipe_input;
+  int pdesc[2];
+  int serrno;
+  int child_stdin = -2, child_stdout = -2;
 
-  /* If this is the first process, initialize.  */
+  /* If this is the first process, last_pipe_input ought to be -1.  */
   if (flags & PEXECUTE_FIRST)
-    last_pipe_input = STDIN_FILE_NO;
+    if (last_pipe_input != -1)
+      abort ();
 
-  input_desc = last_pipe_input;
+  child_stdin = last_pipe_input;
 
-  /* If this isn't the last process, make a pipe for its output,
-     and record it as waiting to be the input to the next process.  */
-  if (! (flags & PEXECUTE_LAST))
+  /* If this is the last process, don't do anything with its output
+     pipe.  */
+  if (flags & PEXECUTE_LAST)
+    child_stdout = -1;
+  else
     {
-      if (_pipe (pdes, 256, O_BINARY) < 0)
+      /* Create a pipe to go between this process and the next one in
+	 the pipeline.  */
+      if (pmkpipe (pdesc))
 	{
 	  *errmsg_fmt = "pipe";
 	  *errmsg_arg = NULL;
 	  return -1;
 	}
-      output_desc = pdes[WRITE_PORT];
-      last_pipe_input = pdes[READ_PORT];
-    }
-  else
-    {
-      /* Last process.  */
-      output_desc = STDOUT_FILE_NO;
-      last_pipe_input = STDIN_FILE_NO;
-    }
-
-  if (input_desc != STDIN_FILE_NO)
-    {
-      org_stdin = dup (STDIN_FILE_NO);
-      dup2 (input_desc, STDIN_FILE_NO);
-      close (input_desc); 
+      last_pipe_input = pdesc[READ_PORT];
+      child_stdout = pdesc[WRITE_PORT];
     }
 
-  if (output_desc != STDOUT_FILE_NO)
-    {
-      org_stdout = dup (STDOUT_FILE_NO);
-      dup2 (output_desc, STDOUT_FILE_NO);
-      close (output_desc);
-    }
+  pid = pexec (program, argv, (flags & PEXECUTE_SEARCH),
+	       child_stdin, child_stdout, -1);
 
-  pid = (flags & PEXECUTE_SEARCH ? _spawnvp : _spawnv)
-    (_P_NOWAIT, program, fix_argv(argv));
+  serrno = errno;
+  xclose (child_stdin);
+  xclose (child_stdout);
 
-  if (input_desc != STDIN_FILE_NO)
-    {
-      dup2 (org_stdin, STDIN_FILE_NO);
-      close (org_stdin);
-    }
+  /* To prevent a file descriptor leak, close last_pipe_input if pexec
+     failed.  */
+  if (pid == -1)
+    xclose (last_pipe_input);
 
-  if (output_desc != STDOUT_FILE_NO)
-    {
-      dup2 (org_stdout, STDOUT_FILE_NO);
-      close (org_stdout);
-    }
+  errno = serrno;
 
   if (pid == -1)
     {
-      *errmsg_fmt = install_error_msg;
-      *errmsg_arg = program;
-      return -1;
+      *errmsg_fmt = "spawn";
+      *errmsg_arg = NULL;
     }
 
   return pid;
 }
 
-/* MS CRTDLL doesn't return enough information in status to decide if the
-   child exited due to a signal or not, rather it simply returns an
-   integer with the exit code of the child; eg., if the child exited with 
-   an abort() call and didn't have a handler for SIGABRT, it simply returns
-   with status = 3. We fix the status code to conform to the usual WIF*
-   macros. Note that WIFSIGNALED will never be true under CRTDLL. */
-
 int
 pwait (pid, status, flags)
      int pid;
      int *status;
-     int flags;
+     int flags ATTRIBUTE_UNUSED;
 {
-  int termstat;
-
-  pid = _cwait (&termstat, pid, WAIT_CHILD);
-
-  /* ??? Here's an opportunity to canonicalize the values in STATUS.
-     Needed?  */
-
-  /* cwait returns the child process exit code in termstat.
-     A value of 3 indicates that the child caught a signal, but not
-     which one.  Since only SIGABRT, SIGFPE and SIGINT do anything, we
-     report SIGABRT.  */
-  if (termstat == 3)
-    *status = SIGABRT;
-  else
-    *status = (((termstat) & 0xff) << 8);
+  HANDLE proch = OpenProcess (SYNCHRONIZE | PROCESS_QUERY_INFORMATION,
+			      FALSE, pid);
+  if (proch == 0)
+    return -1;
+  if (WaitForSingleObject (proch, INFINITE) != WAIT_OBJECT_0)
+    {
+      CloseHandle (proch);
+      return -1;
+    }
 
+  GetExitCodeProcess (proch, (DWORD *)status);
+  CloseHandle (proch);
   return pid;
+}
+
+void
+pexec_set_program_name (name)
+     const char *name;
+{
 }


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]