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