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