]>
Commit | Line | Data |
---|---|---|
d7f09764 | 1 | /* Wrapper to call lto. Used by collect2 and the linker plugin. |
d7fb0a6d | 2 | Copyright (C) 2009, 2010 Free Software Foundation, Inc. |
d7f09764 DN |
3 | |
4 | Factored out of collect2 by Rafael Espindola <espindola@google.com> | |
5 | ||
6 | This file is part of GCC. | |
7 | ||
8 | GCC is free software; you can redistribute it and/or modify it under | |
9 | the terms of the GNU General Public License as published by the Free | |
10 | Software Foundation; either version 3, or (at your option) any later | |
11 | version. | |
12 | ||
13 | GCC is distributed in the hope that it will be useful, but WITHOUT ANY | |
14 | WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
15 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
16 | for more details. | |
17 | ||
18 | You should have received a copy of the GNU General Public License | |
19 | along with GCC; see the file COPYING3. If not see | |
20 | <http://www.gnu.org/licenses/>. */ | |
21 | ||
22 | ||
23 | /* This program is passed a gcc, a list of gcc arguments and a list of | |
24 | object files containing IL. It scans the argument list to check if | |
25 | we are in whopr mode or not modifies the arguments and needed and | |
26 | prints a list of output files on stdout. | |
27 | ||
28 | Example: | |
29 | ||
30 | $ lto-wrapper gcc/xgcc -B gcc a.o b.o -o test -flto | |
31 | ||
32 | The above will print something like | |
33 | /tmp/ccwbQ8B2.lto.o | |
34 | ||
35 | If -fwhopr is used instead, more than one file might be produced | |
36 | ./ccXj2DTk.lto.ltrans.o | |
37 | ./ccCJuXGv.lto.ltrans.o | |
38 | */ | |
39 | ||
40 | #include "config.h" | |
41 | #include "system.h" | |
62116e60 | 42 | #include <errno.h> |
d7f09764 DN |
43 | #include "coretypes.h" |
44 | #include "tm.h" | |
45 | #include "intl.h" | |
46 | #include "libiberty.h" | |
48cf395b | 47 | #include "obstack.h" |
d7f09764 | 48 | |
cf96bae7 RG |
49 | int debug; /* true if -save-temps. */ |
50 | int verbose; /* true if -v. */ | |
d7f09764 DN |
51 | |
52 | enum lto_mode_d { | |
cf96bae7 RG |
53 | LTO_MODE_NONE, /* Not doing LTO. */ |
54 | LTO_MODE_LTO, /* Normal LTO. */ | |
55 | LTO_MODE_WHOPR /* WHOPR. */ | |
d7f09764 DN |
56 | }; |
57 | ||
58 | /* Current LTO mode. */ | |
59 | static enum lto_mode_d lto_mode = LTO_MODE_NONE; | |
60 | ||
b1b07c92 RG |
61 | static char *ltrans_output_file; |
62 | static char *flto_out; | |
63 | static char *args_name; | |
50ee30d5 RG |
64 | static unsigned int nr; |
65 | static char **input_names; | |
66 | static char **output_names; | |
67 | static char *makefile; | |
b1b07c92 RG |
68 | |
69 | static void maybe_unlink_file (const char *); | |
70 | ||
71 | /* Delete tempfiles and exit function. */ | |
72 | ||
73 | static void | |
74 | lto_wrapper_exit (int status) | |
75 | { | |
8aea79e6 RAE |
76 | static bool cleanup_done = false; |
77 | if (!cleanup_done) | |
78 | { | |
50ee30d5 RG |
79 | unsigned int i; |
80 | ||
8aea79e6 RAE |
81 | /* Setting cleanup_done prevents an infinite loop if one of the |
82 | calls to maybe_unlink_file fails. */ | |
83 | cleanup_done = true; | |
84 | ||
85 | if (ltrans_output_file) | |
86 | maybe_unlink_file (ltrans_output_file); | |
87 | if (flto_out) | |
88 | maybe_unlink_file (flto_out); | |
89 | if (args_name) | |
90 | maybe_unlink_file (args_name); | |
50ee30d5 RG |
91 | if (makefile) |
92 | maybe_unlink_file (makefile); | |
93 | for (i = 0; i < nr; ++i) | |
94 | { | |
95 | maybe_unlink_file (input_names[i]); | |
96 | if (output_names[i]) | |
97 | maybe_unlink_file (output_names[i]); | |
98 | } | |
8aea79e6 | 99 | } |
b1b07c92 RG |
100 | exit (status); |
101 | } | |
102 | ||
d7f09764 DN |
103 | /* Just die. CMSGID is the error message. */ |
104 | ||
105 | static void __attribute__ ((format (printf, 1, 2))) | |
106 | fatal (const char * cmsgid, ...) | |
107 | { | |
108 | va_list ap; | |
109 | ||
110 | va_start (ap, cmsgid); | |
111 | fprintf (stderr, "lto-wrapper: "); | |
112 | vfprintf (stderr, _(cmsgid), ap); | |
113 | fprintf (stderr, "\n"); | |
114 | va_end (ap); | |
115 | ||
b1b07c92 | 116 | lto_wrapper_exit (FATAL_EXIT_CODE); |
d7f09764 DN |
117 | } |
118 | ||
119 | ||
120 | /* Die when sys call fails. CMSGID is the error message. */ | |
121 | ||
122 | static void __attribute__ ((format (printf, 1, 2))) | |
123 | fatal_perror (const char *cmsgid, ...) | |
124 | { | |
125 | int e = errno; | |
126 | va_list ap; | |
127 | ||
128 | va_start (ap, cmsgid); | |
129 | fprintf (stderr, "lto-wrapper: "); | |
130 | vfprintf (stderr, _(cmsgid), ap); | |
131 | fprintf (stderr, ": %s\n", xstrerror (e)); | |
132 | va_end (ap); | |
133 | ||
b1b07c92 | 134 | lto_wrapper_exit (FATAL_EXIT_CODE); |
d7f09764 DN |
135 | } |
136 | ||
137 | ||
138 | /* Execute a program, and wait for the reply. ARGV are the arguments. The | |
139 | last one must be NULL. */ | |
140 | ||
141 | static struct pex_obj * | |
142 | collect_execute (char **argv) | |
143 | { | |
144 | struct pex_obj *pex; | |
145 | const char *errmsg; | |
146 | int err; | |
147 | ||
cf96bae7 | 148 | if (verbose) |
d7f09764 DN |
149 | { |
150 | char **p_argv; | |
151 | const char *str; | |
152 | ||
153 | for (p_argv = argv; (str = *p_argv) != (char *) 0; p_argv++) | |
154 | fprintf (stderr, " %s", str); | |
155 | ||
156 | fprintf (stderr, "\n"); | |
157 | } | |
158 | ||
159 | fflush (stdout); | |
160 | fflush (stderr); | |
161 | ||
162 | pex = pex_init (0, "lto-wrapper", NULL); | |
163 | if (pex == NULL) | |
164 | fatal_perror ("pex_init failed"); | |
165 | ||
c04b6b38 RG |
166 | /* Do not use PEX_LAST here, we use our stdout for communicating with |
167 | collect2 or the linker-plugin. Any output from the sub-process | |
168 | will confuse that. */ | |
169 | errmsg = pex_run (pex, PEX_SEARCH, argv[0], argv, NULL, | |
d7f09764 DN |
170 | NULL, &err); |
171 | if (errmsg != NULL) | |
172 | { | |
173 | if (err != 0) | |
174 | { | |
175 | errno = err; | |
176 | fatal_perror (errmsg); | |
177 | } | |
178 | else | |
179 | fatal (errmsg); | |
180 | } | |
181 | ||
182 | return pex; | |
183 | } | |
184 | ||
185 | ||
186 | /* Wait for a process to finish, and exit if a nonzero status is found. | |
187 | PROG is the program name. PEX is the process we should wait for. */ | |
188 | ||
189 | static int | |
190 | collect_wait (const char *prog, struct pex_obj *pex) | |
191 | { | |
192 | int status; | |
193 | ||
194 | if (!pex_get_status (pex, 1, &status)) | |
195 | fatal_perror ("can't get program status"); | |
196 | pex_free (pex); | |
197 | ||
198 | if (status) | |
199 | { | |
200 | if (WIFSIGNALED (status)) | |
201 | { | |
202 | int sig = WTERMSIG (status); | |
203 | if (WCOREDUMP (status)) | |
204 | fatal ("%s terminated with signal %d [%s], core dumped", | |
205 | prog, sig, strsignal (sig)); | |
206 | else | |
207 | fatal ("%s terminated with signal %d [%s]", | |
208 | prog, sig, strsignal (sig)); | |
209 | } | |
210 | ||
211 | if (WIFEXITED (status)) | |
212 | fatal ("%s returned %d exit status", prog, WEXITSTATUS (status)); | |
213 | } | |
214 | ||
215 | return 0; | |
216 | } | |
217 | ||
218 | ||
219 | /* Unlink a temporary LTRANS file unless requested otherwise. */ | |
220 | ||
221 | static void | |
222 | maybe_unlink_file (const char *file) | |
223 | { | |
224 | if (! debug) | |
225 | { | |
62116e60 RG |
226 | if (unlink_if_ordinary (file) |
227 | && errno != ENOENT) | |
d7f09764 DN |
228 | fatal_perror ("deleting LTRANS file %s", file); |
229 | } | |
230 | else | |
231 | fprintf (stderr, "[Leaving LTRANS %s]\n", file); | |
232 | } | |
233 | ||
234 | ||
235 | /* Execute program ARGV[0] with arguments ARGV. Wait for it to finish. */ | |
236 | ||
237 | static void | |
238 | fork_execute (char **argv) | |
239 | { | |
240 | struct pex_obj *pex; | |
241 | char *new_argv[3]; | |
b1b07c92 RG |
242 | char *at_args; |
243 | FILE *args; | |
d7f09764 DN |
244 | int status; |
245 | ||
b1b07c92 RG |
246 | args_name = make_temp_file (".args"); |
247 | at_args = concat ("@", args_name, NULL); | |
248 | args = fopen (args_name, "w"); | |
d7f09764 DN |
249 | if (args == NULL) |
250 | fatal ("failed to open %s", args_name); | |
251 | ||
252 | status = writeargv (&argv[1], args); | |
253 | ||
254 | if (status) | |
255 | fatal ("could not write to temporary file %s", args_name); | |
256 | ||
257 | fclose (args); | |
258 | ||
259 | new_argv[0] = argv[0]; | |
260 | new_argv[1] = at_args; | |
261 | new_argv[2] = NULL; | |
262 | ||
263 | pex = collect_execute (new_argv); | |
264 | collect_wait (new_argv[0], pex); | |
265 | ||
266 | maybe_unlink_file (args_name); | |
62116e60 | 267 | args_name = NULL; |
d7f09764 DN |
268 | free (at_args); |
269 | } | |
270 | ||
48cf395b RB |
271 | /* Template of LTRANS dumpbase suffix. */ |
272 | #define DUMPBASE_SUFFIX ".ltrans18446744073709551615" | |
d7f09764 DN |
273 | |
274 | /* Execute gcc. ARGC is the number of arguments. ARGV contains the arguments. */ | |
275 | ||
276 | static void | |
277 | run_gcc (unsigned argc, char *argv[]) | |
278 | { | |
cf96bae7 | 279 | unsigned i, j; |
d7f09764 DN |
280 | const char **new_argv; |
281 | const char **argv_ptr; | |
d7f09764 | 282 | char *list_option_full = NULL; |
cf96bae7 RG |
283 | const char *linker_output = NULL; |
284 | const char *collect_gcc_options, *collect_gcc; | |
285 | struct obstack env_obstack; | |
286 | bool seen_o = false; | |
c04b6b38 | 287 | int parallel = 0; |
cf96bae7 RG |
288 | |
289 | /* Get the driver and options. */ | |
290 | collect_gcc = getenv ("COLLECT_GCC"); | |
291 | if (!collect_gcc) | |
292 | fatal ("environment variable COLLECT_GCC must be set"); | |
293 | ||
294 | /* Set the CFLAGS environment variable. */ | |
295 | collect_gcc_options = getenv ("COLLECT_GCC_OPTIONS"); | |
296 | if (!collect_gcc_options) | |
297 | fatal ("environment variable COLLECT_GCC_OPTIONS must be set"); | |
298 | ||
299 | /* Count arguments. */ | |
300 | i = 0; | |
301 | for (j = 0; collect_gcc_options[j] != '\0'; ++j) | |
302 | if (collect_gcc_options[j] == '\'') | |
303 | ++i; | |
304 | ||
305 | if (i % 2 != 0) | |
306 | fatal ("malformed COLLECT_GCC_OPTIONS"); | |
307 | ||
308 | /* Initalize the common arguments for the driver. */ | |
309 | new_argv = (const char **) xmalloc ((15 + i / 2 + argc) * sizeof (char *)); | |
310 | argv_ptr = new_argv; | |
311 | *argv_ptr++ = collect_gcc; | |
312 | *argv_ptr++ = "-xlto"; | |
313 | *argv_ptr++ = "-c"; | |
314 | for (j = 0; collect_gcc_options[j] != '\0'; ++j) | |
315 | if (collect_gcc_options[j] == '\'') | |
316 | { | |
317 | char *option; | |
318 | ||
319 | ++j; | |
320 | i = j; | |
321 | while (collect_gcc_options[j] != '\'') | |
322 | ++j; | |
323 | ||
324 | obstack_init (&env_obstack); | |
325 | obstack_grow (&env_obstack, &collect_gcc_options[i], j - i); | |
326 | obstack_1grow (&env_obstack, 0); | |
327 | option = XOBFINISH (&env_obstack, char *); | |
328 | if (seen_o) | |
329 | { | |
330 | linker_output = option; | |
331 | seen_o = false; | |
332 | continue; | |
333 | } | |
334 | ||
335 | /* If we see -o, skip it and skip and record its argument. */ | |
336 | if (option[0] == '-' && option[1] == 'o') | |
337 | { | |
338 | if (option[2] == '\0') | |
339 | seen_o = true; | |
340 | else | |
341 | linker_output = &option[2]; | |
342 | continue; | |
343 | } | |
d7f09764 | 344 | |
cf96bae7 RG |
345 | if (strcmp (option, "-save-temps") == 0) |
346 | debug = 1; | |
347 | if (strcmp (option, "-v") == 0) | |
348 | verbose = 1; | |
349 | ||
350 | /* We've handled these LTO options, do not pass them on. */ | |
351 | if (strcmp (option, "-flto") == 0) | |
352 | lto_mode = LTO_MODE_LTO; | |
c04b6b38 RG |
353 | else if (strncmp (option, "-fwhopr", 7) == 0) |
354 | { | |
355 | lto_mode = LTO_MODE_WHOPR; | |
356 | if (option[7] == '=') | |
357 | { | |
358 | parallel = atoi (option+8); | |
359 | if (parallel <= 1) | |
360 | parallel = 0; | |
361 | } | |
362 | } | |
cf96bae7 RG |
363 | else |
364 | *argv_ptr++ = option; | |
365 | } | |
366 | ||
367 | if (linker_output) | |
368 | { | |
369 | char *output_dir, *base, *name; | |
d7f09764 | 370 | |
cf96bae7 RG |
371 | output_dir = xstrdup (linker_output); |
372 | base = output_dir; | |
373 | for (name = base; *name; name++) | |
374 | if (IS_DIR_SEPARATOR (*name)) | |
375 | base = name + 1; | |
376 | *base = '\0'; | |
377 | ||
378 | linker_output = &linker_output[base - output_dir]; | |
379 | if (*output_dir == '\0') | |
380 | { | |
381 | static char current_dir[] = { '.', DIR_SEPARATOR, '\0' }; | |
382 | output_dir = current_dir; | |
383 | } | |
384 | *argv_ptr++ = "-dumpdir"; | |
385 | *argv_ptr++ = output_dir; | |
386 | ||
387 | *argv_ptr++ = "-dumpbase"; | |
388 | } | |
389 | else | |
390 | argv_ptr--; | |
d7f09764 | 391 | |
d7f09764 DN |
392 | if (lto_mode == LTO_MODE_LTO) |
393 | { | |
394 | flto_out = make_temp_file (".lto.o"); | |
cf96bae7 RG |
395 | if (linker_output) |
396 | argv_ptr[0] = linker_output; | |
397 | argv_ptr[1] = "-o"; | |
398 | argv_ptr[2] = flto_out; | |
399 | argv_ptr[3] = "-combine"; | |
d7f09764 DN |
400 | } |
401 | else if (lto_mode == LTO_MODE_WHOPR) | |
402 | { | |
403 | const char *list_option = "-fltrans-output-list="; | |
404 | size_t list_option_len = strlen (list_option); | |
405 | char *tmp; | |
406 | ||
cf96bae7 RG |
407 | if (linker_output) |
408 | { | |
409 | char *dumpbase = (char *) xmalloc (strlen (linker_output) | |
b5611987 | 410 | + sizeof (".wpa") + 1); |
cf96bae7 RG |
411 | strcpy (dumpbase, linker_output); |
412 | strcat (dumpbase, ".wpa"); | |
413 | argv_ptr[0] = dumpbase; | |
414 | } | |
415 | ||
b5611987 RG |
416 | if (linker_output && debug) |
417 | { | |
418 | ltrans_output_file = (char *) xmalloc (strlen (linker_output) | |
419 | + sizeof (".ltrans.out") + 1); | |
420 | strcpy (ltrans_output_file, linker_output); | |
421 | strcat (ltrans_output_file, ".ltrans.out"); | |
422 | } | |
423 | else | |
424 | ltrans_output_file = make_temp_file (".ltrans.out"); | |
d7f09764 DN |
425 | list_option_full = (char *) xmalloc (sizeof (char) * |
426 | (strlen (ltrans_output_file) + list_option_len + 1)); | |
427 | tmp = list_option_full; | |
428 | ||
cf96bae7 | 429 | argv_ptr[1] = tmp; |
d7f09764 DN |
430 | strcpy (tmp, list_option); |
431 | tmp += list_option_len; | |
432 | strcpy (tmp, ltrans_output_file); | |
433 | ||
cf96bae7 RG |
434 | argv_ptr[2] = "-fwpa"; |
435 | argv_ptr[3] = "-combine"; | |
d7f09764 DN |
436 | } |
437 | else | |
438 | fatal ("invalid LTO mode"); | |
439 | ||
cf96bae7 RG |
440 | /* Append the input objects and possible preceeding arguments. */ |
441 | for (i = 1; i < argc; ++i) | |
442 | argv_ptr[3 + i] = argv[i]; | |
443 | argv_ptr[3 + i] = NULL; | |
d7f09764 DN |
444 | |
445 | fork_execute (CONST_CAST (char **, new_argv)); | |
48cf395b | 446 | |
d7f09764 DN |
447 | if (lto_mode == LTO_MODE_LTO) |
448 | { | |
449 | printf("%s\n", flto_out); | |
450 | free (flto_out); | |
451 | flto_out = NULL; | |
452 | } | |
453 | else if (lto_mode == LTO_MODE_WHOPR) | |
454 | { | |
455 | FILE *stream = fopen (ltrans_output_file, "r"); | |
c04b6b38 | 456 | FILE *mstream = NULL; |
d7f09764 DN |
457 | |
458 | if (!stream) | |
459 | fatal_perror ("fopen: %s", ltrans_output_file); | |
460 | ||
50ee30d5 RG |
461 | /* Parse the list of LTRANS inputs from the WPA stage. */ |
462 | nr = 0; | |
48cf395b RB |
463 | for (;;) |
464 | { | |
465 | const unsigned piece = 32; | |
50ee30d5 | 466 | char *output_name = NULL; |
48cf395b RB |
467 | char *buf, *input_name = (char *)xmalloc (piece); |
468 | size_t len; | |
469 | ||
470 | buf = input_name; | |
471 | cont: | |
472 | if (!fgets (buf, piece, stream)) | |
473 | break; | |
474 | len = strlen (input_name); | |
475 | if (input_name[len - 1] != '\n') | |
476 | { | |
477 | input_name = (char *)xrealloc (input_name, len + piece); | |
478 | buf = input_name + len; | |
479 | goto cont; | |
480 | } | |
481 | input_name[len - 1] = '\0'; | |
482 | ||
483 | if (input_name[0] == '*') | |
c04b6b38 | 484 | output_name = &input_name[1]; |
48cf395b | 485 | |
c04b6b38 RG |
486 | nr++; |
487 | input_names = (char **)xrealloc (input_names, nr * sizeof (char *)); | |
488 | output_names = (char **)xrealloc (output_names, nr * sizeof (char *)); | |
489 | input_names[nr-1] = input_name; | |
490 | output_names[nr-1] = output_name; | |
491 | } | |
50ee30d5 RG |
492 | fclose (stream); |
493 | maybe_unlink_file (ltrans_output_file); | |
494 | ltrans_output_file = NULL; | |
495 | ||
496 | if (parallel) | |
497 | { | |
498 | makefile = make_temp_file (".mk"); | |
499 | mstream = fopen (makefile, "w"); | |
500 | } | |
501 | ||
502 | /* Execute the LTRANS stage for each input file (or prepare a | |
503 | makefile to invoke this in parallel). */ | |
504 | for (i = 0; i < nr; ++i) | |
505 | { | |
506 | char *output_name; | |
507 | char *input_name = input_names[i]; | |
508 | /* If it's a pass-through file do nothing. */ | |
509 | if (output_names[i]) | |
510 | continue; | |
511 | ||
512 | /* Replace the .o suffix with a .ltrans.o suffix and write | |
513 | the resulting name to the LTRANS output list. */ | |
514 | obstack_init (&env_obstack); | |
515 | obstack_grow (&env_obstack, input_name, strlen (input_name) - 2); | |
516 | obstack_grow (&env_obstack, ".ltrans.o", sizeof (".ltrans.o")); | |
517 | output_name = XOBFINISH (&env_obstack, char *); | |
518 | ||
519 | /* Adjust the dumpbase if the linker output file was seen. */ | |
520 | if (linker_output) | |
521 | { | |
522 | char *dumpbase | |
523 | = (char *) xmalloc (strlen (linker_output) | |
524 | + sizeof(DUMPBASE_SUFFIX) + 1); | |
525 | snprintf (dumpbase, | |
526 | strlen (linker_output) + sizeof(DUMPBASE_SUFFIX), | |
b5611987 | 527 | "%s.ltrans%u", linker_output, i); |
50ee30d5 RG |
528 | argv_ptr[0] = dumpbase; |
529 | } | |
530 | ||
531 | argv_ptr[1] = "-fltrans"; | |
532 | argv_ptr[2] = "-o"; | |
533 | argv_ptr[3] = output_name; | |
534 | argv_ptr[4] = input_name; | |
535 | argv_ptr[5] = NULL; | |
536 | if (parallel) | |
537 | { | |
538 | fprintf (mstream, "%s:\n\t@%s ", output_name, new_argv[0]); | |
539 | for (j = 1; new_argv[j] != NULL; ++j) | |
540 | fprintf (mstream, " '%s'", new_argv[j]); | |
541 | fprintf (mstream, "\n"); | |
542 | } | |
543 | else | |
544 | fork_execute (CONST_CAST (char **, new_argv)); | |
545 | ||
546 | output_names[i] = output_name; | |
547 | } | |
c04b6b38 RG |
548 | if (parallel) |
549 | { | |
550 | struct pex_obj *pex; | |
551 | char jobs[32]; | |
552 | fprintf (mstream, "all:"); | |
553 | for (i = 0; i < nr; ++i) | |
554 | fprintf (mstream, " \\\n\t%s", output_names[i]); | |
555 | fprintf (mstream, "\n"); | |
556 | fclose (mstream); | |
5767217f RW |
557 | /* Avoid passing --jobserver-fd= and similar flags. */ |
558 | putenv (xstrdup ("MAKEFLAGS=")); | |
559 | putenv (xstrdup ("MFLAGS=")); | |
560 | new_argv[0] = getenv ("MAKE"); | |
561 | if (!new_argv[0]) | |
562 | new_argv[0] = "make"; | |
c04b6b38 RG |
563 | new_argv[1] = "-f"; |
564 | new_argv[2] = makefile; | |
565 | snprintf (jobs, 31, "-j%d", parallel); | |
566 | new_argv[3] = jobs; | |
567 | new_argv[4] = "all"; | |
568 | new_argv[5] = NULL; | |
569 | pex = collect_execute (CONST_CAST (char **, new_argv)); | |
570 | collect_wait (new_argv[0], pex); | |
571 | maybe_unlink_file (makefile); | |
50ee30d5 | 572 | makefile = NULL; |
c04b6b38 RG |
573 | } |
574 | for (i = 0; i < nr; ++i) | |
575 | { | |
576 | fputs (output_names[i], stdout); | |
48cf395b | 577 | putc ('\n', stdout); |
c04b6b38 RG |
578 | maybe_unlink_file (input_names[i]); |
579 | free (input_names[i]); | |
48cf395b | 580 | } |
50ee30d5 | 581 | nr = 0; |
c04b6b38 RG |
582 | free (output_names); |
583 | free (input_names); | |
d7f09764 DN |
584 | free (list_option_full); |
585 | } | |
586 | else | |
587 | fatal ("invalid LTO mode"); | |
d7f09764 | 588 | |
cf96bae7 | 589 | obstack_free (&env_obstack, NULL); |
d7f09764 DN |
590 | } |
591 | ||
592 | ||
593 | /* Entry point. */ | |
594 | ||
595 | int | |
596 | main (int argc, char *argv[]) | |
597 | { | |
d7f09764 DN |
598 | gcc_init_libintl (); |
599 | ||
600 | /* We may be called with all the arguments stored in some file and | |
601 | passed with @file. Expand them into argv before processing. */ | |
602 | expandargv (&argc, &argv); | |
cf96bae7 | 603 | run_gcc (argc, argv); |
d7f09764 DN |
604 | |
605 | return 0; | |
606 | } |