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