1 /* dllwrap.c -- wrapper for DLLTOOL and GCC to generate PE style DLLs
2    Copyright (C) 1998-2022 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 
63 static char *driver_name = NULL;
64 static char *cygwin_driver_flags =
65   "-Wl,--dll -nostartfiles";
66 static char *mingw32_driver_flags = "-mdll";
67 static char *generic_driver_flags = "-Wl,--dll";
68 
69 static char *entry_point;
70 
71 static char *dlltool_name = NULL;
72 
73 static char *target = TARGET;
74 
75 /* -1: use default, 0: no underscoring, 1: underscore.  */
76 static int is_leading_underscore = -1;
77 
78 typedef enum {
79   UNKNOWN_TARGET,
80   CYGWIN_TARGET,
81   MINGW_TARGET
82 }
83 target_type;
84 
85 typedef enum {
86   UNKNOWN_CPU,
87   X86_CPU,
88   X64_CPU,
89   ARM_CPU
90 }
91 target_cpu;
92 
93 static target_type which_target = UNKNOWN_TARGET;
94 static target_cpu which_cpu = UNKNOWN_CPU;
95 
96 static int dontdeltemps = 0;
97 static int dry_run = 0;
98 
99 static char *prog_name;
100 
101 static int verbose = 0;
102 
103 static char *dll_file_name;
104 static char *dll_name;
105 static char *base_file_name;
106 static char *exp_file_name;
107 static char *def_file_name;
108 static int delete_base_file = 1;
109 static int delete_exp_file = 1;
110 static int delete_def_file = 1;
111 
112 static int run (const char *, char *);
113 static char *mybasename (const char *);
114 static int strhash (const char *);
115 static void usage (FILE *, int);
116 static void display (const char *, va_list) ATTRIBUTE_PRINTF(1,0);
117 static void inform (const char *, ...) ATTRIBUTE_PRINTF_1;
118 static void warn (const char *, ...) ATTRIBUTE_PRINTF_1;
119 static char *look_for_prog (const char *, const char *, int);
120 static char *deduce_name (const char *);
121 static void delete_temp_files (void);
122 static 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 
134 static void
display(const char * message,va_list args)135 display (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 
145 static void
inform(const char * message,...)146 inform (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 
160 static void
warn(const char * format,...)161 warn (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 
177 static char *
look_for_prog(const char * progname,const char * prefix,int end_prefix)178 look_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 
243 static char *
deduce_name(const char * name)244 deduce_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 
288 static void
delete_temp_files(void)289 delete_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 
339 static void
cleanup_and_exit(int status)340 cleanup_and_exit (int status)
341 {
342   delete_temp_files ();
343   exit (status);
344 }
345 
346 static int
run(const char * what,char * args)347 run (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 = NULL, *errmsg_arg = NULL;
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 
433 static char *
mybasename(const char * name)434 mybasename (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 
449 static int
strhash(const char * str)450 strhash (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 
474 static void
usage(FILE * file,int status)475 usage (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 
562 static 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 
605 int main (int, char **);
606 
607 int
main(int argc,char ** argv)608 main (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 #ifdef HAVE_LC_MESSAGES
634   setlocale (LC_MESSAGES, "");
635 #endif
636   setlocale (LC_CTYPE, "");
637   bindtextdomain (PACKAGE, LOCALEDIR);
638   textdomain (PACKAGE);
639 
640   warn (_("WARNING: %s is deprecated, use gcc -shared or ld -shared instead\n"),
641 	prog_name);
642 
643   expandargv (&argc, &argv);
644 
645   saved_argv = (char **) xmalloc (argc * sizeof (char*));
646   dlltool_arg_indices = (int *) xmalloc (argc * sizeof (int));
647   driver_arg_indices = (int *) xmalloc (argc * sizeof (int));
648   for (i = 0; i < argc; ++i)
649     {
650       size_t len = strlen (argv[i]);
651       char *arg = (char *) xmalloc (len + 1);
652       strcpy (arg, argv[i]);
653       cmdline_len += len;
654       saved_argv[i] = arg;
655       dlltool_arg_indices[i] = 0;
656       driver_arg_indices[i] = 1;
657     }
658   cmdline_len++;
659 
660   /* We recognize dllwrap and dlltool options, and everything else is
661      passed onto the language driver (eg., to GCC). We collect options
662      to dlltool and driver in dlltool_args and driver_args.  */
663 
664   opterr = 0;
665   while ((c = getopt_long_only (argc, argv, "nkAqve:Uho:l:L:I:",
666 				long_options, (int *) 0)) != EOF)
667     {
668       int dlltool_arg;
669       int driver_arg;
670       int single_word_option_value_pair;
671 
672       dlltool_arg = 0;
673       driver_arg = 1;
674       single_word_option_value_pair = 0;
675 
676       if (c != '?')
677 	{
678 	  /* We recognize this option, so it has to be either dllwrap or
679 	     dlltool option. Do not pass to driver unless it's one of the
680 	     generic options that are passed to all the tools (such as -v)
681 	     which are dealt with later.  */
682 	  driver_arg = 0;
683 	}
684 
685       /* deal with generic and dllwrap options first.  */
686       switch (c)
687 	{
688 	case 'h':
689 	  usage (stdout, 0);
690 	  break;
691 	case 'q':
692 	  verbose = 0;
693 	  break;
694 	case 'v':
695 	  verbose = 1;
696 	  break;
697 	case OPTION_VERSION:
698 	  print_version (prog_name);
699 	  break;
700 	case 'e':
701 	  entry_point = optarg;
702 	  break;
703 	case OPTION_IMAGE_BASE:
704 	  image_base_str = optarg;
705 	  break;
706 	case OPTION_DEF:
707 	  def_file_name = optarg;
708 	  def_file_seen = 1;
709 	  delete_def_file = 0;
710 	  break;
711 	case 'n':
712 	  dontdeltemps = 1;
713 	  dlltool_arg = 1;
714 	  break;
715 	case 'o':
716 	  dll_file_name = optarg;
717 	  break;
718 	case 'I':
719 	case 'l':
720 	case 'L':
721 	  driver_arg = 1;
722 	  break;
723 	case OPTION_DLLNAME:
724 	  dll_name = optarg;
725 	  break;
726 	case OPTION_DRY_RUN:
727 	  dry_run = 1;
728 	  break;
729 	case OPTION_DRIVER_NAME:
730 	  driver_name = optarg;
731 	  break;
732 	case OPTION_DRIVER_FLAGS:
733 	  driver_flags = optarg;
734 	  break;
735 	case OPTION_DLLTOOL_NAME:
736 	  dlltool_name = optarg;
737 	  break;
738 	case OPTION_TARGET:
739 	  target = optarg;
740 	  break;
741 	case OPTION_MNO_CYGWIN:
742 	  target = "i386-mingw32";
743 	  break;
744 	case OPTION_NO_LEADING_UNDERSCORE:
745 	  is_leading_underscore = 0;
746 	  break;
747 	case OPTION_LEADING_UNDERSCORE:
748 	  is_leading_underscore = 1;
749 	  break;
750 	case OPTION_BASE_FILE:
751 	  base_file_name = optarg;
752 	  delete_base_file = 0;
753 	  break;
754 	case OPTION_OUTPUT_EXP:
755 	  exp_file_name = optarg;
756 	  delete_exp_file = 0;
757 	  break;
758 	case OPTION_EXPORT_ALL_SYMS:
759 	  export_all = 1;
760 	  break;
761 	case OPTION_OUTPUT_LIB:
762 	  output_lib_file_name = optarg;
763 	  break;
764 	case '?':
765 	  break;
766 	default:
767 	  dlltool_arg = 1;
768 	  break;
769 	}
770 
771       /* Handle passing through --option=value case.  */
772       if (optarg
773 	  && saved_argv[optind-1][0] == '-'
774 	  && saved_argv[optind-1][1] == '-'
775 	  && strchr (saved_argv[optind-1], '='))
776 	single_word_option_value_pair = 1;
777 
778       if (dlltool_arg)
779 	{
780 	  dlltool_arg_indices[optind-1] = 1;
781 	  if (optarg && ! single_word_option_value_pair)
782 	    {
783 	      dlltool_arg_indices[optind-2] = 1;
784 	    }
785 	}
786 
787       if (! driver_arg)
788 	{
789 	  driver_arg_indices[optind-1] = 0;
790 	  if (optarg && ! single_word_option_value_pair)
791 	    {
792 	      driver_arg_indices[optind-2] = 0;
793 	    }
794 	}
795     }
796 
797   /* Sanity checks.  */
798   if (! dll_name && ! dll_file_name)
799     {
800       warn (_("Must provide at least one of -o or --dllname options"));
801       exit (1);
802     }
803   else if (! dll_name)
804     {
805       dll_name = xstrdup (mybasename (dll_file_name));
806     }
807   else if (! dll_file_name)
808     {
809       dll_file_name = xstrdup (dll_name);
810     }
811 
812   /* Deduce driver-name and dlltool-name from our own.  */
813   if (driver_name == NULL)
814     driver_name = deduce_name ("gcc");
815 
816   if (dlltool_name == NULL)
817     dlltool_name = deduce_name ("dlltool");
818 
819   if (! def_file_seen)
820     {
821       char *fileprefix = choose_temp_base ();
822 
823       def_file_name = (char *) xmalloc (strlen (fileprefix) + 5);
824       sprintf (def_file_name, "%s.def",
825 	       (dontdeltemps) ? mybasename (fileprefix) : fileprefix);
826       delete_def_file = 1;
827       free (fileprefix);
828       delete_def_file = 1;
829       warn (_("no export definition file provided.\n\
830 Creating one, but that may not be what you want"));
831     }
832 
833   /* Set the target platform.  */
834   if (strstr (target, "cygwin"))
835     which_target = CYGWIN_TARGET;
836   else if (strstr (target, "mingw"))
837     which_target = MINGW_TARGET;
838   else
839     which_target = UNKNOWN_TARGET;
840 
841   if (startswith (target, "arm"))
842     which_cpu = ARM_CPU;
843   else if (startswith (target, "x86_64")
844 	   || startswith (target, "athlon64")
845 	   || startswith (target, "amd64"))
846     which_cpu = X64_CPU;
847   else if (target[0] == 'i' && (target[1] >= '3' && target[1] <= '6')
848 	   && target[2] == '8' && target[3] == '6')
849     which_cpu = X86_CPU;
850   else
851     which_cpu = UNKNOWN_CPU;
852 
853   if (is_leading_underscore == -1)
854     is_leading_underscore = (which_cpu != X64_CPU && which_cpu != ARM_CPU);
855 
856   /* Re-create the command lines as a string, taking care to quote stuff.  */
857   dlltool_cmdline = dyn_string_new (cmdline_len);
858   if (verbose)
859     dyn_string_append_cstr (dlltool_cmdline, " -v");
860 
861   dyn_string_append_cstr (dlltool_cmdline, " --dllname ");
862   dyn_string_append_cstr (dlltool_cmdline, dll_name);
863 
864   for (i = 1; i < argc; ++i)
865     {
866       if (dlltool_arg_indices[i])
867 	{
868 	  char *arg = saved_argv[i];
869 	  int quote = (strchr (arg, ' ') || strchr (arg, '\t'));
870 	  dyn_string_append_cstr (dlltool_cmdline,
871 	                     (quote) ? " \"" : " ");
872 	  dyn_string_append_cstr (dlltool_cmdline, arg);
873 	  dyn_string_append_cstr (dlltool_cmdline,
874 	                     (quote) ? "\"" : "");
875 	}
876     }
877 
878   driver_cmdline = dyn_string_new (cmdline_len);
879   if (! driver_flags || strlen (driver_flags) == 0)
880     {
881       switch (which_target)
882 	{
883 	case CYGWIN_TARGET:
884 	  driver_flags = cygwin_driver_flags;
885 	  break;
886 
887 	case MINGW_TARGET:
888 	  driver_flags = mingw32_driver_flags;
889 	  break;
890 
891 	default:
892 	  driver_flags = generic_driver_flags;
893 	  break;
894 	}
895     }
896   dyn_string_append_cstr (driver_cmdline, driver_flags);
897   dyn_string_append_cstr (driver_cmdline, " -o ");
898   dyn_string_append_cstr (driver_cmdline, dll_file_name);
899 
900   if (is_leading_underscore == 0)
901     dyn_string_append_cstr (driver_cmdline, " --no-leading-underscore");
902   else if (is_leading_underscore == 1)
903     dyn_string_append_cstr (driver_cmdline, " --leading-underscore");
904 
905   if (! entry_point || strlen (entry_point) == 0)
906     {
907       const char *prefix = (is_leading_underscore != 0 ? "_" : "");
908       const char *postfix = "";
909       const char *name_entry;
910 
911       if (which_cpu == X86_CPU || which_cpu == UNKNOWN_CPU)
912 	postfix = "@12";
913 
914       switch (which_target)
915 	{
916 	case CYGWIN_TARGET:
917 	  name_entry = "_cygwin_dll_entry";
918 	  break;
919 
920 	case MINGW_TARGET:
921 	  name_entry = "DllMainCRTStartup";
922 	  break;
923 
924 	default:
925 	  name_entry = "DllMain";
926 	  break;
927 	}
928       entry_point =
929 	(char *) malloc (strlen (name_entry) + strlen (prefix)
930 			 + strlen (postfix) + 1);
931       sprintf (entry_point, "%s%s%s", prefix, name_entry, postfix);
932     }
933   dyn_string_append_cstr (driver_cmdline, " -Wl,-e,");
934   dyn_string_append_cstr (driver_cmdline, entry_point);
935   dyn_string_append_cstr (dlltool_cmdline, " --exclude-symbol=");
936   dyn_string_append_cstr (dlltool_cmdline,
937                     (entry_point[0] == '_') ? entry_point+1 : entry_point);
938 
939   if (! image_base_str || strlen (image_base_str) == 0)
940     {
941       char *tmpbuf = (char *) xmalloc (sizeof ("0x12345678") + 1);
942       unsigned long hash = strhash (dll_file_name);
943       sprintf (tmpbuf, "0x%.8lX", 0x60000000|((hash<<16)&0xFFC0000));
944       image_base_str = tmpbuf;
945     }
946 
947   dyn_string_append_cstr (driver_cmdline, " -Wl,--image-base,");
948   dyn_string_append_cstr (driver_cmdline, image_base_str);
949 
950   if (verbose)
951     {
952       dyn_string_append_cstr (driver_cmdline, " -v");
953     }
954 
955   for (i = 1; i < argc; ++i)
956     {
957       if (driver_arg_indices[i])
958 	{
959 	  char *arg = saved_argv[i];
960 	  int quote = (strchr (arg, ' ') || strchr (arg, '\t'));
961 	  dyn_string_append_cstr (driver_cmdline,
962 	                     (quote) ? " \"" : " ");
963 	  dyn_string_append_cstr (driver_cmdline, arg);
964 	  dyn_string_append_cstr (driver_cmdline,
965 	                     (quote) ? "\"" : "");
966 	}
967     }
968 
969   /* Step pre-1. If no --def <EXPORT_DEF> is specified,
970      then create it and then pass it on.  */
971 
972   if (! def_file_seen)
973     {
974       dyn_string_t step_pre1;
975 
976       step_pre1 = dyn_string_new (1024);
977 
978       dyn_string_append_cstr (step_pre1, dlltool_cmdline->s);
979       if (export_all)
980 	{
981 	  dyn_string_append_cstr (step_pre1, " --export-all --exclude-symbol=");
982 	  dyn_string_append_cstr (step_pre1,
983 	  "_cygwin_dll_entry@12,DllMainCRTStartup@12,DllMain@12,DllEntryPoint@12");
984 	}
985       dyn_string_append_cstr (step_pre1, " --output-def ");
986       dyn_string_append_cstr (step_pre1, def_file_name);
987 
988       for (i = 1; i < argc; ++i)
989 	{
990 	  if (driver_arg_indices[i])
991 	    {
992 	      char *arg = saved_argv[i];
993 	      size_t len = strlen (arg);
994 	      if (len >= 2 && arg[len-2] == '.'
995 	          && (arg[len-1] == 'o' || arg[len-1] == 'a'))
996 		{
997 		  int quote = (strchr (arg, ' ') || strchr (arg, '\t'));
998 		  dyn_string_append_cstr (step_pre1,
999 				     (quote) ? " \"" : " ");
1000 		  dyn_string_append_cstr (step_pre1, arg);
1001 		  dyn_string_append_cstr (step_pre1,
1002 				     (quote) ? "\"" : "");
1003 		}
1004 	    }
1005 	}
1006 
1007       if (run (dlltool_name, step_pre1->s))
1008 	cleanup_and_exit (1);
1009 
1010       dyn_string_delete (step_pre1);
1011     }
1012 
1013   dyn_string_append_cstr (dlltool_cmdline, " --def ");
1014   dyn_string_append_cstr (dlltool_cmdline, def_file_name);
1015 
1016   if (verbose)
1017     {
1018       fprintf (stderr, _("DLLTOOL name    : %s\n"), dlltool_name);
1019       fprintf (stderr, _("DLLTOOL options : %s\n"), dlltool_cmdline->s);
1020       fprintf (stderr, _("DRIVER name     : %s\n"), driver_name);
1021       fprintf (stderr, _("DRIVER options  : %s\n"), driver_cmdline->s);
1022     }
1023 
1024   /* Step 1. Call GCC/LD to create base relocation file. If using GCC, the
1025      driver command line will look like the following:
1026 
1027         % gcc -Wl,--dll --Wl,--base-file,foo.base [rest of command line]
1028 
1029      If the user does not specify a base name, create temporary one that
1030      is deleted at exit.  */
1031 
1032   if (! base_file_name)
1033     {
1034       char *fileprefix = choose_temp_base ();
1035       base_file_name = (char *) xmalloc (strlen (fileprefix) + 6);
1036       sprintf (base_file_name, "%s.base",
1037 	       (dontdeltemps) ? mybasename (fileprefix) : fileprefix);
1038       delete_base_file = 1;
1039       free (fileprefix);
1040     }
1041 
1042   {
1043     int quote;
1044 
1045     dyn_string_t step1 = dyn_string_new (driver_cmdline->length
1046 					 + strlen (base_file_name)
1047 					 + 20);
1048     dyn_string_append_cstr (step1, "-Wl,--base-file,");
1049     quote = (strchr (base_file_name, ' ')
1050 	     || strchr (base_file_name, '\t'));
1051     dyn_string_append_cstr (step1,
1052 	               (quote) ? "\"" : "");
1053     dyn_string_append_cstr (step1, base_file_name);
1054     dyn_string_append_cstr (step1,
1055 	               (quote) ? "\"" : "");
1056     if (driver_cmdline->length)
1057       {
1058 	dyn_string_append_cstr (step1, " ");
1059 	dyn_string_append_cstr (step1, driver_cmdline->s);
1060       }
1061 
1062     if (run (driver_name, step1->s))
1063       cleanup_and_exit (1);
1064 
1065     dyn_string_delete (step1);
1066   }
1067 
1068   /* Step 2. generate the exp file by running dlltool.
1069      dlltool command line will look like the following:
1070 
1071         % dlltool -Wl,--dll --Wl,--base-file,foo.base [rest of command line]
1072 
1073      If the user does not specify a base name, create temporary one that
1074      is deleted at exit.  */
1075 
1076   if (! exp_file_name)
1077     {
1078       char *p = strrchr (dll_name, '.');
1079       size_t prefix_len = (p) ? (size_t) (p - dll_name) : strlen (dll_name);
1080 
1081       exp_file_name = (char *) xmalloc (prefix_len + 4 + 1);
1082       strncpy (exp_file_name, dll_name, prefix_len);
1083       exp_file_name[prefix_len] = '\0';
1084       strcat (exp_file_name, ".exp");
1085       delete_exp_file = 1;
1086     }
1087 
1088   {
1089     int quote;
1090 
1091     dyn_string_t step2 = dyn_string_new (dlltool_cmdline->length
1092 					 + strlen (base_file_name)
1093 					 + strlen (exp_file_name)
1094 				         + 20);
1095 
1096     dyn_string_append_cstr (step2, "--base-file ");
1097     quote = (strchr (base_file_name, ' ')
1098 	     || strchr (base_file_name, '\t'));
1099     dyn_string_append_cstr (step2,
1100 	               (quote) ? "\"" : "");
1101     dyn_string_append_cstr (step2, base_file_name);
1102     dyn_string_append_cstr (step2,
1103 	               (quote) ? "\" " : " ");
1104 
1105     dyn_string_append_cstr (step2, "--output-exp ");
1106     quote = (strchr (exp_file_name, ' ')
1107 	     || strchr (exp_file_name, '\t'));
1108     dyn_string_append_cstr (step2,
1109 	               (quote) ? "\"" : "");
1110     dyn_string_append_cstr (step2, exp_file_name);
1111     dyn_string_append_cstr (step2,
1112 	               (quote) ? "\"" : "");
1113 
1114     if (dlltool_cmdline->length)
1115       {
1116 	dyn_string_append_cstr (step2, " ");
1117 	dyn_string_append_cstr (step2, dlltool_cmdline->s);
1118       }
1119 
1120     if (run (dlltool_name, step2->s))
1121       cleanup_and_exit (1);
1122 
1123     dyn_string_delete (step2);
1124   }
1125 
1126   /*
1127    * Step 3. Call GCC/LD to again, adding the exp file this time.
1128    * driver command line will look like the following:
1129    *
1130    *    % gcc -Wl,--dll --Wl,--base-file,foo.base foo.exp [rest ...]
1131    */
1132 
1133   {
1134     int quote;
1135 
1136     dyn_string_t step3 = dyn_string_new (driver_cmdline->length
1137 					 + strlen (exp_file_name)
1138 					 + strlen (base_file_name)
1139 				         + 20);
1140     dyn_string_append_cstr (step3, "-Wl,--base-file,");
1141     quote = (strchr (base_file_name, ' ')
1142 	     || strchr (base_file_name, '\t'));
1143     dyn_string_append_cstr (step3,
1144 	               (quote) ? "\"" : "");
1145     dyn_string_append_cstr (step3, base_file_name);
1146     dyn_string_append_cstr (step3,
1147 	               (quote) ? "\" " : " ");
1148 
1149     quote = (strchr (exp_file_name, ' ')
1150 	     || strchr (exp_file_name, '\t'));
1151     dyn_string_append_cstr (step3,
1152 	               (quote) ? "\"" : "");
1153     dyn_string_append_cstr (step3, exp_file_name);
1154     dyn_string_append_cstr (step3,
1155 	               (quote) ? "\"" : "");
1156 
1157     if (driver_cmdline->length)
1158       {
1159 	dyn_string_append_cstr (step3, " ");
1160 	dyn_string_append_cstr (step3, driver_cmdline->s);
1161       }
1162 
1163     if (run (driver_name, step3->s))
1164       cleanup_and_exit (1);
1165 
1166     dyn_string_delete (step3);
1167   }
1168 
1169 
1170   /*
1171    * Step 4. Run DLLTOOL again using the same command line.
1172    */
1173 
1174   {
1175     int quote;
1176     dyn_string_t step4 = dyn_string_new (dlltool_cmdline->length
1177 					 + strlen (base_file_name)
1178 					 + strlen (exp_file_name)
1179 				         + 20);
1180 
1181     dyn_string_append_cstr (step4, "--base-file ");
1182     quote = (strchr (base_file_name, ' ')
1183 	     || strchr (base_file_name, '\t'));
1184     dyn_string_append_cstr (step4,
1185 	               (quote) ? "\"" : "");
1186     dyn_string_append_cstr (step4, base_file_name);
1187     dyn_string_append_cstr (step4,
1188 	               (quote) ? "\" " : " ");
1189 
1190     dyn_string_append_cstr (step4, "--output-exp ");
1191     quote = (strchr (exp_file_name, ' ')
1192 	     || strchr (exp_file_name, '\t'));
1193     dyn_string_append_cstr (step4,
1194 	               (quote) ? "\"" : "");
1195     dyn_string_append_cstr (step4, exp_file_name);
1196     dyn_string_append_cstr (step4,
1197 	               (quote) ? "\"" : "");
1198 
1199     if (dlltool_cmdline->length)
1200       {
1201 	dyn_string_append_cstr (step4, " ");
1202 	dyn_string_append_cstr (step4, dlltool_cmdline->s);
1203       }
1204 
1205     if (output_lib_file_name)
1206       {
1207 	dyn_string_append_cstr (step4, " --output-lib ");
1208 	dyn_string_append_cstr (step4, output_lib_file_name);
1209       }
1210 
1211     if (run (dlltool_name, step4->s))
1212       cleanup_and_exit (1);
1213 
1214     dyn_string_delete (step4);
1215   }
1216 
1217 
1218   /*
1219    * Step 5. Link it all together and be done with it.
1220    * driver command line will look like the following:
1221    *
1222    *    % gcc -Wl,--dll foo.exp [rest ...]
1223    *
1224    */
1225 
1226   {
1227     int quote;
1228 
1229     dyn_string_t step5 = dyn_string_new (driver_cmdline->length
1230 					 + strlen (exp_file_name)
1231 				         + 20);
1232     quote = (strchr (exp_file_name, ' ')
1233 	     || strchr (exp_file_name, '\t'));
1234     dyn_string_append_cstr (step5,
1235 	               (quote) ? "\"" : "");
1236     dyn_string_append_cstr (step5, exp_file_name);
1237     dyn_string_append_cstr (step5,
1238 	               (quote) ? "\"" : "");
1239 
1240     if (driver_cmdline->length)
1241       {
1242 	dyn_string_append_cstr (step5, " ");
1243 	dyn_string_append_cstr (step5, driver_cmdline->s);
1244       }
1245 
1246     if (run (driver_name, step5->s))
1247       cleanup_and_exit (1);
1248 
1249     dyn_string_delete (step5);
1250   }
1251 
1252   cleanup_and_exit (0);
1253 
1254   return 0;
1255 }
1256