* nlmconv.c (debug, unlink_on_exit): New static variables.
authorIan Lance Taylor <ian@airs.com>
Fri, 10 Dec 1993 05:33:49 +0000 (05:33 +0000)
committerIan Lance Taylor <ian@airs.com>
Fri, 10 Dec 1993 05:33:49 +0000 (05:33 +0000)
(long_options): Add "debug" and "linker".
(main): Handle -d and -l arguments.  Make command line input and
output files optional.  Parse the command file before opening the
BFD's, which requires storing more information in local variables.
If INPUT names multiple files, link them together.  Use OUTPUT for
the output file name if not named on command line.
(show_usage): Changed for new options.
(link_inputs): New function to automatically invoke linker to
handle multiple INPUT files.
(choose_temp_base_try, choose_temp_base, pexecute): New functions,
mostly copied from gcc/gcc.c.
* nlmconv.h (input_files, output_file): Declare.
* nlmheader.y (input_files, output_file): Define.
(command): Support INPUT with a string_list argument.  Support
OUTPUT.
(string_list): Renamed from module_list.
* Makefile.in (nlmconv.o): Define LD_NAME based on
program_transform_name.

Fixes PR 3974.

binutils/ChangeLog
binutils/nlmconv.c
binutils/nlmheader.y

index 5cc24e7a68f6fb402c458237701184981bb65855..c878242e82c59914e90d88b05b358a961a58f09f 100644 (file)
@@ -1,3 +1,25 @@
+Thu Dec  9 17:47:19 1993  Ian Lance Taylor  (ian@deneb.cygnus.com)
+
+       * nlmconv.c (debug, unlink_on_exit): New static variables.
+       (long_options): Add "debug" and "linker".
+       (main): Handle -d and -l arguments.  Make command line input and
+       output files optional.  Parse the command file before opening the
+       BFD's, which requires storing more information in local variables.
+       If INPUT names multiple files, link them together.  Use OUTPUT for
+       the output file name if not named on command line.
+       (show_usage): Changed for new options.
+       (link_inputs): New function to automatically invoke linker to
+       handle multiple INPUT files.
+       (choose_temp_base_try, choose_temp_base, pexecute): New functions,
+       mostly copied from gcc/gcc.c.
+       * nlmconv.h (input_files, output_file): Declare.
+       * nlmheader.y (input_files, output_file): Define.
+       (command): Support INPUT with a string_list argument.  Support
+       OUTPUT.
+       (string_list): Renamed from module_list.
+       * Makefile.in (nlmconv.o): Define LD_NAME based on
+       program_transform_name.
+
 Wed Dec  8 10:09:04 1993  Ian Lance Taylor  (ian@deneb.cygnus.com)
 
        * nlmheader.y (nlmheader_identify): New function.  Use it to print
index 1bf485323c66fb27d3960f5f03bd6543a48d762d..b9b84e6b7a557e439e7a110c576188ad8d0c76df 100644 (file)
@@ -32,6 +32,7 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
 #include <ctype.h>
 #include <sys/types.h>
 #include <sys/stat.h>
+#include <sys/file.h>
 #include <assert.h>
 #include <getopt.h>
 #include <bfd.h>
@@ -54,9 +55,19 @@ extern char *strerror ();
 extern struct tm *localtime ();
 #endif
 
+#ifndef getenv
+extern char *getenv ();
+#endif
+
 #ifndef SEEK_SET
 #define SEEK_SET 0
 #endif
+
+#ifndef R_OK
+#define R_OK 4
+#define W_OK 2
+#define X_OK 1
+#endif
 \f
 /* Global variables.  */
 
@@ -68,15 +79,26 @@ extern char *program_version;
 
 /* Local variables.  */
 
+/* Whether to print out debugging information (currently just controls
+   whether it prints the linker command if there is one).  */
+static int debug;
+
 /* The symbol table.  */
 static asymbol **symbols;
 
+/* A temporary file name to be unlinked on exit.  Actually, for most
+   errors, we leave it around.  It's not clear whether that is helpful
+   or not.  */
+static char *unlink_on_exit;
+
 /* The list of long options.  */
 static struct option long_options[] =
 {
+  { "debug", no_argument, 0, 'd' },
   { "header-file", required_argument, 0, 'T' },
   { "help", no_argument, 0, 'h' },
   { "input-format", required_argument, 0, 'I' },
+  { "linker", required_argument, 0, 'l' },
   { "output-format", required_argument, 0, 'O' },
   { "version", no_argument, 0, 'V' },
   { NULL, no_argument, 0, 0 }
@@ -102,6 +124,11 @@ static void alpha_mangle_relocs PARAMS ((bfd *, asection *, arelent ***,
 static void default_mangle_relocs PARAMS ((bfd *, asection *, arelent ***,
                                           bfd_size_type *, char *,
                                           bfd_size_type));
+static char *link_inputs PARAMS ((struct string_list *, char *));
+static const char *choose_temp_base_try PARAMS ((const char *,
+                                                const char *));
+static void choose_temp_base PARAMS ((void));
+static int pexecute PARAMS ((char *, char *[]));
 \f
 /* The main routine.  */
 
@@ -111,9 +138,16 @@ main (argc, argv)
      char **argv;
 {
   int opt;
+  char *input_file = NULL;
   const char *input_format = NULL;
   const char *output_format = NULL;
   const char *header_file = NULL;
+  char *ld_arg = NULL;
+  Nlm_Internal_Fixed_Header fixed_hdr_struct;
+  Nlm_Internal_Variable_Header var_hdr_struct;
+  Nlm_Internal_Version_Header version_hdr_struct;
+  Nlm_Internal_Copyright_Header copyright_hdr_struct;
+  Nlm_Internal_Extended_Header extended_hdr_struct;
   bfd *inbfd;
   bfd *outbfd;
   asymbol **newsyms, **outsyms;
@@ -140,17 +174,24 @@ main (argc, argv)
 
   bfd_init ();
 
-  while ((opt = getopt_long (argc, argv, "hI:O:T:V", long_options, (int *) 0))
+  while ((opt = getopt_long (argc, argv, "dhI:l:O:T:V", long_options,
+                            (int *) NULL))
         != EOF)
     {
       switch (opt)
        {
+       case 'd':
+         debug = 1;
+         break;
        case 'h':
          show_help ();
          /*NOTREACHED*/
        case 'I':
          input_format = optarg;
          break;
+       case 'l':
+         ld_arg = optarg;
+         break;
        case 'O':
          output_format = optarg;
          break;
@@ -169,48 +210,39 @@ main (argc, argv)
        }
     }
 
-  if (optind + 2 != argc)
-    show_usage (stderr, 1);
-
-  if (strcmp (argv[optind], argv[optind + 1]) == 0)
+  /* The input and output files may be named on the command line.  */
+  output_file = NULL;
+  if (optind < argc)
     {
-      fprintf (stderr, "%s: input and output files must be different\n",
-              program_name);
-      exit (1);
+      input_file = argv[optind];
+      ++optind;
+      if (optind < argc)
+       {
+         output_file = argv[optind];
+         ++optind;
+         if (optind < argc)
+           show_usage (stderr, 1);
+         if (strcmp (input_file, output_file) == 0)
+           {
+             fprintf (stderr,
+                      "%s: input and output files must be different\n",
+                      program_name);
+             exit (1);
+           }
+       }
     }
 
-  inbfd = bfd_openr (argv[optind], input_format);
-  if (inbfd == NULL)
-    bfd_fatal (argv[optind]);
-
-  if (! bfd_check_format (inbfd, bfd_object))
-    bfd_fatal (argv[optind]);
-
-  if (output_format == NULL)
-    output_format = select_output_format (bfd_get_arch (inbfd),
-                                         bfd_get_mach (inbfd),
-                                         inbfd->xvec->byteorder_big_p);
-
-  assert (output_format != NULL);
-  outbfd = bfd_openw (argv[optind + 1], output_format);
-  if (outbfd == NULL)
-    bfd_fatal (argv[optind + 1]);
-  if (! bfd_set_format (outbfd, bfd_object))
-    bfd_fatal (argv[optind + 1]);
-
-  assert (outbfd->xvec->flavour == bfd_target_nlm_flavour);
-
-  if (bfd_arch_get_compatible (inbfd, outbfd) == NULL)
-    fprintf (stderr,
-            "%s: warning:input and output formats are not compatible\n",
-            program_name);
-
   /* Initialize the header information to default values.  */
-  fixed_hdr = nlm_fixed_header (outbfd);
-  var_hdr = nlm_variable_header (outbfd);
-  version_hdr = nlm_version_header (outbfd);
-  copyright_hdr = nlm_copyright_header (outbfd);
-  extended_hdr = nlm_extended_header (outbfd);
+  fixed_hdr = &fixed_hdr_struct;
+  memset ((PTR) &fixed_hdr_struct, 0, sizeof fixed_hdr_struct);
+  var_hdr = &var_hdr_struct;
+  memset ((PTR) &var_hdr_struct, 0, sizeof var_hdr_struct);
+  version_hdr = &version_hdr_struct;
+  memset ((PTR) &version_hdr_struct, 0, sizeof version_hdr_struct);
+  copyright_hdr = &copyright_hdr_struct;
+  memset ((PTR) &copyright_hdr_struct, 0, sizeof copyright_hdr_struct);
+  extended_hdr = &extended_hdr_struct;
+  memset ((PTR) &extended_hdr_struct, 0, sizeof extended_hdr_struct);
   check_procedure = NULL;
   custom_file = NULL;
   debug_info = false;
@@ -238,6 +270,69 @@ main (argc, argv)
        exit (1);
     }
 
+  if (input_files != NULL)
+    {
+      if (input_file != NULL)
+       {
+         fprintf (stderr,
+                  "%s: input file named both on command line and with INPUT\n",
+                  program_name);
+         exit (1);
+       }
+      if (input_files->next == NULL)
+       input_file = input_files->string;
+      else
+       input_file = link_inputs (input_files, ld_arg);
+    }
+  else if (input_file == NULL)
+    {
+      fprintf (stderr, "%s: no input file\n", program_name);
+      show_usage (stderr, 1);
+    }
+
+  inbfd = bfd_openr (input_file, input_format);
+  if (inbfd == NULL)
+    bfd_fatal (input_file);
+
+  if (! bfd_check_format (inbfd, bfd_object))
+    bfd_fatal (input_file);
+
+  if (output_format == NULL)
+    output_format = select_output_format (bfd_get_arch (inbfd),
+                                         bfd_get_mach (inbfd),
+                                         inbfd->xvec->byteorder_big_p);
+
+  assert (output_format != NULL);
+
+  /* Use the output file named on the command line if it exists.
+     Otherwise use the file named in the OUTPUT statement.  */
+  if (output_file == NULL)
+    {
+      fprintf (stderr, "%s: no name for output file\n",
+              program_name);
+      show_usage (stderr, 1);
+    }
+
+  outbfd = bfd_openw (output_file, output_format);
+  if (outbfd == NULL)
+    bfd_fatal (output_file);
+  if (! bfd_set_format (outbfd, bfd_object))
+    bfd_fatal (output_file);
+
+  assert (outbfd->xvec->flavour == bfd_target_nlm_flavour);
+
+  if (bfd_arch_get_compatible (inbfd, outbfd) == NULL)
+    fprintf (stderr,
+            "%s: warning:input and output formats are not compatible\n",
+            program_name);
+
+  /* Move the values read from the command file into outbfd.  */
+  *nlm_fixed_header (outbfd) = fixed_hdr_struct;
+  *nlm_variable_header (outbfd) = var_hdr_struct;
+  *nlm_version_header (outbfd) = version_hdr_struct;
+  *nlm_copyright_header (outbfd) = copyright_hdr_struct;
+  *nlm_extended_header (outbfd) = extended_hdr_struct;
+
   /* Start copying the input BFD to the output BFD.  */
   if (! bfd_set_file_flags (outbfd, bfd_get_file_flags (inbfd)))
     bfd_fatal (bfd_get_filename (outbfd));
@@ -849,12 +944,12 @@ main (argc, argv)
        sharedhdr.exitProcedureOffset;
       free (data);
     }
-  len = strlen (argv[optind + 1]);
+  len = strlen (output_file);
   if (len > NLM_MODULE_NAME_SIZE - 2)
     len = NLM_MODULE_NAME_SIZE - 2;
   nlm_fixed_header (outbfd)->moduleName[0] = len;
 
-  strncpy (nlm_fixed_header (outbfd)->moduleName + 1, argv[optind + 1],
+  strncpy (nlm_fixed_header (outbfd)->moduleName + 1, output_file,
           NLM_MODULE_NAME_SIZE - 2);
   nlm_fixed_header (outbfd)->moduleName[NLM_MODULE_NAME_SIZE - 1] = '\0';
   for (modname = nlm_fixed_header (outbfd)->moduleName;
@@ -867,9 +962,12 @@ main (argc, argv)
           NLM_OLD_THREAD_NAME_LENGTH);
 
   if (! bfd_close (outbfd))
-    bfd_fatal (argv[optind + 1]);
+    bfd_fatal (output_file);
   if (! bfd_close (inbfd))
-    bfd_fatal (argv[optind]);
+    bfd_fatal (input_file);
+
+  if (unlink_on_exit != NULL)
+    unlink (unlink_on_exit);
 
   return 0;
 }
@@ -892,10 +990,11 @@ show_usage (file, status)
      int status;
 {
   fprintf (file, "\
-Usage: %s [-hV] [-I format] [-O format] [-T header-file]\n\
+Usage: %s [-dhV] [-I format] [-O format] [-T header-file] [-l linker]\n\
        [--input-format=format] [--output-format=format]\n\
-       [--header-file=file] [--help] [--version]\n\
-       in-file out-file\n",
+       [--header-file=file] [--linker=linker] [--debug]\n\
+       [--help] [--version]\n\
+       [in-file [out-file]]\n",
           program_name);
   exit (status);
 }
@@ -1389,3 +1488,256 @@ alpha_mangle_relocs (outbfd, insec, relocs_ptr, reloc_count_ptr, contents,
        (*relocs)->address += insec->output_offset;
     }
 }
+\f
+/* Name of linker.  */
+#ifndef LD_NAME
+#define LD_NAME "ld"
+#endif
+
+/* Temporary file name base.  */
+static char *temp_filename;
+
+/* The user has specified several input files.  Invoke the linker to
+   link them all together, and convert and delete the resulting output
+   file.  */
+
+static char *
+link_inputs (inputs, ld)
+     struct string_list *inputs;
+     char *ld;
+{
+  size_t c;
+  struct string_list *q;
+  char **argv;
+  size_t i;
+  int pid;
+  int status;
+
+  c = 0;
+  for (q = inputs; q != NULL; q = q->next)
+    ++c;
+
+  argv = (char **) alloca (c + 5);
+
+#ifndef __MSDOS__
+  if (ld == NULL)
+    {
+      char *p;
+
+      /* Find the linker to invoke based on how nlmconv was run.  */
+      p = program_name + strlen (program_name);
+      while (p != program_name)
+       {
+         if (p[-1] == '/')
+           {
+             ld = (char *) xmalloc (p - program_name + strlen (LD_NAME) + 1);
+             memcpy (ld, program_name, p - program_name);
+             strcpy (ld + (p - program_name), LD_NAME);
+             break;
+           }
+         --p;
+       }
+    }
+#endif
+
+  if (ld == NULL)
+    ld = (char *) LD_NAME;
+
+  choose_temp_base ();
+
+  unlink_on_exit = xmalloc (strlen (temp_filename) + 3);
+  sprintf (unlink_on_exit, "%s.O", temp_filename);
+
+  argv[0] = ld;
+  argv[1] = (char *) "-r";
+  argv[2] = (char *) "-o";
+  argv[3] = unlink_on_exit;
+  i = 4;
+  for (q = inputs; q != NULL; q = q->next, i++)
+    argv[i] = q->string;
+  argv[i] = NULL;
+
+  if (debug)
+    {
+      for (i = 0; argv[i] != NULL; i++)
+       fprintf (stderr, " %s", argv[i]);
+      fprintf (stderr, "\n");
+    }
+
+  pid = pexecute (ld, argv);
+
+  if (waitpid (pid, &status, 0) < 0)
+    {
+      perror ("waitpid");
+      unlink (unlink_on_exit);
+      exit (1);
+    }
+
+  if (status != 0)
+    {
+      fprintf (stderr, "%s: Execution of %s failed\n", program_name, ld);
+      unlink (unlink_on_exit);
+      exit (1);
+    }
+
+  return unlink_on_exit;
+}
+
+/* Choose a temporary file name.  Stolen from gcc.c.  */
+
+static const char *
+choose_temp_base_try (try, base)
+     const char *try;
+     const char *base;
+{
+  const char *rv;
+
+  if (base)
+    rv = base;
+  else if (try == NULL)
+    rv = NULL;
+  else if (access (try, R_OK | W_OK) != 0)
+    rv = NULL;
+  else
+    rv = try;
+  return rv;
+}
+
+static void
+choose_temp_base ()
+{
+  const char *base = NULL;
+  int len;
+
+  base = choose_temp_base_try (getenv ("TMPDIR"), base);
+  base = choose_temp_base_try (getenv ("TMP"), base);
+  base = choose_temp_base_try (getenv ("TEMP"), base);
+
+#ifdef P_tmpdir
+  base = choose_temp_base_try (P_tmpdir, base);
+#endif
+
+  base = choose_temp_base_try ("/usr/tmp", base);
+  base = choose_temp_base_try ("/tmp", base);
+
+  /* If all else fails, use the current directory! */  
+  if (base == NULL)
+    base = "./";
+
+  len = strlen (base);
+  temp_filename = xmalloc (len + sizeof("/ccXXXXXX") + 1);
+  strcpy (temp_filename, base);
+  if (len > 0 && temp_filename[len-1] != '/')
+    temp_filename[len++] = '/';
+  strcpy (temp_filename + len, "ccXXXXXX");
+
+  mktemp (temp_filename);
+  if (*temp_filename == '\0')
+    abort ();
+}
+
+/* Execute a job.  Stolen from gcc.c.  */
+
+#ifndef OS2
+#ifdef __MSDOS__
+
+static int
+pexecute (program, argv)
+     char *program;
+     char *argv[];
+{
+  char *scmd, *rf;
+  FILE *argfile;
+  int i;
+
+  scmd = (char *)malloc (strlen (program) + strlen (temp_filename) + 10);
+  rf = scmd + strlen(program) + 2 + el;
+  sprintf (scmd, "%s.exe @%s.gp", program, temp_filename);
+  argfile = fopen (rf, "w");
+  if (argfile == 0)
+    pfatal_with_name (rf);
+
+  for (i=1; argv[i]; i++)
+    {
+      char *cp;
+      for (cp = argv[i]; *cp; cp++)
+       {
+         if (*cp == '"' || *cp == '\'' || *cp == '\\' || isspace (*cp))
+           fputc ('\\', argfile);
+         fputc (*cp, argfile);
+       }
+      fputc ('\n', argfile);
+    }
+  fclose (argfile);
+
+  i = system (scmd);
+
+  remove (rf);
+  
+  if (i == -1)
+    {
+      perror (program);
+      return MIN_FATAL_STATUS << 8;
+    }
+
+  return i << 8;
+}
+
+#else /* not __MSDOS__ */
+
+static int
+pexecute (program, argv)
+     char *program;
+     char *argv[];
+{
+  int pid;
+  int retries, sleep_interval;
+
+  /* Fork a subprocess; wait and retry if it fails.  */
+  sleep_interval = 1;
+  for (retries = 0; retries < 4; retries++)
+    {
+      pid = vfork ();
+      if (pid >= 0)
+       break;
+      sleep (sleep_interval);
+      sleep_interval *= 2;
+    }
+
+  switch (pid)
+    {
+    case -1:
+#ifdef vfork
+      perror ("fork");
+#else
+      perror ("vfork");
+#endif
+      exit (1);
+      /* NOTREACHED */
+      return 0;
+
+    case 0: /* child */
+      /* Exec the program.  */
+      execvp (program, argv);
+      perror (program);
+      exit (1);
+      /* NOTREACHED */
+      return 0;
+
+    default:
+      /* Return child's process number.  */
+      return pid;
+    }
+}
+
+#endif /* not __MSDOS__ */
+#else /* not OS2 */
+
+static int
+pexecute (program, argv)
+     char *program;
+     char *argv[];
+{
+  return spawnvp (1, program, argv);
+}
+#endif /* not OS2 */
index 2b78b893e10c07260bd2c39852c2b40cbbda2aaf..caa8f439ced8927587936e5a751590de74116148 100644 (file)
@@ -55,6 +55,8 @@ boolean debug_info;
 char *exit_procedure;
 /* Exported symbols (EXPORT).  */
 struct string_list *export_symbols;
+/* List of files from INPUT.  */
+struct string_list *input_files;
 /* Map file name (MAP, FULLMAP).  */
 char *map_file;
 /* Whether a full map has been requested (FULLMAP).  */
@@ -67,6 +69,8 @@ struct string_list *import_symbols;
 char *message_file;
 /* Autoload module list (MODULE).  */
 struct string_list *modules;
+/* File named by OUTPUT.  */
+char *output_file;
 /* File named by SHARELIB.  */
 char *sharelib_file;
 /* Start procedure name (START).  */
@@ -126,7 +130,7 @@ static char *xstrdup PARAMS ((const char *));
 %token <string> QUOTED_STRING
 
 /* Typed non-terminals.  */
-%type <list> symbol_list_opt symbol_list module_list
+%type <list> symbol_list_opt symbol_list string_list
 %type <string> symbol
 
 %%
@@ -252,10 +256,9 @@ command:
          {
            import_symbols = string_list_append (import_symbols, $3);
          }
-       | INPUT STRING
+       | INPUT string_list
          {
-           nlmheader_warn ("INPUT not supported", -1);
-           free ($2);
+           input_files = string_list_append (input_files, $2);
          }
        | MAP STRING
          {
@@ -265,7 +268,7 @@ command:
          {
            message_file = $2;
          }
-       | MODULE module_list
+       | MODULE string_list
          {
            modules = string_list_append (modules, $2);
          }
@@ -279,8 +282,10 @@ command:
          }
        | OUTPUT STRING
          {
-           nlmheader_warn ("OUTPUT not supported", -1);
-           free ($2);
+           if (output_file == NULL)
+             output_file = $2;
+           else
+             nlmheader_warn ("ignoring duplicate OUTPUT statement", -1);
          }
        | PSEUDOPREEMPTION
          {
@@ -460,14 +465,14 @@ symbol:
          }
        ;
 
-/* A list of modules.  */
+/* A list of strings.  */
 
-module_list:
+string_list:
          /* May be empty.  */
          {
            $$ = NULL;
          }
-       | STRING module_list
+       | STRING string_list
          {
            $$ = string_list_cons ($1, $2);
          }