Home | History | Annotate | Download | only in binutils
      1 /* dllwrap.c -- wrapper for DLLTOOL and GCC to generate PE style DLLs
      2    Copyright (C) 1998-2014 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 = alloca (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 
    396   if (pid == -1)
    397     {
    398       int errno_val = errno;
    399 
    400       fprintf (stderr, "%s: ", prog_name);
    401       fprintf (stderr, errmsg_fmt, errmsg_arg);
    402       fprintf (stderr, ": %s\n", strerror (errno_val));
    403       return 1;
    404     }
    405 
    406   retcode = 0;
    407   pid = pwait (pid, &wait_status, 0);
    408   if (pid == -1)
    409     {
    410       warn (_("pwait returns: %s"), strerror (errno));
    411       retcode = 1;
    412     }
    413   else if (WIFSIGNALED (wait_status))
    414     {
    415       warn (_("subprocess got fatal signal %d"), WTERMSIG (wait_status));
    416       retcode = 1;
    417     }
    418   else if (WIFEXITED (wait_status))
    419     {
    420       if (WEXITSTATUS (wait_status) != 0)
    421 	{
    422 	  warn (_("%s exited with status %d"), what, WEXITSTATUS (wait_status));
    423 	  retcode = 1;
    424 	}
    425     }
    426   else
    427     retcode = 1;
    428 
    429   return retcode;
    430 }
    431 
    432 static char *
    433 mybasename (const char *name)
    434 {
    435   const char *base = name;
    436 
    437   while (*name)
    438     {
    439       if (*name == '/' || *name == '\\')
    440 	{
    441 	  base = name + 1;
    442 	}
    443       ++name;
    444     }
    445   return (char *) base;
    446 }
    447 
    448 static int
    449 strhash (const char *str)
    450 {
    451   const unsigned char *s;
    452   unsigned long hash;
    453   unsigned int c;
    454   unsigned int len;
    455 
    456   hash = 0;
    457   len = 0;
    458   s = (const unsigned char *) str;
    459   while ((c = *s++) != '\0')
    460     {
    461       hash += c + (c << 17);
    462       hash ^= hash >> 2;
    463       ++len;
    464     }
    465   hash += len + (len << 17);
    466   hash ^= hash >> 2;
    467 
    468   return hash;
    469 }
    470 
    471 /**********************************************************************/
    472 
    473 static void
    474 usage (FILE *file, int status)
    475 {
    476   fprintf (file, _("Usage %s <option(s)> <object-file(s)>\n"), prog_name);
    477   fprintf (file, _("  Generic options:\n"));
    478   fprintf (file, _("   @<file>                Read options from <file>\n"));
    479   fprintf (file, _("   --quiet, -q            Work quietly\n"));
    480   fprintf (file, _("   --verbose, -v          Verbose\n"));
    481   fprintf (file, _("   --version              Print dllwrap version\n"));
    482   fprintf (file, _("   --implib <outname>     Synonym for --output-lib\n"));
    483   fprintf (file, _("  Options for %s:\n"), prog_name);
    484   fprintf (file, _("   --driver-name <driver> Defaults to \"gcc\"\n"));
    485   fprintf (file, _("   --driver-flags <flags> Override default ld flags\n"));
    486   fprintf (file, _("   --dlltool-name <dlltool> Defaults to \"dlltool\"\n"));
    487   fprintf (file, _("   --entry <entry>        Specify alternate DLL entry point\n"));
    488   fprintf (file, _("   --image-base <base>    Specify image base address\n"));
    489   fprintf (file, _("   --target <machine>     i386-cygwin32 or i386-mingw32\n"));
    490   fprintf (file, _("   --dry-run              Show what needs to be run\n"));
    491   fprintf (file, _("   --mno-cygwin           Create Mingw DLL\n"));
    492   fprintf (file, _("  Options passed to DLLTOOL:\n"));
    493   fprintf (file, _("   --machine <machine>\n"));
    494   fprintf (file, _("   --output-exp <outname> Generate export file.\n"));
    495   fprintf (file, _("   --output-lib <outname> Generate input library.\n"));
    496   fprintf (file, _("   --add-indirect         Add dll indirects to export file.\n"));
    497   fprintf (file, _("   --dllname <name>       Name of input dll to put into output lib.\n"));
    498   fprintf (file, _("   --def <deffile>        Name input .def file\n"));
    499   fprintf (file, _("   --output-def <deffile> Name output .def file\n"));
    500   fprintf (file, _("   --export-all-symbols     Export all symbols to .def\n"));
    501   fprintf (file, _("   --no-export-all-symbols  Only export .drectve symbols\n"));
    502   fprintf (file, _("   --exclude-symbols <list> Exclude <list> from .def\n"));
    503   fprintf (file, _("   --no-default-excludes    Zap default exclude symbols\n"));
    504   fprintf (file, _("   --base-file <basefile> Read linker generated base file\n"));
    505   fprintf (file, _("   --no-idata4           Don't generate idata$4 section\n"));
    506   fprintf (file, _("   --no-idata5           Don't generate idata$5 section\n"));
    507   fprintf (file, _("   -U                     Add underscores to .lib\n"));
    508   fprintf (file, _("   -k                     Kill @<n> from exported names\n"));
    509   fprintf (file, _("   --add-stdcall-alias    Add aliases without @<n>\n"));
    510   fprintf (file, _("   --as <name>            Use <name> for assembler\n"));
    511   fprintf (file, _("   --nodelete             Keep temp files.\n"));
    512   fprintf (file, _("   --no-leading-underscore  Entrypoint without underscore\n"));
    513   fprintf (file, _("   --leading-underscore     Entrypoint with underscore.\n"));
    514   fprintf (file, _("  Rest are passed unmodified to the language driver\n"));
    515   fprintf (file, "\n\n");
    516   if (REPORT_BUGS_TO[0] && status == 0)
    517     fprintf (file, _("Report bugs to %s\n"), REPORT_BUGS_TO);
    518   exit (status);
    519 }
    520 
    521 #define OPTION_START		149
    522 
    523 /* GENERIC options.  */
    524 #define OPTION_QUIET		(OPTION_START + 1)
    525 #define OPTION_VERBOSE		(OPTION_QUIET + 1)
    526 #define OPTION_VERSION		(OPTION_VERBOSE + 1)
    527 
    528 /* DLLWRAP options.  */
    529 #define OPTION_DRY_RUN		(OPTION_VERSION + 1)
    530 #define OPTION_DRIVER_NAME	(OPTION_DRY_RUN + 1)
    531 #define OPTION_DRIVER_FLAGS	(OPTION_DRIVER_NAME + 1)
    532 #define OPTION_DLLTOOL_NAME	(OPTION_DRIVER_FLAGS + 1)
    533 #define OPTION_ENTRY		(OPTION_DLLTOOL_NAME + 1)
    534 #define OPTION_IMAGE_BASE	(OPTION_ENTRY + 1)
    535 #define OPTION_TARGET		(OPTION_IMAGE_BASE + 1)
    536 #define OPTION_MNO_CYGWIN	(OPTION_TARGET + 1)
    537 #define OPTION_NO_LEADING_UNDERSCORE (OPTION_MNO_CYGWIN + 1)
    538 #define OPTION_LEADING_UNDERSCORE (OPTION_NO_LEADING_UNDERSCORE + 1)
    539 
    540 /* DLLTOOL options.  */
    541 #define OPTION_NODELETE		(OPTION_LEADING_UNDERSCORE + 1)
    542 #define OPTION_DLLNAME		(OPTION_NODELETE + 1)
    543 #define OPTION_NO_IDATA4	(OPTION_DLLNAME + 1)
    544 #define OPTION_NO_IDATA5	(OPTION_NO_IDATA4 + 1)
    545 #define OPTION_OUTPUT_EXP	(OPTION_NO_IDATA5 + 1)
    546 #define OPTION_OUTPUT_DEF	(OPTION_OUTPUT_EXP + 1)
    547 #define OPTION_EXPORT_ALL_SYMS	(OPTION_OUTPUT_DEF + 1)
    548 #define OPTION_NO_EXPORT_ALL_SYMS (OPTION_EXPORT_ALL_SYMS + 1)
    549 #define OPTION_EXCLUDE_SYMS	(OPTION_NO_EXPORT_ALL_SYMS + 1)
    550 #define OPTION_NO_DEFAULT_EXCLUDES (OPTION_EXCLUDE_SYMS + 1)
    551 #define OPTION_OUTPUT_LIB	(OPTION_NO_DEFAULT_EXCLUDES + 1)
    552 #define OPTION_DEF		(OPTION_OUTPUT_LIB + 1)
    553 #define OPTION_ADD_UNDERSCORE	(OPTION_DEF + 1)
    554 #define OPTION_KILLAT		(OPTION_ADD_UNDERSCORE + 1)
    555 #define OPTION_HELP		(OPTION_KILLAT + 1)
    556 #define OPTION_MACHINE		(OPTION_HELP + 1)
    557 #define OPTION_ADD_INDIRECT	(OPTION_MACHINE + 1)
    558 #define OPTION_BASE_FILE	(OPTION_ADD_INDIRECT + 1)
    559 #define OPTION_AS		(OPTION_BASE_FILE + 1)
    560 
    561 static const struct option long_options[] =
    562 {
    563   /* generic options.  */
    564   {"quiet", no_argument, NULL, 'q'},
    565   {"verbose", no_argument, NULL, 'v'},
    566   {"version", no_argument, NULL, OPTION_VERSION},
    567   {"implib", required_argument, NULL, OPTION_OUTPUT_LIB},
    568 
    569   /* dllwrap options.  */
    570   {"dry-run", no_argument, NULL, OPTION_DRY_RUN},
    571   {"driver-name", required_argument, NULL, OPTION_DRIVER_NAME},
    572   {"driver-flags", required_argument, NULL, OPTION_DRIVER_FLAGS},
    573   {"dlltool-name", required_argument, NULL, OPTION_DLLTOOL_NAME},
    574   {"entry", required_argument, NULL, 'e'},
    575   {"image-base", required_argument, NULL, OPTION_IMAGE_BASE},
    576   {"target", required_argument, NULL, OPTION_TARGET},
    577   {"no-leading-underscore", no_argument, NULL, OPTION_NO_LEADING_UNDERSCORE},
    578   {"leading-underscore", no_argument, NULL, OPTION_NO_LEADING_UNDERSCORE},
    579 
    580   /* dlltool options.  */
    581   {"no-delete", no_argument, NULL, 'n'},
    582   {"dllname", required_argument, NULL, OPTION_DLLNAME},
    583   {"no-idata4", no_argument, NULL, OPTION_NO_IDATA4},
    584   {"no-idata5", no_argument, NULL, OPTION_NO_IDATA5},
    585   {"output-exp", required_argument, NULL, OPTION_OUTPUT_EXP},
    586   {"output-def", required_argument, NULL, OPTION_OUTPUT_DEF},
    587   {"export-all-symbols", no_argument, NULL, OPTION_EXPORT_ALL_SYMS},
    588   {"no-export-all-symbols", no_argument, NULL, OPTION_NO_EXPORT_ALL_SYMS},
    589   {"exclude-symbols", required_argument, NULL, OPTION_EXCLUDE_SYMS},
    590   {"no-default-excludes", no_argument, NULL, OPTION_NO_DEFAULT_EXCLUDES},
    591   {"output-lib", required_argument, NULL, OPTION_OUTPUT_LIB},
    592   {"def", required_argument, NULL, OPTION_DEF},
    593   {"add-underscore", no_argument, NULL, 'U'},
    594   {"killat", no_argument, NULL, 'k'},
    595   {"add-stdcall-alias", no_argument, NULL, 'A'},
    596   {"help", no_argument, NULL, 'h'},
    597   {"machine", required_argument, NULL, OPTION_MACHINE},
    598   {"add-indirect", no_argument, NULL, OPTION_ADD_INDIRECT},
    599   {"base-file", required_argument, NULL, OPTION_BASE_FILE},
    600   {"as", required_argument, NULL, OPTION_AS},
    601   {0, 0, 0, 0}
    602 };
    603 
    604 int main (int, char **);
    605 
    606 int
    607 main (int argc, char **argv)
    608 {
    609   int c;
    610   int i;
    611 
    612   char **saved_argv = 0;
    613   int cmdline_len = 0;
    614 
    615   int export_all = 0;
    616 
    617   int *dlltool_arg_indices;
    618   int *driver_arg_indices;
    619 
    620   char *driver_flags = 0;
    621   char *output_lib_file_name = 0;
    622 
    623   dyn_string_t dlltool_cmdline;
    624   dyn_string_t driver_cmdline;
    625 
    626   int def_file_seen = 0;
    627 
    628   char *image_base_str = 0;
    629 
    630   prog_name = argv[0];
    631 
    632 #if defined (HAVE_SETLOCALE) && defined (HAVE_LC_MESSAGES)
    633   setlocale (LC_MESSAGES, "");
    634 #endif
    635 #if defined (HAVE_SETLOCALE)
    636   setlocale (LC_CTYPE, "");
    637 #endif
    638   bindtextdomain (PACKAGE, LOCALEDIR);
    639   textdomain (PACKAGE);
    640 
    641   expandargv (&argc, &argv);
    642 
    643   saved_argv = (char **) xmalloc (argc * sizeof (char*));
    644   dlltool_arg_indices = (int *) xmalloc (argc * sizeof (int));
    645   driver_arg_indices = (int *) xmalloc (argc * sizeof (int));
    646   for (i = 0; i < argc; ++i)
    647     {
    648       size_t len = strlen (argv[i]);
    649       char *arg = (char *) xmalloc (len + 1);
    650       strcpy (arg, argv[i]);
    651       cmdline_len += len;
    652       saved_argv[i] = arg;
    653       dlltool_arg_indices[i] = 0;
    654       driver_arg_indices[i] = 1;
    655     }
    656   cmdline_len++;
    657 
    658   /* We recognize dllwrap and dlltool options, and everything else is
    659      passed onto the language driver (eg., to GCC). We collect options
    660      to dlltool and driver in dlltool_args and driver_args.  */
    661 
    662   opterr = 0;
    663   while ((c = getopt_long_only (argc, argv, "nkAqve:Uho:l:L:I:",
    664 				long_options, (int *) 0)) != EOF)
    665     {
    666       int dlltool_arg;
    667       int driver_arg;
    668       int single_word_option_value_pair;
    669 
    670       dlltool_arg = 0;
    671       driver_arg = 1;
    672       single_word_option_value_pair = 0;
    673 
    674       if (c != '?')
    675 	{
    676 	  /* We recognize this option, so it has to be either dllwrap or
    677 	     dlltool option. Do not pass to driver unless it's one of the
    678 	     generic options that are passed to all the tools (such as -v)
    679 	     which are dealt with later.  */
    680 	  driver_arg = 0;
    681 	}
    682 
    683       /* deal with generic and dllwrap options first.  */
    684       switch (c)
    685 	{
    686 	case 'h':
    687 	  usage (stdout, 0);
    688 	  break;
    689 	case 'q':
    690 	  verbose = 0;
    691 	  break;
    692 	case 'v':
    693 	  verbose = 1;
    694 	  break;
    695 	case OPTION_VERSION:
    696 	  print_version (prog_name);
    697 	  break;
    698 	case 'e':
    699 	  entry_point = optarg;
    700 	  break;
    701 	case OPTION_IMAGE_BASE:
    702 	  image_base_str = optarg;
    703 	  break;
    704 	case OPTION_DEF:
    705 	  def_file_name = optarg;
    706 	  def_file_seen = 1;
    707 	  delete_def_file = 0;
    708 	  break;
    709 	case 'n':
    710 	  dontdeltemps = 1;
    711 	  dlltool_arg = 1;
    712 	  break;
    713 	case 'o':
    714 	  dll_file_name = optarg;
    715 	  break;
    716 	case 'I':
    717 	case 'l':
    718 	case 'L':
    719 	  driver_arg = 1;
    720 	  break;
    721 	case OPTION_DLLNAME:
    722 	  dll_name = optarg;
    723 	  break;
    724 	case OPTION_DRY_RUN:
    725 	  dry_run = 1;
    726 	  break;
    727 	case OPTION_DRIVER_NAME:
    728 	  driver_name = optarg;
    729 	  break;
    730 	case OPTION_DRIVER_FLAGS:
    731 	  driver_flags = optarg;
    732 	  break;
    733 	case OPTION_DLLTOOL_NAME:
    734 	  dlltool_name = optarg;
    735 	  break;
    736 	case OPTION_TARGET:
    737 	  target = optarg;
    738 	  break;
    739 	case OPTION_MNO_CYGWIN:
    740 	  target = "i386-mingw32";
    741 	  break;
    742 	case OPTION_NO_LEADING_UNDERSCORE:
    743 	  is_leading_underscore = 0;
    744 	  break;
    745 	case OPTION_LEADING_UNDERSCORE:
    746 	  is_leading_underscore = 1;
    747 	  break;
    748 	case OPTION_BASE_FILE:
    749 	  base_file_name = optarg;
    750 	  delete_base_file = 0;
    751 	  break;
    752 	case OPTION_OUTPUT_EXP:
    753 	  exp_file_name = optarg;
    754 	  delete_exp_file = 0;
    755 	  break;
    756 	case OPTION_EXPORT_ALL_SYMS:
    757 	  export_all = 1;
    758 	  break;
    759 	case OPTION_OUTPUT_LIB:
    760 	  output_lib_file_name = optarg;
    761 	  break;
    762 	case '?':
    763 	  break;
    764 	default:
    765 	  dlltool_arg = 1;
    766 	  break;
    767 	}
    768 
    769       /* Handle passing through --option=value case.  */
    770       if (optarg
    771 	  && saved_argv[optind-1][0] == '-'
    772 	  && saved_argv[optind-1][1] == '-'
    773 	  && strchr (saved_argv[optind-1], '='))
    774 	single_word_option_value_pair = 1;
    775 
    776       if (dlltool_arg)
    777 	{
    778 	  dlltool_arg_indices[optind-1] = 1;
    779 	  if (optarg && ! single_word_option_value_pair)
    780 	    {
    781 	      dlltool_arg_indices[optind-2] = 1;
    782 	    }
    783 	}
    784 
    785       if (! driver_arg)
    786 	{
    787 	  driver_arg_indices[optind-1] = 0;
    788 	  if (optarg && ! single_word_option_value_pair)
    789 	    {
    790 	      driver_arg_indices[optind-2] = 0;
    791 	    }
    792 	}
    793     }
    794 
    795   /* Sanity checks.  */
    796   if (! dll_name && ! dll_file_name)
    797     {
    798       warn (_("Must provide at least one of -o or --dllname options"));
    799       exit (1);
    800     }
    801   else if (! dll_name)
    802     {
    803       dll_name = xstrdup (mybasename (dll_file_name));
    804     }
    805   else if (! dll_file_name)
    806     {
    807       dll_file_name = xstrdup (dll_name);
    808     }
    809 
    810   /* Deduce driver-name and dlltool-name from our own.  */
    811   if (driver_name == NULL)
    812     driver_name = deduce_name ("gcc");
    813 
    814   if (dlltool_name == NULL)
    815     dlltool_name = deduce_name ("dlltool");
    816 
    817   if (! def_file_seen)
    818     {
    819       char *fileprefix = choose_temp_base ();
    820 
    821       def_file_name = (char *) xmalloc (strlen (fileprefix) + 5);
    822       sprintf (def_file_name, "%s.def",
    823 	       (dontdeltemps) ? mybasename (fileprefix) : fileprefix);
    824       delete_def_file = 1;
    825       free (fileprefix);
    826       delete_def_file = 1;
    827       warn (_("no export definition file provided.\n\
    828 Creating one, but that may not be what you want"));
    829     }
    830 
    831   /* Set the target platform.  */
    832   if (strstr (target, "cygwin"))
    833     which_target = CYGWIN_TARGET;
    834   else if (strstr (target, "mingw"))
    835     which_target = MINGW_TARGET;
    836   else
    837     which_target = UNKNOWN_TARGET;
    838 
    839   if (! strncmp (target, "arm", 3))
    840     which_cpu = ARM_CPU;
    841   else if (!strncmp (target, "x86_64", 6)
    842 	   || !strncmp (target, "athlon64", 8)
    843 	   || !strncmp (target, "amd64", 5))
    844     which_cpu = X64_CPU;
    845   else if (target[0] == 'i' && (target[1] >= '3' && target[1] <= '6')
    846 	   && target[2] == '8' && target[3] == '6')
    847     which_cpu = X86_CPU;
    848   else
    849     which_cpu = UNKNOWN_CPU;
    850 
    851   if (is_leading_underscore == -1)
    852     is_leading_underscore = (which_cpu != X64_CPU && which_cpu != ARM_CPU);
    853 
    854   /* Re-create the command lines as a string, taking care to quote stuff.  */
    855   dlltool_cmdline = dyn_string_new (cmdline_len);
    856   if (verbose)
    857     dyn_string_append_cstr (dlltool_cmdline, " -v");
    858 
    859   dyn_string_append_cstr (dlltool_cmdline, " --dllname ");
    860   dyn_string_append_cstr (dlltool_cmdline, dll_name);
    861 
    862   for (i = 1; i < argc; ++i)
    863     {
    864       if (dlltool_arg_indices[i])
    865 	{
    866 	  char *arg = saved_argv[i];
    867 	  int quote = (strchr (arg, ' ') || strchr (arg, '\t'));
    868 	  dyn_string_append_cstr (dlltool_cmdline,
    869 	                     (quote) ? " \"" : " ");
    870 	  dyn_string_append_cstr (dlltool_cmdline, arg);
    871 	  dyn_string_append_cstr (dlltool_cmdline,
    872 	                     (quote) ? "\"" : "");
    873 	}
    874     }
    875 
    876   driver_cmdline = dyn_string_new (cmdline_len);
    877   if (! driver_flags || strlen (driver_flags) == 0)
    878     {
    879       switch (which_target)
    880 	{
    881 	case CYGWIN_TARGET:
    882 	  driver_flags = cygwin_driver_flags;
    883 	  break;
    884 
    885 	case MINGW_TARGET:
    886 	  driver_flags = mingw32_driver_flags;
    887 	  break;
    888 
    889 	default:
    890 	  driver_flags = generic_driver_flags;
    891 	  break;
    892 	}
    893     }
    894   dyn_string_append_cstr (driver_cmdline, driver_flags);
    895   dyn_string_append_cstr (driver_cmdline, " -o ");
    896   dyn_string_append_cstr (driver_cmdline, dll_file_name);
    897 
    898   if (is_leading_underscore == 0)
    899     dyn_string_append_cstr (driver_cmdline, " --no-leading-underscore");
    900   else if (is_leading_underscore == 1)
    901     dyn_string_append_cstr (driver_cmdline, " --leading-underscore");
    902 
    903   if (! entry_point || strlen (entry_point) == 0)
    904     {
    905       const char *prefix = (is_leading_underscore != 0 ? "_" : "");
    906       const char *postfix = "";
    907       const char *name_entry;
    908 
    909       if (which_cpu == X86_CPU || which_cpu == UNKNOWN_CPU)
    910 	postfix = "@12";
    911 
    912       switch (which_target)
    913 	{
    914 	case CYGWIN_TARGET:
    915 	  name_entry = "_cygwin_dll_entry";
    916 	  break;
    917 
    918 	case MINGW_TARGET:
    919 	  name_entry = "DllMainCRTStartup";
    920 	  break;
    921 
    922 	default:
    923 	  name_entry = "DllMain";
    924 	  break;
    925 	}
    926       entry_point =
    927 	(char *) malloc (strlen (name_entry) + strlen (prefix)
    928 			 + strlen (postfix) + 1);
    929       sprintf (entry_point, "%s%s%s", prefix, name_entry, postfix);
    930     }
    931   dyn_string_append_cstr (driver_cmdline, " -Wl,-e,");
    932   dyn_string_append_cstr (driver_cmdline, entry_point);
    933   dyn_string_append_cstr (dlltool_cmdline, " --exclude-symbol=");
    934   dyn_string_append_cstr (dlltool_cmdline,
    935                     (entry_point[0] == '_') ? entry_point+1 : entry_point);
    936 
    937   if (! image_base_str || strlen (image_base_str) == 0)
    938     {
    939       char *tmpbuf = (char *) xmalloc (sizeof ("0x12345678") + 1);
    940       unsigned long hash = strhash (dll_file_name);
    941       sprintf (tmpbuf, "0x%.8lX", 0x60000000|((hash<<16)&0xFFC0000));
    942       image_base_str = tmpbuf;
    943     }
    944 
    945   dyn_string_append_cstr (driver_cmdline, " -Wl,--image-base,");
    946   dyn_string_append_cstr (driver_cmdline, image_base_str);
    947 
    948   if (verbose)
    949     {
    950       dyn_string_append_cstr (driver_cmdline, " -v");
    951     }
    952 
    953   for (i = 1; i < argc; ++i)
    954     {
    955       if (driver_arg_indices[i])
    956 	{
    957 	  char *arg = saved_argv[i];
    958 	  int quote = (strchr (arg, ' ') || strchr (arg, '\t'));
    959 	  dyn_string_append_cstr (driver_cmdline,
    960 	                     (quote) ? " \"" : " ");
    961 	  dyn_string_append_cstr (driver_cmdline, arg);
    962 	  dyn_string_append_cstr (driver_cmdline,
    963 	                     (quote) ? "\"" : "");
    964 	}
    965     }
    966 
    967   /* Step pre-1. If no --def <EXPORT_DEF> is specified,
    968      then create it and then pass it on.  */
    969 
    970   if (! def_file_seen)
    971     {
    972       dyn_string_t step_pre1;
    973 
    974       step_pre1 = dyn_string_new (1024);
    975 
    976       dyn_string_append_cstr (step_pre1, dlltool_cmdline->s);
    977       if (export_all)
    978 	{
    979 	  dyn_string_append_cstr (step_pre1, " --export-all --exclude-symbol=");
    980 	  dyn_string_append_cstr (step_pre1,
    981 	  "_cygwin_dll_entry@12,DllMainCRTStartup@12,DllMain@12,DllEntryPoint@12");
    982 	}
    983       dyn_string_append_cstr (step_pre1, " --output-def ");
    984       dyn_string_append_cstr (step_pre1, def_file_name);
    985 
    986       for (i = 1; i < argc; ++i)
    987 	{
    988 	  if (driver_arg_indices[i])
    989 	    {
    990 	      char *arg = saved_argv[i];
    991 	      size_t len = strlen (arg);
    992 	      if (len >= 2 && arg[len-2] == '.'
    993 	          && (arg[len-1] == 'o' || arg[len-1] == 'a'))
    994 		{
    995 		  int quote = (strchr (arg, ' ') || strchr (arg, '\t'));
    996 		  dyn_string_append_cstr (step_pre1,
    997 				     (quote) ? " \"" : " ");
    998 		  dyn_string_append_cstr (step_pre1, arg);
    999 		  dyn_string_append_cstr (step_pre1,
   1000 				     (quote) ? "\"" : "");
   1001 		}
   1002 	    }
   1003 	}
   1004 
   1005       if (run (dlltool_name, step_pre1->s))
   1006 	cleanup_and_exit (1);
   1007 
   1008       dyn_string_delete (step_pre1);
   1009     }
   1010 
   1011   dyn_string_append_cstr (dlltool_cmdline, " --def ");
   1012   dyn_string_append_cstr (dlltool_cmdline, def_file_name);
   1013 
   1014   if (verbose)
   1015     {
   1016       fprintf (stderr, _("DLLTOOL name    : %s\n"), dlltool_name);
   1017       fprintf (stderr, _("DLLTOOL options : %s\n"), dlltool_cmdline->s);
   1018       fprintf (stderr, _("DRIVER name     : %s\n"), driver_name);
   1019       fprintf (stderr, _("DRIVER options  : %s\n"), driver_cmdline->s);
   1020     }
   1021 
   1022   /* Step 1. Call GCC/LD to create base relocation file. If using GCC, the
   1023      driver command line will look like the following:
   1024 
   1025         % gcc -Wl,--dll --Wl,--base-file,foo.base [rest of command line]
   1026 
   1027      If the user does not specify a base name, create temporary one that
   1028      is deleted at exit.  */
   1029 
   1030   if (! base_file_name)
   1031     {
   1032       char *fileprefix = choose_temp_base ();
   1033       base_file_name = (char *) xmalloc (strlen (fileprefix) + 6);
   1034       sprintf (base_file_name, "%s.base",
   1035 	       (dontdeltemps) ? mybasename (fileprefix) : fileprefix);
   1036       delete_base_file = 1;
   1037       free (fileprefix);
   1038     }
   1039 
   1040   {
   1041     int quote;
   1042 
   1043     dyn_string_t step1 = dyn_string_new (driver_cmdline->length
   1044 					 + strlen (base_file_name)
   1045 					 + 20);
   1046     dyn_string_append_cstr (step1, "-Wl,--base-file,");
   1047     quote = (strchr (base_file_name, ' ')
   1048 	     || strchr (base_file_name, '\t'));
   1049     dyn_string_append_cstr (step1,
   1050 	               (quote) ? "\"" : "");
   1051     dyn_string_append_cstr (step1, base_file_name);
   1052     dyn_string_append_cstr (step1,
   1053 	               (quote) ? "\"" : "");
   1054     if (driver_cmdline->length)
   1055       {
   1056 	dyn_string_append_cstr (step1, " ");
   1057 	dyn_string_append_cstr (step1, driver_cmdline->s);
   1058       }
   1059 
   1060     if (run (driver_name, step1->s))
   1061       cleanup_and_exit (1);
   1062 
   1063     dyn_string_delete (step1);
   1064   }
   1065 
   1066   /* Step 2. generate the exp file by running dlltool.
   1067      dlltool command line will look like the following:
   1068 
   1069         % dlltool -Wl,--dll --Wl,--base-file,foo.base [rest of command line]
   1070 
   1071      If the user does not specify a base name, create temporary one that
   1072      is deleted at exit.  */
   1073 
   1074   if (! exp_file_name)
   1075     {
   1076       char *p = strrchr (dll_name, '.');
   1077       size_t prefix_len = (p) ? (size_t) (p - dll_name) : strlen (dll_name);
   1078 
   1079       exp_file_name = (char *) xmalloc (prefix_len + 4 + 1);
   1080       strncpy (exp_file_name, dll_name, prefix_len);
   1081       exp_file_name[prefix_len] = '\0';
   1082       strcat (exp_file_name, ".exp");
   1083       delete_exp_file = 1;
   1084     }
   1085 
   1086   {
   1087     int quote;
   1088 
   1089     dyn_string_t step2 = dyn_string_new (dlltool_cmdline->length
   1090 					 + strlen (base_file_name)
   1091 					 + strlen (exp_file_name)
   1092 				         + 20);
   1093 
   1094     dyn_string_append_cstr (step2, "--base-file ");
   1095     quote = (strchr (base_file_name, ' ')
   1096 	     || strchr (base_file_name, '\t'));
   1097     dyn_string_append_cstr (step2,
   1098 	               (quote) ? "\"" : "");
   1099     dyn_string_append_cstr (step2, base_file_name);
   1100     dyn_string_append_cstr (step2,
   1101 	               (quote) ? "\" " : " ");
   1102 
   1103     dyn_string_append_cstr (step2, "--output-exp ");
   1104     quote = (strchr (exp_file_name, ' ')
   1105 	     || strchr (exp_file_name, '\t'));
   1106     dyn_string_append_cstr (step2,
   1107 	               (quote) ? "\"" : "");
   1108     dyn_string_append_cstr (step2, exp_file_name);
   1109     dyn_string_append_cstr (step2,
   1110 	               (quote) ? "\"" : "");
   1111 
   1112     if (dlltool_cmdline->length)
   1113       {
   1114 	dyn_string_append_cstr (step2, " ");
   1115 	dyn_string_append_cstr (step2, dlltool_cmdline->s);
   1116       }
   1117 
   1118     if (run (dlltool_name, step2->s))
   1119       cleanup_and_exit (1);
   1120 
   1121     dyn_string_delete (step2);
   1122   }
   1123 
   1124   /*
   1125    * Step 3. Call GCC/LD to again, adding the exp file this time.
   1126    * driver command line will look like the following:
   1127    *
   1128    *    % gcc -Wl,--dll --Wl,--base-file,foo.base foo.exp [rest ...]
   1129    */
   1130 
   1131   {
   1132     int quote;
   1133 
   1134     dyn_string_t step3 = dyn_string_new (driver_cmdline->length
   1135 					 + strlen (exp_file_name)
   1136 					 + strlen (base_file_name)
   1137 				         + 20);
   1138     dyn_string_append_cstr (step3, "-Wl,--base-file,");
   1139     quote = (strchr (base_file_name, ' ')
   1140 	     || strchr (base_file_name, '\t'));
   1141     dyn_string_append_cstr (step3,
   1142 	               (quote) ? "\"" : "");
   1143     dyn_string_append_cstr (step3, base_file_name);
   1144     dyn_string_append_cstr (step3,
   1145 	               (quote) ? "\" " : " ");
   1146 
   1147     quote = (strchr (exp_file_name, ' ')
   1148 	     || strchr (exp_file_name, '\t'));
   1149     dyn_string_append_cstr (step3,
   1150 	               (quote) ? "\"" : "");
   1151     dyn_string_append_cstr (step3, exp_file_name);
   1152     dyn_string_append_cstr (step3,
   1153 	               (quote) ? "\"" : "");
   1154 
   1155     if (driver_cmdline->length)
   1156       {
   1157 	dyn_string_append_cstr (step3, " ");
   1158 	dyn_string_append_cstr (step3, driver_cmdline->s);
   1159       }
   1160 
   1161     if (run (driver_name, step3->s))
   1162       cleanup_and_exit (1);
   1163 
   1164     dyn_string_delete (step3);
   1165   }
   1166 
   1167 
   1168   /*
   1169    * Step 4. Run DLLTOOL again using the same command line.
   1170    */
   1171 
   1172   {
   1173     int quote;
   1174     dyn_string_t step4 = dyn_string_new (dlltool_cmdline->length
   1175 					 + strlen (base_file_name)
   1176 					 + strlen (exp_file_name)
   1177 				         + 20);
   1178 
   1179     dyn_string_append_cstr (step4, "--base-file ");
   1180     quote = (strchr (base_file_name, ' ')
   1181 	     || strchr (base_file_name, '\t'));
   1182     dyn_string_append_cstr (step4,
   1183 	               (quote) ? "\"" : "");
   1184     dyn_string_append_cstr (step4, base_file_name);
   1185     dyn_string_append_cstr (step4,
   1186 	               (quote) ? "\" " : " ");
   1187 
   1188     dyn_string_append_cstr (step4, "--output-exp ");
   1189     quote = (strchr (exp_file_name, ' ')
   1190 	     || strchr (exp_file_name, '\t'));
   1191     dyn_string_append_cstr (step4,
   1192 	               (quote) ? "\"" : "");
   1193     dyn_string_append_cstr (step4, exp_file_name);
   1194     dyn_string_append_cstr (step4,
   1195 	               (quote) ? "\"" : "");
   1196 
   1197     if (dlltool_cmdline->length)
   1198       {
   1199 	dyn_string_append_cstr (step4, " ");
   1200 	dyn_string_append_cstr (step4, dlltool_cmdline->s);
   1201       }
   1202 
   1203     if (output_lib_file_name)
   1204       {
   1205 	dyn_string_append_cstr (step4, " --output-lib ");
   1206 	dyn_string_append_cstr (step4, output_lib_file_name);
   1207       }
   1208 
   1209     if (run (dlltool_name, step4->s))
   1210       cleanup_and_exit (1);
   1211 
   1212     dyn_string_delete (step4);
   1213   }
   1214 
   1215 
   1216   /*
   1217    * Step 5. Link it all together and be done with it.
   1218    * driver command line will look like the following:
   1219    *
   1220    *    % gcc -Wl,--dll foo.exp [rest ...]
   1221    *
   1222    */
   1223 
   1224   {
   1225     int quote;
   1226 
   1227     dyn_string_t step5 = dyn_string_new (driver_cmdline->length
   1228 					 + strlen (exp_file_name)
   1229 				         + 20);
   1230     quote = (strchr (exp_file_name, ' ')
   1231 	     || strchr (exp_file_name, '\t'));
   1232     dyn_string_append_cstr (step5,
   1233 	               (quote) ? "\"" : "");
   1234     dyn_string_append_cstr (step5, exp_file_name);
   1235     dyn_string_append_cstr (step5,
   1236 	               (quote) ? "\"" : "");
   1237 
   1238     if (driver_cmdline->length)
   1239       {
   1240 	dyn_string_append_cstr (step5, " ");
   1241 	dyn_string_append_cstr (step5, driver_cmdline->s);
   1242       }
   1243 
   1244     if (run (driver_name, step5->s))
   1245       cleanup_and_exit (1);
   1246 
   1247     dyn_string_delete (step5);
   1248   }
   1249 
   1250   cleanup_and_exit (0);
   1251 
   1252   return 0;
   1253 }
   1254