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