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