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