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@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
display(const char * message,va_list args)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
inform(const char * message,...)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
warn(const char * format,...)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 *
look_for_prog(const char * progname,const char * prefix,int end_prefix)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 *
deduce_name(const char * name)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
delete_temp_files(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
cleanup_and_exit(int status)340 cleanup_and_exit (int status)
341 {
342   delete_temp_files ();
343   exit (status);
344 }
345 
346 static int
run(const char * what,char * args)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 *
mybasename(const char * name)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
strhash(const char * str)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
usage(FILE * file,int status)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
main(int argc,char ** argv)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