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