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