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