Home | History | Annotate | Download | only in binutils
      1 /* dllwrap.c -- wrapper for DLLTOOL and GCC to generate PE style DLLs
      2    Copyright (C) 1998-2016 Free Software Foundation, Inc.
      3    Contributed by Mumit Khan (khan (at) xraylith.wisc.edu).
      4 
      5    This file is part of GNU Binutils.
      6 
      7    This program is free software; you can redistribute it and/or modify
      8    it under the terms of the GNU General Public License as published by
      9    the Free Software Foundation; either version 3 of the License, or
     10    (at your option) any later version.
     11 
     12    This program is distributed in the hope that it will be useful,
     13    but WITHOUT ANY WARRANTY; without even the implied warranty of
     14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     15    GNU General Public License for more details.
     16 
     17    You should have received a copy of the GNU General Public License
     18    along with this program; if not, write to the Free Software
     19    Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
     20    02110-1301, USA.  */
     21 
     22 #include "sysdep.h"
     23 #include "bfd.h"
     24 #include "libiberty.h"
     25 #include "getopt.h"
     26 #include "dyn-string.h"
     27 #include "bucomm.h"
     28 
     29 #include <time.h>
     30 
     31 #ifdef HAVE_SYS_WAIT_H
     32 #include <sys/wait.h>
     33 #else /* ! HAVE_SYS_WAIT_H */
     34 #if ! defined (_WIN32) || defined (__CYGWIN32__)
     35 #ifndef WIFEXITED
     36 #define WIFEXITED(w)	(((w)&0377) == 0)
     37 #endif
     38 #ifndef WIFSIGNALED
     39 #define WIFSIGNALED(w)	(((w)&0377) != 0177 && ((w)&~0377) == 0)
     40 #endif
     41 #ifndef WTERMSIG
     42 #define WTERMSIG(w)	((w) & 0177)
     43 #endif
     44 #ifndef WEXITSTATUS
     45 #define WEXITSTATUS(w)	(((w) >> 8) & 0377)
     46 #endif
     47 #else /* defined (_WIN32) && ! defined (__CYGWIN32__) */
     48 #ifndef WIFEXITED
     49 #define WIFEXITED(w)	(((w) & 0xff) == 0)
     50 #endif
     51 #ifndef WIFSIGNALED
     52 #define WIFSIGNALED(w)	(((w) & 0xff) != 0 && ((w) & 0xff) != 0x7f)
     53 #endif
     54 #ifndef WTERMSIG
     55 #define WTERMSIG(w)	((w) & 0x7f)
     56 #endif
     57 #ifndef WEXITSTATUS
     58 #define WEXITSTATUS(w)	(((w) & 0xff00) >> 8)
     59 #endif
     60 #endif /* defined (_WIN32) && ! defined (__CYGWIN32__) */
     61 #endif /* ! HAVE_SYS_WAIT_H */
     62 
     63 static char *driver_name = NULL;
     64 static char *cygwin_driver_flags =
     65   "-Wl,--dll -nostartfiles";
     66 static char *mingw32_driver_flags = "-mdll";
     67 static char *generic_driver_flags = "-Wl,--dll";
     68 
     69 static char *entry_point;
     70 
     71 static char *dlltool_name = NULL;
     72 
     73 static char *target = TARGET;
     74 
     75 /* -1: use default, 0: no underscoring, 1: underscore.  */
     76 static int is_leading_underscore = -1;
     77 
     78 typedef enum {
     79   UNKNOWN_TARGET,
     80   CYGWIN_TARGET,
     81   MINGW_TARGET
     82 }
     83 target_type;
     84 
     85 typedef enum {
     86   UNKNOWN_CPU,
     87   X86_CPU,
     88   X64_CPU,
     89   ARM_CPU
     90 }
     91 target_cpu;
     92 
     93 static target_type which_target = UNKNOWN_TARGET;
     94 static target_cpu which_cpu = UNKNOWN_CPU;
     95 
     96 static int dontdeltemps = 0;
     97 static int dry_run = 0;
     98 
     99 static char *prog_name;
    100 
    101 static int verbose = 0;
    102 
    103 static char *dll_file_name;
    104 static char *dll_name;
    105 static char *base_file_name;
    106 static char *exp_file_name;
    107 static char *def_file_name;
    108 static int delete_base_file = 1;
    109 static int delete_exp_file = 1;
    110 static int delete_def_file = 1;
    111 
    112 static int run (const char *, char *);
    113 static char *mybasename (const char *);
    114 static int strhash (const char *);
    115 static void usage (FILE *, int);
    116 static void display (const char *, va_list) ATTRIBUTE_PRINTF(1,0);
    117 static void inform (const char *, ...) ATTRIBUTE_PRINTF_1;
    118 static void warn (const char *, ...) ATTRIBUTE_PRINTF_1;
    119 static char *look_for_prog (const char *, const char *, int);
    120 static char *deduce_name (const char *);
    121 static void delete_temp_files (void);
    122 static void cleanup_and_exit (int);
    123 
    124 /**********************************************************************/
    125 
    126 /* Please keep the following 4 routines in sync with dlltool.c:
    127      display ()
    128      inform ()
    129      look_for_prog ()
    130      deduce_name ()
    131    It's not worth the hassle to break these out since dllwrap will
    132    (hopefully) soon be retired in favor of `ld --shared.  */
    133 
    134 static void
    135 display (const char * message, va_list args)
    136 {
    137   if (prog_name != NULL)
    138     fprintf (stderr, "%s: ", prog_name);
    139 
    140   vfprintf (stderr, message, args);
    141   fputc ('\n', stderr);
    142 }
    143 
    144 
    145 static void
    146 inform (const char *message, ...)
    147 {
    148   va_list args;
    149 
    150   va_start (args, message);
    151 
    152   if (!verbose)
    153     return;
    154 
    155   display (message, args);
    156 
    157   va_end (args);
    158 }
    159 
    160 static void
    161 warn (const char *format, ...)
    162 {
    163   va_list args;
    164 
    165   va_start (args, format);
    166 
    167   display (format, args);
    168 
    169   va_end (args);
    170 }
    171 
    172 /* Look for the program formed by concatenating PROG_NAME and the
    173    string running from PREFIX to END_PREFIX.  If the concatenated
    174    string contains a '/', try appending EXECUTABLE_SUFFIX if it is
    175    appropriate.  */
    176 
    177 static char *
    178 look_for_prog (const char *progname, const char *prefix, int end_prefix)
    179 {
    180   struct stat s;
    181   char *cmd;
    182 
    183   cmd = xmalloc (strlen (prefix)
    184 		 + strlen (progname)
    185 #ifdef HAVE_EXECUTABLE_SUFFIX
    186 		 + strlen (EXECUTABLE_SUFFIX)
    187 #endif
    188 		 + 10);
    189   strcpy (cmd, prefix);
    190 
    191   sprintf (cmd + end_prefix, "%s", progname);
    192 
    193   if (strchr (cmd, '/') != NULL)
    194     {
    195       int found;
    196 
    197       found = (stat (cmd, &s) == 0
    198 #ifdef HAVE_EXECUTABLE_SUFFIX
    199 	       || stat (strcat (cmd, EXECUTABLE_SUFFIX), &s) == 0
    200 #endif
    201 	       );
    202 
    203       if (! found)
    204 	{
    205 	  /* xgettext:c-format */
    206 	  inform (_("Tried file: %s"), cmd);
    207 	  free (cmd);
    208 	  return NULL;
    209 	}
    210     }
    211 
    212   /* xgettext:c-format */
    213   inform (_("Using file: %s"), cmd);
    214 
    215   return cmd;
    216 }
    217 
    218 /* Deduce the name of the program we are want to invoke.
    219    PROG_NAME is the basic name of the program we want to run,
    220    eg "as" or "ld".  The catch is that we might want actually
    221    run "i386-pe-as" or "ppc-pe-ld".
    222 
    223    If argv[0] contains the full path, then try to find the program
    224    in the same place, with and then without a target-like prefix.
    225 
    226    Given, argv[0] = /usr/local/bin/i586-cygwin32-dlltool,
    227    deduce_name("as") uses the following search order:
    228 
    229      /usr/local/bin/i586-cygwin32-as
    230      /usr/local/bin/as
    231      as
    232 
    233    If there's an EXECUTABLE_SUFFIX, it'll use that as well; for each
    234    name, it'll try without and then with EXECUTABLE_SUFFIX.
    235 
    236    Given, argv[0] = i586-cygwin32-dlltool, it will not even try "as"
    237    as the fallback, but rather return i586-cygwin32-as.
    238 
    239    Oh, and given, argv[0] = dlltool, it'll return "as".
    240 
    241    Returns a dynamically allocated string.  */
    242 
    243 static char *
    244 deduce_name (const char * name)
    245 {
    246   char *cmd;
    247   const char *dash;
    248   const char *slash;
    249   const char *cp;
    250 
    251   dash = NULL;
    252   slash = NULL;
    253   for (cp = prog_name; *cp != '\0'; ++cp)
    254     {
    255       if (*cp == '-')
    256 	dash = cp;
    257 
    258       if (
    259 #if defined(__DJGPP__) || defined (__CYGWIN__) || defined(__WIN32__)
    260 	  *cp == ':' || *cp == '\\' ||
    261 #endif
    262 	  *cp == '/')
    263 	{
    264 	  slash = cp;
    265 	  dash = NULL;
    266 	}
    267     }
    268 
    269   cmd = NULL;
    270 
    271   if (dash != NULL)
    272     /* First, try looking for a prefixed NAME in the
    273        PROG_NAME directory, with the same prefix as PROG_NAME.  */
    274     cmd = look_for_prog (name, prog_name, dash - prog_name + 1);
    275 
    276   if (slash != NULL && cmd == NULL)
    277     /* Next, try looking for a NAME in the same directory as
    278        that of this program.  */
    279     cmd = look_for_prog (name, prog_name, slash - prog_name + 1);
    280 
    281   if (cmd == NULL)
    282     /* Just return NAME as is.  */
    283     cmd = xstrdup (name);
    284 
    285   return cmd;
    286 }
    287 
    288 static void
    289 delete_temp_files (void)
    290 {
    291   if (delete_base_file && base_file_name)
    292     {
    293       if (verbose)
    294 	{
    295 	  if (dontdeltemps)
    296 	    warn (_("Keeping temporary base file %s"), base_file_name);
    297 	  else
    298 	    warn (_("Deleting temporary base file %s"), base_file_name);
    299 	}
    300       if (! dontdeltemps)
    301 	{
    302 	  unlink (base_file_name);
    303 	  free (base_file_name);
    304 	}
    305     }
    306 
    307   if (delete_exp_file && exp_file_name)
    308     {
    309       if (verbose)
    310 	{
    311 	  if (dontdeltemps)
    312 	    warn (_("Keeping temporary exp file %s"), exp_file_name);
    313 	  else
    314 	    warn (_("Deleting temporary exp file %s"), exp_file_name);
    315 	}
    316       if (! dontdeltemps)
    317 	{
    318 	  unlink (exp_file_name);
    319 	  free (exp_file_name);
    320 	}
    321     }
    322   if (delete_def_file && def_file_name)
    323     {
    324       if (verbose)
    325 	{
    326 	  if (dontdeltemps)
    327 	    warn (_("Keeping temporary def file %s"), def_file_name);
    328 	  else
    329 	    warn (_("Deleting temporary def file %s"), def_file_name);
    330 	}
    331       if (! dontdeltemps)
    332 	{
    333 	  unlink (def_file_name);
    334 	  free (def_file_name);
    335 	}
    336     }
    337 }
    338 
    339 static void
    340 cleanup_and_exit (int status)
    341 {
    342   delete_temp_files ();
    343   exit (status);
    344 }
    345 
    346 static int
    347 run (const char *what, char *args)
    348 {
    349   char *s;
    350   int pid, wait_status, retcode;
    351   int i;
    352   const char **argv;
    353   char *errmsg_fmt, *errmsg_arg;
    354   char *temp_base = choose_temp_base ();
    355   int in_quote;
    356   char sep;
    357 
    358   if (verbose || dry_run)
    359     fprintf (stderr, "%s %s\n", what, args);
    360 
    361   /* Count the args */
    362   i = 0;
    363   for (s = args; *s; s++)
    364     if (*s == ' ')
    365       i++;
    366   i++;
    367   argv = xmalloc (sizeof (char *) * (i + 3));
    368   i = 0;
    369   argv[i++] = what;
    370   s = args;
    371   while (1)
    372     {
    373       while (*s == ' ' && *s != 0)
    374 	s++;
    375       if (*s == 0)
    376 	break;
    377       in_quote = (*s == '\'' || *s == '"');
    378       sep = (in_quote) ? *s++ : ' ';
    379       argv[i++] = s;
    380       while (*s != sep && *s != 0)
    381 	s++;
    382       if (*s == 0)
    383 	break;
    384       *s++ = 0;
    385       if (in_quote)
    386 	s++;
    387     }
    388   argv[i++] = NULL;
    389 
    390   if (dry_run)
    391     return 0;
    392 
    393   pid = pexecute (argv[0], (char * const *) argv, prog_name, temp_base,
    394 		  &errmsg_fmt, &errmsg_arg, PEXECUTE_ONE | PEXECUTE_SEARCH);
    395   free (argv);
    396 
    397   if (pid == -1)
    398     {
    399       int errno_val = errno;
    400 
    401       fprintf (stderr, "%s: ", prog_name);
    402       fprintf (stderr, errmsg_fmt, errmsg_arg);
    403       fprintf (stderr, ": %s\n", strerror (errno_val));
    404       return 1;
    405     }
    406 
    407   retcode = 0;
    408   pid = pwait (pid, &wait_status, 0);
    409   if (pid == -1)
    410     {
    411       warn (_("pwait returns: %s"), strerror (errno));
    412       retcode = 1;
    413     }
    414   else if (WIFSIGNALED (wait_status))
    415     {
    416       warn (_("subprocess got fatal signal %d"), WTERMSIG (wait_status));
    417       retcode = 1;
    418     }
    419   else if (WIFEXITED (wait_status))
    420     {
    421       if (WEXITSTATUS (wait_status) != 0)
    422 	{
    423 	  warn (_("%s exited with status %d"), what, WEXITSTATUS (wait_status));
    424 	  retcode = 1;
    425 	}
    426     }
    427   else
    428     retcode = 1;
    429 
    430   return retcode;
    431 }
    432 
    433 static char *
    434 mybasename (const char *name)
    435 {
    436   const char *base = name;
    437 
    438   while (*name)
    439     {
    440       if (*name == '/' || *name == '\\')
    441 	{
    442 	  base = name + 1;
    443 	}
    444       ++name;
    445     }
    446   return (char *) base;
    447 }
    448 
    449 static int
    450 strhash (const char *str)
    451 {
    452   const unsigned char *s;
    453   unsigned long hash;
    454   unsigned int c;
    455   unsigned int len;
    456 
    457   hash = 0;
    458   len = 0;
    459   s = (const unsigned char *) str;
    460   while ((c = *s++) != '\0')
    461     {
    462       hash += c + (c << 17);
    463       hash ^= hash >> 2;
    464       ++len;
    465     }
    466   hash += len + (len << 17);
    467   hash ^= hash >> 2;
    468 
    469   return hash;
    470 }
    471 
    472 /**********************************************************************/
    473 
    474 static void
    475 usage (FILE *file, int status)
    476 {
    477   fprintf (file, _("Usage %s <option(s)> <object-file(s)>\n"), prog_name);
    478   fprintf (file, _("  Generic options:\n"));
    479   fprintf (file, _("   @<file>                Read options from <file>\n"));
    480   fprintf (file, _("   --quiet, -q            Work quietly\n"));
    481   fprintf (file, _("   --verbose, -v          Verbose\n"));
    482   fprintf (file, _("   --version              Print dllwrap version\n"));
    483   fprintf (file, _("   --implib <outname>     Synonym for --output-lib\n"));
    484   fprintf (file, _("  Options for %s:\n"), prog_name);
    485   fprintf (file, _("   --driver-name <driver> Defaults to \"gcc\"\n"));
    486   fprintf (file, _("   --driver-flags <flags> Override default ld flags\n"));
    487   fprintf (file, _("   --dlltool-name <dlltool> Defaults to \"dlltool\"\n"));
    488   fprintf (file, _("   --entry <entry>        Specify alternate DLL entry point\n"));
    489   fprintf (file, _("   --image-base <base>    Specify image base address\n"));
    490   fprintf (file, _("   --target <machine>     i386-cygwin32 or i386-mingw32\n"));
    491   fprintf (file, _("   --dry-run              Show what needs to be run\n"));
    492   fprintf (file, _("   --mno-cygwin           Create Mingw DLL\n"));
    493   fprintf (file, _("  Options passed to DLLTOOL:\n"));
    494   fprintf (file, _("   --machine <machine>\n"));
    495   fprintf (file, _("   --output-exp <outname> Generate export file.\n"));
    496   fprintf (file, _("   --output-lib <outname> Generate input library.\n"));
    497   fprintf (file, _("   --add-indirect         Add dll indirects to export file.\n"));
    498   fprintf (file, _("   --dllname <name>       Name of input dll to put into output lib.\n"));
    499   fprintf (file, _("   --def <deffile>        Name input .def file\n"));
    500   fprintf (file, _("   --output-def <deffile> Name output .def file\n"));
    501   fprintf (file, _("   --export-all-symbols     Export all symbols to .def\n"));
    502   fprintf (file, _("   --no-export-all-symbols  Only export .drectve symbols\n"));
    503   fprintf (file, _("   --exclude-symbols <list> Exclude <list> from .def\n"));
    504   fprintf (file, _("   --no-default-excludes    Zap default exclude symbols\n"));
    505   fprintf (file, _("   --base-file <basefile> Read linker generated base file\n"));
    506   fprintf (file, _("   --no-idata4           Don't generate idata$4 section\n"));
    507   fprintf (file, _("   --no-idata5           Don't generate idata$5 section\n"));
    508   fprintf (file, _("   -U                     Add underscores to .lib\n"));
    509   fprintf (file, _("   -k                     Kill @<n> from exported names\n"));
    510   fprintf (file, _("   --add-stdcall-alias    Add aliases without @<n>\n"));
    511   fprintf (file, _("   --as <name>            Use <name> for assembler\n"));
    512   fprintf (file, _("   --nodelete             Keep temp files.\n"));
    513   fprintf (file, _("   --no-leading-underscore  Entrypoint without underscore\n"));
    514   fprintf (file, _("   --leading-underscore     Entrypoint with underscore.\n"));
    515   fprintf (file, _("  Rest are passed unmodified to the language driver\n"));
    516   fprintf (file, "\n\n");
    517   if (REPORT_BUGS_TO[0] && status == 0)
    518     fprintf (file, _("Report bugs to %s\n"), REPORT_BUGS_TO);
    519   exit (status);
    520 }
    521 
    522 #define OPTION_START		149
    523 
    524 /* GENERIC options.  */
    525 #define OPTION_QUIET		(OPTION_START + 1)
    526 #define OPTION_VERBOSE		(OPTION_QUIET + 1)
    527 #define OPTION_VERSION		(OPTION_VERBOSE + 1)
    528 
    529 /* DLLWRAP options.  */
    530 #define OPTION_DRY_RUN		(OPTION_VERSION + 1)
    531 #define OPTION_DRIVER_NAME	(OPTION_DRY_RUN + 1)
    532 #define OPTION_DRIVER_FLAGS	(OPTION_DRIVER_NAME + 1)
    533 #define OPTION_DLLTOOL_NAME	(OPTION_DRIVER_FLAGS + 1)
    534 #define OPTION_ENTRY		(OPTION_DLLTOOL_NAME + 1)
    535 #define OPTION_IMAGE_BASE	(OPTION_ENTRY + 1)
    536 #define OPTION_TARGET		(OPTION_IMAGE_BASE + 1)
    537 #define OPTION_MNO_CYGWIN	(OPTION_TARGET + 1)
    538 #define OPTION_NO_LEADING_UNDERSCORE (OPTION_MNO_CYGWIN + 1)
    539 #define OPTION_LEADING_UNDERSCORE (OPTION_NO_LEADING_UNDERSCORE + 1)
    540 
    541 /* DLLTOOL options.  */
    542 #define OPTION_NODELETE		(OPTION_LEADING_UNDERSCORE + 1)
    543 #define OPTION_DLLNAME		(OPTION_NODELETE + 1)
    544 #define OPTION_NO_IDATA4	(OPTION_DLLNAME + 1)
    545 #define OPTION_NO_IDATA5	(OPTION_NO_IDATA4 + 1)
    546 #define OPTION_OUTPUT_EXP	(OPTION_NO_IDATA5 + 1)
    547 #define OPTION_OUTPUT_DEF	(OPTION_OUTPUT_EXP + 1)
    548 #define OPTION_EXPORT_ALL_SYMS	(OPTION_OUTPUT_DEF + 1)
    549 #define OPTION_NO_EXPORT_ALL_SYMS (OPTION_EXPORT_ALL_SYMS + 1)
    550 #define OPTION_EXCLUDE_SYMS	(OPTION_NO_EXPORT_ALL_SYMS + 1)
    551 #define OPTION_NO_DEFAULT_EXCLUDES (OPTION_EXCLUDE_SYMS + 1)
    552 #define OPTION_OUTPUT_LIB	(OPTION_NO_DEFAULT_EXCLUDES + 1)
    553 #define OPTION_DEF		(OPTION_OUTPUT_LIB + 1)
    554 #define OPTION_ADD_UNDERSCORE	(OPTION_DEF + 1)
    555 #define OPTION_KILLAT		(OPTION_ADD_UNDERSCORE + 1)
    556 #define OPTION_HELP		(OPTION_KILLAT + 1)
    557 #define OPTION_MACHINE		(OPTION_HELP + 1)
    558 #define OPTION_ADD_INDIRECT	(OPTION_MACHINE + 1)
    559 #define OPTION_BASE_FILE	(OPTION_ADD_INDIRECT + 1)
    560 #define OPTION_AS		(OPTION_BASE_FILE + 1)
    561 
    562 static const struct option long_options[] =
    563 {
    564   /* generic options.  */
    565   {"quiet", no_argument, NULL, 'q'},
    566   {"verbose", no_argument, NULL, 'v'},
    567   {"version", no_argument, NULL, OPTION_VERSION},
    568   {"implib", required_argument, NULL, OPTION_OUTPUT_LIB},
    569 
    570   /* dllwrap options.  */
    571   {"dry-run", no_argument, NULL, OPTION_DRY_RUN},
    572   {"driver-name", required_argument, NULL, OPTION_DRIVER_NAME},
    573   {"driver-flags", required_argument, NULL, OPTION_DRIVER_FLAGS},
    574   {"dlltool-name", required_argument, NULL, OPTION_DLLTOOL_NAME},
    575   {"entry", required_argument, NULL, 'e'},
    576   {"image-base", required_argument, NULL, OPTION_IMAGE_BASE},
    577   {"target", required_argument, NULL, OPTION_TARGET},
    578   {"no-leading-underscore", no_argument, NULL, OPTION_NO_LEADING_UNDERSCORE},
    579   {"leading-underscore", no_argument, NULL, OPTION_NO_LEADING_UNDERSCORE},
    580 
    581   /* dlltool options.  */
    582   {"no-delete", no_argument, NULL, 'n'},
    583   {"dllname", required_argument, NULL, OPTION_DLLNAME},
    584   {"no-idata4", no_argument, NULL, OPTION_NO_IDATA4},
    585   {"no-idata5", no_argument, NULL, OPTION_NO_IDATA5},
    586   {"output-exp", required_argument, NULL, OPTION_OUTPUT_EXP},
    587   {"output-def", required_argument, NULL, OPTION_OUTPUT_DEF},
    588   {"export-all-symbols", no_argument, NULL, OPTION_EXPORT_ALL_SYMS},
    589   {"no-export-all-symbols", no_argument, NULL, OPTION_NO_EXPORT_ALL_SYMS},
    590   {"exclude-symbols", required_argument, NULL, OPTION_EXCLUDE_SYMS},
    591   {"no-default-excludes", no_argument, NULL, OPTION_NO_DEFAULT_EXCLUDES},
    592   {"output-lib", required_argument, NULL, OPTION_OUTPUT_LIB},
    593   {"def", required_argument, NULL, OPTION_DEF},
    594   {"add-underscore", no_argument, NULL, 'U'},
    595   {"killat", no_argument, NULL, 'k'},
    596   {"add-stdcall-alias", no_argument, NULL, 'A'},
    597   {"help", no_argument, NULL, 'h'},
    598   {"machine", required_argument, NULL, OPTION_MACHINE},
    599   {"add-indirect", no_argument, NULL, OPTION_ADD_INDIRECT},
    600   {"base-file", required_argument, NULL, OPTION_BASE_FILE},
    601   {"as", required_argument, NULL, OPTION_AS},
    602   {0, 0, 0, 0}
    603 };
    604 
    605 int main (int, char **);
    606 
    607 int
    608 main (int argc, char **argv)
    609 {
    610   int c;
    611   int i;
    612 
    613   char **saved_argv = 0;
    614   int cmdline_len = 0;
    615 
    616   int export_all = 0;
    617 
    618   int *dlltool_arg_indices;
    619   int *driver_arg_indices;
    620 
    621   char *driver_flags = 0;
    622   char *output_lib_file_name = 0;
    623 
    624   dyn_string_t dlltool_cmdline;
    625   dyn_string_t driver_cmdline;
    626 
    627   int def_file_seen = 0;
    628 
    629   char *image_base_str = 0;
    630 
    631   prog_name = argv[0];
    632 
    633 #if defined (HAVE_SETLOCALE) && defined (HAVE_LC_MESSAGES)
    634   setlocale (LC_MESSAGES, "");
    635 #endif
    636 #if defined (HAVE_SETLOCALE)
    637   setlocale (LC_CTYPE, "");
    638 #endif
    639   bindtextdomain (PACKAGE, LOCALEDIR);
    640   textdomain (PACKAGE);
    641 
    642   expandargv (&argc, &argv);
    643 
    644   saved_argv = (char **) xmalloc (argc * sizeof (char*));
    645   dlltool_arg_indices = (int *) xmalloc (argc * sizeof (int));
    646   driver_arg_indices = (int *) xmalloc (argc * sizeof (int));
    647   for (i = 0; i < argc; ++i)
    648     {
    649       size_t len = strlen (argv[i]);
    650       char *arg = (char *) xmalloc (len + 1);
    651       strcpy (arg, argv[i]);
    652       cmdline_len += len;
    653       saved_argv[i] = arg;
    654       dlltool_arg_indices[i] = 0;
    655       driver_arg_indices[i] = 1;
    656     }
    657   cmdline_len++;
    658 
    659   /* We recognize dllwrap and dlltool options, and everything else is
    660      passed onto the language driver (eg., to GCC). We collect options
    661      to dlltool and driver in dlltool_args and driver_args.  */
    662 
    663   opterr = 0;
    664   while ((c = getopt_long_only (argc, argv, "nkAqve:Uho:l:L:I:",
    665 				long_options, (int *) 0)) != EOF)
    666     {
    667       int dlltool_arg;
    668       int driver_arg;
    669       int single_word_option_value_pair;
    670 
    671       dlltool_arg = 0;
    672       driver_arg = 1;
    673       single_word_option_value_pair = 0;
    674 
    675       if (c != '?')
    676 	{
    677 	  /* We recognize this option, so it has to be either dllwrap or
    678 	     dlltool option. Do not pass to driver unless it's one of the
    679 	     generic options that are passed to all the tools (such as -v)
    680 	     which are dealt with later.  */
    681 	  driver_arg = 0;
    682 	}
    683 
    684       /* deal with generic and dllwrap options first.  */
    685       switch (c)
    686 	{
    687 	case 'h':
    688 	  usage (stdout, 0);
    689 	  break;
    690 	case 'q':
    691 	  verbose = 0;
    692 	  break;
    693 	case 'v':
    694 	  verbose = 1;
    695 	  break;
    696 	case OPTION_VERSION:
    697 	  print_version (prog_name);
    698 	  break;
    699 	case 'e':
    700 	  entry_point = optarg;
    701 	  break;
    702 	case OPTION_IMAGE_BASE:
    703 	  image_base_str = optarg;
    704 	  break;
    705 	case OPTION_DEF:
    706 	  def_file_name = optarg;
    707 	  def_file_seen = 1;
    708 	  delete_def_file = 0;
    709 	  break;
    710 	case 'n':
    711 	  dontdeltemps = 1;
    712 	  dlltool_arg = 1;
    713 	  break;
    714 	case 'o':
    715 	  dll_file_name = optarg;
    716 	  break;
    717 	case 'I':
    718 	case 'l':
    719 	case 'L':
    720 	  driver_arg = 1;
    721 	  break;
    722 	case OPTION_DLLNAME:
    723 	  dll_name = optarg;
    724 	  break;
    725 	case OPTION_DRY_RUN:
    726 	  dry_run = 1;
    727 	  break;
    728 	case OPTION_DRIVER_NAME:
    729 	  driver_name = optarg;
    730 	  break;
    731 	case OPTION_DRIVER_FLAGS:
    732 	  driver_flags = optarg;
    733 	  break;
    734 	case OPTION_DLLTOOL_NAME:
    735 	  dlltool_name = optarg;
    736 	  break;
    737 	case OPTION_TARGET:
    738 	  target = optarg;
    739 	  break;
    740 	case OPTION_MNO_CYGWIN:
    741 	  target = "i386-mingw32";
    742 	  break;
    743 	case OPTION_NO_LEADING_UNDERSCORE:
    744 	  is_leading_underscore = 0;
    745 	  break;
    746 	case OPTION_LEADING_UNDERSCORE:
    747 	  is_leading_underscore = 1;
    748 	  break;
    749 	case OPTION_BASE_FILE:
    750 	  base_file_name = optarg;
    751 	  delete_base_file = 0;
    752 	  break;
    753 	case OPTION_OUTPUT_EXP:
    754 	  exp_file_name = optarg;
    755 	  delete_exp_file = 0;
    756 	  break;
    757 	case OPTION_EXPORT_ALL_SYMS:
    758 	  export_all = 1;
    759 	  break;
    760 	case OPTION_OUTPUT_LIB:
    761 	  output_lib_file_name = optarg;
    762 	  break;
    763 	case '?':
    764 	  break;
    765 	default:
    766 	  dlltool_arg = 1;
    767 	  break;
    768 	}
    769 
    770       /* Handle passing through --option=value case.  */
    771       if (optarg
    772 	  && saved_argv[optind-1][0] == '-'
    773 	  && saved_argv[optind-1][1] == '-'
    774 	  && strchr (saved_argv[optind-1], '='))
    775 	single_word_option_value_pair = 1;
    776 
    777       if (dlltool_arg)
    778 	{
    779 	  dlltool_arg_indices[optind-1] = 1;
    780 	  if (optarg && ! single_word_option_value_pair)
    781 	    {
    782 	      dlltool_arg_indices[optind-2] = 1;
    783 	    }
    784 	}
    785 
    786       if (! driver_arg)
    787 	{
    788 	  driver_arg_indices[optind-1] = 0;
    789 	  if (optarg && ! single_word_option_value_pair)
    790 	    {
    791 	      driver_arg_indices[optind-2] = 0;
    792 	    }
    793 	}
    794     }
    795 
    796   /* Sanity checks.  */
    797   if (! dll_name && ! dll_file_name)
    798     {
    799       warn (_("Must provide at least one of -o or --dllname options"));
    800       exit (1);
    801     }
    802   else if (! dll_name)
    803     {
    804       dll_name = xstrdup (mybasename (dll_file_name));
    805     }
    806   else if (! dll_file_name)
    807     {
    808       dll_file_name = xstrdup (dll_name);
    809     }
    810 
    811   /* Deduce driver-name and dlltool-name from our own.  */
    812   if (driver_name == NULL)
    813     driver_name = deduce_name ("gcc");
    814 
    815   if (dlltool_name == NULL)
    816     dlltool_name = deduce_name ("dlltool");
    817 
    818   if (! def_file_seen)
    819     {
    820       char *fileprefix = choose_temp_base ();
    821 
    822       def_file_name = (char *) xmalloc (strlen (fileprefix) + 5);
    823       sprintf (def_file_name, "%s.def",
    824 	       (dontdeltemps) ? mybasename (fileprefix) : fileprefix);
    825       delete_def_file = 1;
    826       free (fileprefix);
    827       delete_def_file = 1;
    828       warn (_("no export definition file provided.\n\
    829 Creating one, but that may not be what you want"));
    830     }
    831 
    832   /* Set the target platform.  */
    833   if (strstr (target, "cygwin"))
    834     which_target = CYGWIN_TARGET;
    835   else if (strstr (target, "mingw"))
    836     which_target = MINGW_TARGET;
    837   else
    838     which_target = UNKNOWN_TARGET;
    839 
    840   if (! strncmp (target, "arm", 3))
    841     which_cpu = ARM_CPU;
    842   else if (!strncmp (target, "x86_64", 6)
    843 	   || !strncmp (target, "athlon64", 8)
    844 	   || !strncmp (target, "amd64", 5))
    845     which_cpu = X64_CPU;
    846   else if (target[0] == 'i' && (target[1] >= '3' && target[1] <= '6')
    847 	   && target[2] == '8' && target[3] == '6')
    848     which_cpu = X86_CPU;
    849   else
    850     which_cpu = UNKNOWN_CPU;
    851 
    852   if (is_leading_underscore == -1)
    853     is_leading_underscore = (which_cpu != X64_CPU && which_cpu != ARM_CPU);
    854 
    855   /* Re-create the command lines as a string, taking care to quote stuff.  */
    856   dlltool_cmdline = dyn_string_new (cmdline_len);
    857   if (verbose)
    858     dyn_string_append_cstr (dlltool_cmdline, " -v");
    859 
    860   dyn_string_append_cstr (dlltool_cmdline, " --dllname ");
    861   dyn_string_append_cstr (dlltool_cmdline, dll_name);
    862 
    863   for (i = 1; i < argc; ++i)
    864     {
    865       if (dlltool_arg_indices[i])
    866 	{
    867 	  char *arg = saved_argv[i];
    868 	  int quote = (strchr (arg, ' ') || strchr (arg, '\t'));
    869 	  dyn_string_append_cstr (dlltool_cmdline,
    870 	                     (quote) ? " \"" : " ");
    871 	  dyn_string_append_cstr (dlltool_cmdline, arg);
    872 	  dyn_string_append_cstr (dlltool_cmdline,
    873 	                     (quote) ? "\"" : "");
    874 	}
    875     }
    876 
    877   driver_cmdline = dyn_string_new (cmdline_len);
    878   if (! driver_flags || strlen (driver_flags) == 0)
    879     {
    880       switch (which_target)
    881 	{
    882 	case CYGWIN_TARGET:
    883 	  driver_flags = cygwin_driver_flags;
    884 	  break;
    885 
    886 	case MINGW_TARGET:
    887 	  driver_flags = mingw32_driver_flags;
    888 	  break;
    889 
    890 	default:
    891 	  driver_flags = generic_driver_flags;
    892 	  break;
    893 	}
    894     }
    895   dyn_string_append_cstr (driver_cmdline, driver_flags);
    896   dyn_string_append_cstr (driver_cmdline, " -o ");
    897   dyn_string_append_cstr (driver_cmdline, dll_file_name);
    898 
    899   if (is_leading_underscore == 0)
    900     dyn_string_append_cstr (driver_cmdline, " --no-leading-underscore");
    901   else if (is_leading_underscore == 1)
    902     dyn_string_append_cstr (driver_cmdline, " --leading-underscore");
    903 
    904   if (! entry_point || strlen (entry_point) == 0)
    905     {
    906       const char *prefix = (is_leading_underscore != 0 ? "_" : "");
    907       const char *postfix = "";
    908       const char *name_entry;
    909 
    910       if (which_cpu == X86_CPU || which_cpu == UNKNOWN_CPU)
    911 	postfix = "@12";
    912 
    913       switch (which_target)
    914 	{
    915 	case CYGWIN_TARGET:
    916 	  name_entry = "_cygwin_dll_entry";
    917 	  break;
    918 
    919 	case MINGW_TARGET:
    920 	  name_entry = "DllMainCRTStartup";
    921 	  break;
    922 
    923 	default:
    924 	  name_entry = "DllMain";
    925 	  break;
    926 	}
    927       entry_point =
    928 	(char *) malloc (strlen (name_entry) + strlen (prefix)
    929 			 + strlen (postfix) + 1);
    930       sprintf (entry_point, "%s%s%s", prefix, name_entry, postfix);
    931     }
    932   dyn_string_append_cstr (driver_cmdline, " -Wl,-e,");
    933   dyn_string_append_cstr (driver_cmdline, entry_point);
    934   dyn_string_append_cstr (dlltool_cmdline, " --exclude-symbol=");
    935   dyn_string_append_cstr (dlltool_cmdline,
    936                     (entry_point[0] == '_') ? entry_point+1 : entry_point);
    937 
    938   if (! image_base_str || strlen (image_base_str) == 0)
    939     {
    940       char *tmpbuf = (char *) xmalloc (sizeof ("0x12345678") + 1);
    941       unsigned long hash = strhash (dll_file_name);
    942       sprintf (tmpbuf, "0x%.8lX", 0x60000000|((hash<<16)&0xFFC0000));
    943       image_base_str = tmpbuf;
    944     }
    945 
    946   dyn_string_append_cstr (driver_cmdline, " -Wl,--image-base,");
    947   dyn_string_append_cstr (driver_cmdline, image_base_str);
    948 
    949   if (verbose)
    950     {
    951       dyn_string_append_cstr (driver_cmdline, " -v");
    952     }
    953 
    954   for (i = 1; i < argc; ++i)
    955     {
    956       if (driver_arg_indices[i])
    957 	{
    958 	  char *arg = saved_argv[i];
    959 	  int quote = (strchr (arg, ' ') || strchr (arg, '\t'));
    960 	  dyn_string_append_cstr (driver_cmdline,
    961 	                     (quote) ? " \"" : " ");
    962 	  dyn_string_append_cstr (driver_cmdline, arg);
    963 	  dyn_string_append_cstr (driver_cmdline,
    964 	                     (quote) ? "\"" : "");
    965 	}
    966     }
    967 
    968   /* Step pre-1. If no --def <EXPORT_DEF> is specified,
    969      then create it and then pass it on.  */
    970 
    971   if (! def_file_seen)
    972     {
    973       dyn_string_t step_pre1;
    974 
    975       step_pre1 = dyn_string_new (1024);
    976 
    977       dyn_string_append_cstr (step_pre1, dlltool_cmdline->s);
    978       if (export_all)
    979 	{
    980 	  dyn_string_append_cstr (step_pre1, " --export-all --exclude-symbol=");
    981 	  dyn_string_append_cstr (step_pre1,
    982 	  "_cygwin_dll_entry@12,DllMainCRTStartup@12,DllMain@12,DllEntryPoint@12");
    983 	}
    984       dyn_string_append_cstr (step_pre1, " --output-def ");
    985       dyn_string_append_cstr (step_pre1, def_file_name);
    986 
    987       for (i = 1; i < argc; ++i)
    988 	{
    989 	  if (driver_arg_indices[i])
    990 	    {
    991 	      char *arg = saved_argv[i];
    992 	      size_t len = strlen (arg);
    993 	      if (len >= 2 && arg[len-2] == '.'
    994 	          && (arg[len-1] == 'o' || arg[len-1] == 'a'))
    995 		{
    996 		  int quote = (strchr (arg, ' ') || strchr (arg, '\t'));
    997 		  dyn_string_append_cstr (step_pre1,
    998 				     (quote) ? " \"" : " ");
    999 		  dyn_string_append_cstr (step_pre1, arg);
   1000 		  dyn_string_append_cstr (step_pre1,
   1001 				     (quote) ? "\"" : "");
   1002 		}
   1003 	    }
   1004 	}
   1005 
   1006       if (run (dlltool_name, step_pre1->s))
   1007 	cleanup_and_exit (1);
   1008 
   1009       dyn_string_delete (step_pre1);
   1010     }
   1011 
   1012   dyn_string_append_cstr (dlltool_cmdline, " --def ");
   1013   dyn_string_append_cstr (dlltool_cmdline, def_file_name);
   1014 
   1015   if (verbose)
   1016     {
   1017       fprintf (stderr, _("DLLTOOL name    : %s\n"), dlltool_name);
   1018       fprintf (stderr, _("DLLTOOL options : %s\n"), dlltool_cmdline->s);
   1019       fprintf (stderr, _("DRIVER name     : %s\n"), driver_name);
   1020       fprintf (stderr, _("DRIVER options  : %s\n"), driver_cmdline->s);
   1021     }
   1022 
   1023   /* Step 1. Call GCC/LD to create base relocation file. If using GCC, the
   1024      driver command line will look like the following:
   1025 
   1026         % gcc -Wl,--dll --Wl,--base-file,foo.base [rest of command line]
   1027 
   1028      If the user does not specify a base name, create temporary one that
   1029      is deleted at exit.  */
   1030 
   1031   if (! base_file_name)
   1032     {
   1033       char *fileprefix = choose_temp_base ();
   1034       base_file_name = (char *) xmalloc (strlen (fileprefix) + 6);
   1035       sprintf (base_file_name, "%s.base",
   1036 	       (dontdeltemps) ? mybasename (fileprefix) : fileprefix);
   1037       delete_base_file = 1;
   1038       free (fileprefix);
   1039     }
   1040 
   1041   {
   1042     int quote;
   1043 
   1044     dyn_string_t step1 = dyn_string_new (driver_cmdline->length
   1045 					 + strlen (base_file_name)
   1046 					 + 20);
   1047     dyn_string_append_cstr (step1, "-Wl,--base-file,");
   1048     quote = (strchr (base_file_name, ' ')
   1049 	     || strchr (base_file_name, '\t'));
   1050     dyn_string_append_cstr (step1,
   1051 	               (quote) ? "\"" : "");
   1052     dyn_string_append_cstr (step1, base_file_name);
   1053     dyn_string_append_cstr (step1,
   1054 	               (quote) ? "\"" : "");
   1055     if (driver_cmdline->length)
   1056       {
   1057 	dyn_string_append_cstr (step1, " ");
   1058 	dyn_string_append_cstr (step1, driver_cmdline->s);
   1059       }
   1060 
   1061     if (run (driver_name, step1->s))
   1062       cleanup_and_exit (1);
   1063 
   1064     dyn_string_delete (step1);
   1065   }
   1066 
   1067   /* Step 2. generate the exp file by running dlltool.
   1068      dlltool command line will look like the following:
   1069 
   1070         % dlltool -Wl,--dll --Wl,--base-file,foo.base [rest of command line]
   1071 
   1072      If the user does not specify a base name, create temporary one that
   1073      is deleted at exit.  */
   1074 
   1075   if (! exp_file_name)
   1076     {
   1077       char *p = strrchr (dll_name, '.');
   1078       size_t prefix_len = (p) ? (size_t) (p - dll_name) : strlen (dll_name);
   1079 
   1080       exp_file_name = (char *) xmalloc (prefix_len + 4 + 1);
   1081       strncpy (exp_file_name, dll_name, prefix_len);
   1082       exp_file_name[prefix_len] = '\0';
   1083       strcat (exp_file_name, ".exp");
   1084       delete_exp_file = 1;
   1085     }
   1086 
   1087   {
   1088     int quote;
   1089 
   1090     dyn_string_t step2 = dyn_string_new (dlltool_cmdline->length
   1091 					 + strlen (base_file_name)
   1092 					 + strlen (exp_file_name)
   1093 				         + 20);
   1094 
   1095     dyn_string_append_cstr (step2, "--base-file ");
   1096     quote = (strchr (base_file_name, ' ')
   1097 	     || strchr (base_file_name, '\t'));
   1098     dyn_string_append_cstr (step2,
   1099 	               (quote) ? "\"" : "");
   1100     dyn_string_append_cstr (step2, base_file_name);
   1101     dyn_string_append_cstr (step2,
   1102 	               (quote) ? "\" " : " ");
   1103 
   1104     dyn_string_append_cstr (step2, "--output-exp ");
   1105     quote = (strchr (exp_file_name, ' ')
   1106 	     || strchr (exp_file_name, '\t'));
   1107     dyn_string_append_cstr (step2,
   1108 	               (quote) ? "\"" : "");
   1109     dyn_string_append_cstr (step2, exp_file_name);
   1110     dyn_string_append_cstr (step2,
   1111 	               (quote) ? "\"" : "");
   1112 
   1113     if (dlltool_cmdline->length)
   1114       {
   1115 	dyn_string_append_cstr (step2, " ");
   1116 	dyn_string_append_cstr (step2, dlltool_cmdline->s);
   1117       }
   1118 
   1119     if (run (dlltool_name, step2->s))
   1120       cleanup_and_exit (1);
   1121 
   1122     dyn_string_delete (step2);
   1123   }
   1124 
   1125   /*
   1126    * Step 3. Call GCC/LD to again, adding the exp file this time.
   1127    * driver command line will look like the following:
   1128    *
   1129    *    % gcc -Wl,--dll --Wl,--base-file,foo.base foo.exp [rest ...]
   1130    */
   1131 
   1132   {
   1133     int quote;
   1134 
   1135     dyn_string_t step3 = dyn_string_new (driver_cmdline->length
   1136 					 + strlen (exp_file_name)
   1137 					 + strlen (base_file_name)
   1138 				         + 20);
   1139     dyn_string_append_cstr (step3, "-Wl,--base-file,");
   1140     quote = (strchr (base_file_name, ' ')
   1141 	     || strchr (base_file_name, '\t'));
   1142     dyn_string_append_cstr (step3,
   1143 	               (quote) ? "\"" : "");
   1144     dyn_string_append_cstr (step3, base_file_name);
   1145     dyn_string_append_cstr (step3,
   1146 	               (quote) ? "\" " : " ");
   1147 
   1148     quote = (strchr (exp_file_name, ' ')
   1149 	     || strchr (exp_file_name, '\t'));
   1150     dyn_string_append_cstr (step3,
   1151 	               (quote) ? "\"" : "");
   1152     dyn_string_append_cstr (step3, exp_file_name);
   1153     dyn_string_append_cstr (step3,
   1154 	               (quote) ? "\"" : "");
   1155 
   1156     if (driver_cmdline->length)
   1157       {
   1158 	dyn_string_append_cstr (step3, " ");
   1159 	dyn_string_append_cstr (step3, driver_cmdline->s);
   1160       }
   1161 
   1162     if (run (driver_name, step3->s))
   1163       cleanup_and_exit (1);
   1164 
   1165     dyn_string_delete (step3);
   1166   }
   1167 
   1168 
   1169   /*
   1170    * Step 4. Run DLLTOOL again using the same command line.
   1171    */
   1172 
   1173   {
   1174     int quote;
   1175     dyn_string_t step4 = dyn_string_new (dlltool_cmdline->length
   1176 					 + strlen (base_file_name)
   1177 					 + strlen (exp_file_name)
   1178 				         + 20);
   1179 
   1180     dyn_string_append_cstr (step4, "--base-file ");
   1181     quote = (strchr (base_file_name, ' ')
   1182 	     || strchr (base_file_name, '\t'));
   1183     dyn_string_append_cstr (step4,
   1184 	               (quote) ? "\"" : "");
   1185     dyn_string_append_cstr (step4, base_file_name);
   1186     dyn_string_append_cstr (step4,
   1187 	               (quote) ? "\" " : " ");
   1188 
   1189     dyn_string_append_cstr (step4, "--output-exp ");
   1190     quote = (strchr (exp_file_name, ' ')
   1191 	     || strchr (exp_file_name, '\t'));
   1192     dyn_string_append_cstr (step4,
   1193 	               (quote) ? "\"" : "");
   1194     dyn_string_append_cstr (step4, exp_file_name);
   1195     dyn_string_append_cstr (step4,
   1196 	               (quote) ? "\"" : "");
   1197 
   1198     if (dlltool_cmdline->length)
   1199       {
   1200 	dyn_string_append_cstr (step4, " ");
   1201 	dyn_string_append_cstr (step4, dlltool_cmdline->s);
   1202       }
   1203 
   1204     if (output_lib_file_name)
   1205       {
   1206 	dyn_string_append_cstr (step4, " --output-lib ");
   1207 	dyn_string_append_cstr (step4, output_lib_file_name);
   1208       }
   1209 
   1210     if (run (dlltool_name, step4->s))
   1211       cleanup_and_exit (1);
   1212 
   1213     dyn_string_delete (step4);
   1214   }
   1215 
   1216 
   1217   /*
   1218    * Step 5. Link it all together and be done with it.
   1219    * driver command line will look like the following:
   1220    *
   1221    *    % gcc -Wl,--dll foo.exp [rest ...]
   1222    *
   1223    */
   1224 
   1225   {
   1226     int quote;
   1227 
   1228     dyn_string_t step5 = dyn_string_new (driver_cmdline->length
   1229 					 + strlen (exp_file_name)
   1230 				         + 20);
   1231     quote = (strchr (exp_file_name, ' ')
   1232 	     || strchr (exp_file_name, '\t'));
   1233     dyn_string_append_cstr (step5,
   1234 	               (quote) ? "\"" : "");
   1235     dyn_string_append_cstr (step5, exp_file_name);
   1236     dyn_string_append_cstr (step5,
   1237 	               (quote) ? "\"" : "");
   1238 
   1239     if (driver_cmdline->length)
   1240       {
   1241 	dyn_string_append_cstr (step5, " ");
   1242 	dyn_string_append_cstr (step5, driver_cmdline->s);
   1243       }
   1244 
   1245     if (run (driver_name, step5->s))
   1246       cleanup_and_exit (1);
   1247 
   1248     dyn_string_delete (step5);
   1249   }
   1250 
   1251   cleanup_and_exit (0);
   1252 
   1253   return 0;
   1254 }
   1255