OSDN Git Service

use AS_HELP_STRING for all configure options
[uclinux-h8/elf2flt.git] / ld-elf2flt.c
1 /*
2  * Wrapper for the real linker and the elf2flt converter.  This was
3  * originally a simple shell script, but that doesn't work on a
4  * Windows host without cygwin.
5  * The proper long term solution is to add FLT as a BFD output format.
6  *
7  * Converted from ld-elf2flt.in by Nathan Sidwell, nathan@codesourcery.com.
8  * Updated to latest elf2flt code by Mike Frysinger, vapier@gentoo.org.
9  *
10  * This is Free Software, under the GNU General Public License V2 or greater.
11  *
12  * Copyright (C) 2006, CodeSourcery Inc.
13  * Copyright (C) 2009, Analog Devices, Inc.
14  * Copyright (C) 2002-2003 David McCullough <davidm@snapgear.com>
15  * Copyright (C) 2000, Lineo. <davidm@lineo.com>
16  */
17
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <stdarg.h>
21 #include <string.h>
22 #include <sys/types.h>
23 #include <sys/stat.h>
24 #include <unistd.h>
25 #include <errno.h>
26 #include <libiberty.h>
27 #include <filenames.h>
28
29 #include "stubs.h"
30 const char *elf2flt_progname;
31
32 static int flag_verbose = 0, flag_final = 1, have_elf2flt_options = 0,
33         flag_move_data = 0, want_shared = 0;
34 static const char *shared_lib_id = NULL;
35 static const char *output_file = "a.out";
36 static const char *linker_script = NULL;
37 static const char *emulation = NULL;
38 static const char *tmp_file = NULL;
39 static const char *output_gdb = NULL;
40 static const char *output_elf = NULL;
41 static const char *output_flt = NULL;
42 static options_t search_dirs, all_options, other_options, flt_options;
43
44 static const char *linker = NULL;
45 static const char *elf2flt = NULL;
46 static const char *nm = NULL;
47 static const char *objdump = NULL;
48 static const char *objcopy = NULL;
49 static const char *tooldir = ".";
50 static const char *ldscriptpath = BINUTILS_LDSCRIPTDIR;
51
52 /* A list of sed commands */
53 typedef struct {
54         options_t *pattern;      /* '^' for start of line match, everything else verbatim */
55         options_t *replacement;  /* Delete line, if NULL */
56 } sed_commands_t;
57
58 /* Initialize a sed structure */
59 #define init_sed(DST) ( \
60         (DST)->pattern = xmalloc(sizeof(*(DST)->pattern)), \
61         (DST)->replacement = xmalloc(sizeof(*(DST)->replacement)), \
62         init_options((DST)->pattern), \
63         init_options((DST)->replacement) \
64 )
65 #define free_sed(DST) (free((DST)->pattern), free((DST)->replacement))
66
67 /* Append a slot for a new sed command.  */
68 static void append_sed(sed_commands_t *dst, const char *pattern,
69                        const char *replacement)
70 {
71         debug1("adding pattern '%s' with replacement '%s'\n",
72                 pattern, replacement);
73         append_option(dst->pattern, pattern);
74         append_option(dst->replacement, replacement);
75 }
76
77 /* Execute an external program COMMAND.  Write its stdout to OUTPUT,
78    unless that is NULL.  Pass the trailing NULL terminated list of
79    options, followed by all those in OPTIONS, if that is non-NULL.
80    Order of options is important here as we may run on systems that
81    do not allow options after non-options (i.e. many BSDs).  So the
82    final command line will look like:
83    <command> [options] [... va args ...]
84    This is because [options] will (should?) never contain non-options,
85    while non-options will always be passed via the [va args].
86  */
87 static int
88 execute(const char *command, const char *output, const options_t *options, ...)
89 {
90         struct pex_obj *pex;
91         const char *errmsg;
92         int err;
93         int status;
94         va_list args;
95         const char *opt;
96         options_t opts;
97
98         debug("command=%s\n", command);
99
100         init_options(&opts);
101         append_option(&opts, command);
102         if (options)
103                 append_options(&opts, options);
104         va_start(args, options);
105         while ((opt = va_arg(args, const char *)))
106                 append_option(&opts, opt);
107         va_end(args);
108         append_option(&opts, NULL);
109
110         fflush(stdout);
111         fflush(stderr);
112
113         pex = pex_init(0, elf2flt_progname, NULL);
114         if (pex == NULL)
115                 fatal_perror("pex_init failed");
116
117         if (flag_verbose) {
118                 unsigned ix;
119
120                 fprintf(stderr, "Invoking:");
121                 for (ix = 0; ix != opts.num - 1; ix++)
122                         fprintf(stderr, " '%s'", opts.options[ix]);
123                 fprintf(stderr, "\n");
124         }
125
126         errmsg = pex_run(pex, PEX_LAST | PEX_SEARCH, command,
127                 (char *const *)opts.options, output, NULL, &err);
128         if (errmsg != NULL) {
129                 if (err != 0) {
130                         errno = err;
131                         fatal_perror(errmsg);
132                 } else
133                         fatal(errmsg);
134         }
135
136         if (!pex_get_status(pex, 1, &status))
137                 fatal_perror("can't get program status");
138         pex_free(pex);
139
140         if (status) {
141                 if (WIFSIGNALED(status)) {
142                         int sig = WTERMSIG(status);
143
144                         fatal("%s terminated with signal %d [%s]%s",
145                               command, sig, strsignal(sig),
146                               WCOREDUMP(status) ? ", core dumped" : "");
147                 }
148
149                 if (WIFEXITED(status))
150                         return WEXITSTATUS(status);
151         }
152         return 0;
153 }
154 /* Auto NULL terminate */
155 #define execute(...) execute(__VA_ARGS__, NULL)
156
157 /* Apply the sed commands in SED to file NAME_IN producing file NAME_OUT */
158 static void
159 do_sed(const sed_commands_t *sed, const char *name_in, const char *name_out)
160 {
161         FILE *in, *out;
162         size_t alloc = 0;
163         char *line = NULL;
164         ssize_t len;
165         const char *pattern, *replacement;
166         int ix;
167
168         if (flag_verbose) {
169                 fprintf(stderr, "emulating: sed \\\n");
170                 for (ix = 0; ix != sed->pattern->num; ix++) {
171                         pattern = sed->pattern->options[ix];
172                         replacement = sed->replacement->options[ix];
173                         if (replacement)
174                                 fprintf(stderr, "\t-e 's/%s/%s/' \\\n", pattern, replacement);
175                         else
176                                 fprintf(stderr, "\t-e 'd/%s/' \\\n", pattern);
177                 }
178                 fprintf(stderr, "\t%s > %s\n", name_in, name_out);
179         }
180
181         in = xfopen(name_in, "r");
182         out = xfopen(name_out, "w");
183
184         while ((len = getline(&line, &alloc, in)) > 0) {
185                 debug2("len=%2zi line=%s", len, line);
186
187                 for (ix = 0; ix != sed->pattern->num; ix++) {
188                         const char *ptr;
189                         int bol;
190                         size_t pat_len;
191
192                         pattern = sed->pattern->options[ix];
193                         replacement = sed->replacement->options[ix];
194                         ptr = line;
195                         bol = pattern[0] == '^';
196
197                         pattern += bol;
198                         pat_len = strlen(pattern);
199
200                         if (!bol) {
201                                 do {
202                                         ptr = strchr(ptr, pattern[0]);
203                                         if (!ptr) ;
204                                         else if (!strncmp(ptr, pattern, pat_len))
205                                                 goto found;
206                                         else
207                                                 ptr++;
208                                 }
209                                 while (ptr);
210                         } else if (!strncmp(ptr, pattern, pat_len)) {
211  found:
212                                 if (replacement) {
213                                         debug2(" [modified]\n");
214                                         fwrite(line, 1, ptr - line, out);
215                                         fwrite(replacement, 1, strlen(replacement), out);
216                                         fwrite(ptr + pat_len, 1,
217                                                len - pat_len - (ptr - line),
218                                                out);
219                                 } else
220                                         debug2("   {dropped}\n");
221                                 goto next_line;
222                         }
223                 }
224
225                 debug2("(untouched)\n");
226                 fwrite(line, 1, len, out);
227  next_line:
228                 ;
229         }
230         fclose(in);
231         if (fclose(out))
232                 fatal_perror("error writing temporary script '%s'", name_out);
233         free(line);
234 }
235
236 /* Generate the flt binary along with any other necessary pieces.  */
237 #define exec_or_ret(...) \
238         do { \
239                 int status = execute(__VA_ARGS__); \
240                 if (status) return status; \
241         } while (0)
242 static int do_final_link(void)
243 {
244         sed_commands_t sed;
245         struct stat buf;
246         const char *script;
247         const char *rel_output;
248         int have_got = 0;
249         FILE *in;
250         char *line = NULL;
251         size_t alloc = 0;
252         ssize_t len;
253
254         init_sed(&sed);
255
256         if (flag_move_data) {
257                 FILE *in;
258
259                 /* See if the .rodata section contains any relocations.  */
260                 if (!output_flt)
261                         output_flt = make_temp_file(NULL);
262                 exec_or_ret(linker, NULL, &other_options, "-r", "-d", "-o", output_flt);
263                 exec_or_ret(objdump, tmp_file, NULL, "-h", output_flt);
264
265                 in = xfopen(tmp_file, "r");
266                 while ((len = getline(&line, &alloc, in)) > 0) {
267                         const char *ptr = line;
268
269                         while (1) {
270                                 ptr = strchr(ptr, '.');
271                                 if (!ptr)
272                                         break;
273                                 if (streqn(ptr, ".rodata")) {
274                                         getline(&line, &alloc, in);
275                                         ptr = line;
276                                         while (1) {
277                                                 ptr = strchr(ptr, 'R');
278                                                 if (!ptr)
279                                                         break;
280                                                 if (streqn(ptr, "RELOC")) {
281                                                         flag_move_data = 0;
282                                                         fprintf(stderr, "warning: .rodata section contains relocations");
283                                                         break;
284                                                 } else
285                                                         ptr++;
286                                         }
287                                         break;
288                                 } else
289                                         ptr++;
290                         }
291                 }
292                 fclose(in);
293         }
294         append_sed(&sed, "^R_RODAT", flag_move_data ? NULL : "");
295         append_sed(&sed, "^W_RODAT", flag_move_data ? "" : NULL);
296         append_sed(&sed, "^SINGLE_LINK:", USE_EMIT_RELOCS ? "" : NULL);
297         append_sed(&sed, "^TOR:", EMIT_CTOR_DTOR ? "" : NULL);
298
299         if (shared_lib_id) {
300                 const char *got_offset;
301                 int adj, id = strtol(shared_lib_id, NULL, 0);
302                 char buf[30];
303
304                 /* Replace addresses using the shared object id.   */
305                 sprintf(buf, "%.2X", id);
306                 append_sed(&sed, "ORIGIN = 0x0,", concat("ORIGIN = 0x", buf, "000000,", NULL));
307                 append_sed(&sed, ".text 0x0 :", concat(".text 0x0", buf, "000000 :", NULL));
308                 if (id)
309                         append_sed(&sed, "ENTRY (" SYMBOL_PREFIX "_start)", "ENTRY (lib_main)");
310
311                 /* Provide the symbol specifying the library's data segment
312                    pointer offset.  */
313                 adj = 4;
314                 if (streq(TARGET_CPU, "h8300"))
315                         got_offset = "__current_shared_library_er5_offset_";
316                 else if (streq(TARGET_CPU, "bfin"))
317                         got_offset = "_current_shared_library_p5_offset_", adj = 1;
318                 else
319                         got_offset = "_current_shared_library_a5_offset_";
320                 append_option(&other_options, "-defsym");
321                 sprintf(buf, "%d", id * -adj - adj);
322                 append_option(&other_options, concat(got_offset, "=", buf, NULL));
323         }
324
325         /* Locate the default linker script, if we don't have one provided. */
326         if (!linker_script)
327                 linker_script = concat(ldscriptpath, "/elf2flt.ld", NULL);
328
329         /* Try and locate the linker script.  */
330         script = linker_script;
331         if (stat(script, &buf) || !S_ISREG(buf.st_mode)) {
332                 script = concat(ldscriptpath, "/", linker_script, NULL);
333                 if (stat(script, &buf) || !S_ISREG(buf.st_mode)) {
334                         script = concat(ldscriptpath, "/ldscripts/", linker_script, NULL);
335                         if (stat(script, &buf) || !S_ISREG(buf.st_mode))
336                                 script = NULL;
337                 }
338         }
339         /* And process it if we can -- if we can't find it, the user must
340            know what they are doing.  */
341         if (script) {
342                 do_sed(&sed, linker_script, tmp_file);
343                 linker_script = tmp_file;
344         }
345         free_sed(&sed);
346
347         if (USE_EMIT_RELOCS) {
348
349                 exec_or_ret(linker, NULL, &other_options,
350                         "-T", linker_script, "-q", "-o", output_gdb, emulation);
351
352                 append_option(&flt_options, "-a");
353                 rel_output = output_gdb;
354
355         } else if (NO_GOT_CHECK) {
356
357                 output_elf = make_temp_file(NULL);
358
359                 exec_or_ret(linker, NULL, &other_options,
360                         "-T", linker_script, "-Ur", "-d", "-o", output_elf, emulation);
361                 exec_or_ret(linker, NULL, &other_options,
362                         "-T", linker_script, "-o", output_gdb, emulation);
363
364                 rel_output = output_elf;
365
366         } else {
367
368                 output_flt = make_temp_file(NULL);
369                 exec_or_ret(linker, NULL, &other_options,
370                         "-r", "-d", "-o", output_flt, emulation);
371
372                 output_elf = make_temp_file(NULL);
373                 exec_or_ret(linker, NULL, &search_dirs,
374                         "-T", linker_script, "-Ur", "-o", output_elf, output_flt, emulation);
375
376                 exec_or_ret(linker, NULL, &search_dirs,
377                         "-T", linker_script, "-o", output_gdb, output_flt, emulation);
378
379                 rel_output = output_elf;
380
381         }
382
383         if (shared_lib_id && strtol(shared_lib_id, NULL, 0) != 0)
384                 exec_or_ret(objcopy, NULL, NULL, "--localize-hidden", "--weaken", output_gdb);
385
386         exec_or_ret(nm, tmp_file, NULL, "-p", output_gdb);
387         in = xfopen(tmp_file, "r");
388         while ((len = getline(&line, &alloc, in)) > 0) {
389                 const char *ptr = strchr(line, '_');
390                 if (ptr && streqn(ptr, "_GLOBAL_OFFSET_TABLE")) {
391                         have_got = 1;
392                         break;
393                 }
394         }
395         fclose(in);
396         if (have_got)
397                 exec_or_ret(elf2flt, NULL, &flt_options,
398                         "-o", output_file, "-p", output_gdb, rel_output);
399         else
400                 exec_or_ret(elf2flt, NULL, &flt_options,
401                         "-o", output_file, "-r", rel_output);
402
403         return 0;
404 }
405
406 /* parse all the arguments provided to us */
407 static void parse_args(int argc, char **argv)
408 {
409         char *fltflags;
410         int argno;
411
412         for (argno = 1; argno < argc; argno++) {
413                 char const *arg = argv[argno];
414                 int to_all = argno;
415
416                 if (streq(arg, "-elf2flt")) {
417                         have_elf2flt_options = 1;
418                         to_all++;
419                 } else if (streqn(arg, "-elf2flt=")) {
420                         have_elf2flt_options = 1;
421                         append_option_str(&flt_options, &arg[9], "\t ");
422                         to_all++;
423                 } else if (streq(arg, "-move-rodata")) {
424                         flag_move_data = 1;
425                 } else if (streq(arg, "-shared-lib-id")) {
426                         shared_lib_id = argv[++argno];
427                 } else if (streq(arg, "-shared") || streq(arg, "-G")) {
428                         want_shared = 1;
429                 } else if (streqn(arg, "-o")) {
430                         output_file = arg[2] ? &arg[2] : argv[++argno];
431                 } else if (streqn(arg, "-T")) {
432                         linker_script = arg[2] ? &arg[2] : argv[++argno];
433                 } else if (streq(arg, "-c")) {
434                         linker_script = argv[++argno];
435                 } else if (streqn(arg, "-L")) {
436                         const char *merged =
437                                 (arg[2] ? arg : concat("-L", argv[++argno], NULL));
438                         append_option(&other_options, merged);
439                         append_option(&search_dirs, merged);
440                 } else if (streq(arg, "-EB")) {
441                         append_option(&other_options, arg);
442                         append_option(&search_dirs, arg);
443                 } else if (streq(arg, "-relax")) {
444                         ;
445                 } else if (streq(arg, "-s") || streq(arg, "--strip-all") ||
446                            streq(arg, "-S") || streq(arg, "--strip-debug")) {
447                         /* Ignore these strip options for links involving elf2flt.
448                            The final flat output will be stripped by definition, and we
449                            don't want to strip the .gdb helper file.  The strip options
450                            are also incompatible with -r and --emit-relocs.  */
451                         ;
452                 } else if (streq(arg, "-r") || streq(arg, "-Ur")) {
453                         flag_final = 0;
454                         append_option(&other_options, arg);
455                 } else if (streq(arg, "-v") || streq(arg, "--verbose")) {
456                         flag_verbose = 1;
457                         append_option(&other_options, arg);
458                 } else if (streqn(arg, "-m")) {
459                         emulation = arg[2] ? arg : concat("-m", argv[++argno], NULL);
460                 } else
461                         append_option(&other_options, arg);
462
463                 while (to_all <= argno)
464                         append_option(&all_options, argv[to_all++]);
465         }
466
467         fltflags = getenv("FLTFLAGS");
468         if (fltflags)
469                 append_option_str(&flt_options, fltflags, "\t ");
470 }
471
472 int main(int argc, char *argv[])
473 {
474         const char *ptr;
475         char *tmp;
476         const char *argv0 = argv[0];
477         size_t len;
478         struct stat buf;
479         const char *have_exe = NULL;
480         int status;
481
482         len = strlen(argv0);
483 #ifdef __WIN32
484         /* Remove the .exe extension, if it's there.  */
485         if (len > 4 && streq(&argv0[len - 4], ".exe")) {
486                 have_exe = ".exe";
487                 len -= 4;
488                 argv0 = tmp = xstrdup(argv0);
489                 tmp[len] = 0;
490         }
491 #endif
492         elf2flt_progname = argv0;
493         for (ptr = elf2flt_progname + len; ptr != elf2flt_progname; ptr--)
494                 if (IS_DIR_SEPARATOR(ptr[-1])) {
495                         tooldir = tmp = xmalloc(len);
496                         memcpy(tmp, argv0, len);
497                         tmp[ptr - elf2flt_progname - 1] = 0;
498                         elf2flt_progname = ptr;
499                         /* The standard binutils tool layout has:
500
501                            bin/<TARGET_ALIAS>-foo
502                            lib/
503                            <TARGET_ALIAS>/bin/foo
504                            <TARGET_ALIAS>/lib
505
506                            It's <TARGET_ALIAS>/ that we want here: files in lib/ are for
507                            the host while those in <TARGET_ALIAS>/lib are for the target.  */
508                         if (streqn(elf2flt_progname, TARGET_ALIAS)) {
509                                 tmp = concat(tooldir, "/../" TARGET_ALIAS "/bin", NULL);
510                                 if (stat(tmp, &buf) == 0 && S_ISDIR(buf.st_mode))
511                                         tooldir = tmp;
512                         }
513                         break;
514                 }
515         /* Typically ld-elf2flt is invoked as `ld` which means error
516          * messages from it will look like "ld: " which is completely
517          * confusing.  So append an identifier to keep things clear.
518          */
519         elf2flt_progname = concat(elf2flt_progname, " (ld-elf2flt)", NULL);
520
521         xmalloc_set_program_name(elf2flt_progname);
522
523         tmp = xmalloc(len + 16);
524         memcpy(tmp, argv0, len);
525         while (len && tmp[len - 1] != '-' && !IS_DIR_SEPARATOR(tmp[len - 1]))
526                 len--;
527         tmp[len] = 0;
528
529         linker = concat(tmp, "ld.real", have_exe, NULL);
530         elf2flt = concat(tmp, "elf2flt", have_exe, NULL);
531         nm = concat(tmp, "nm", have_exe, NULL);
532         objdump = concat(tooldir, "/../../bin/", TARGET_ALIAS, "-objdump", have_exe, NULL);
533         objcopy = concat(tooldir, "/../../bin/", TARGET_ALIAS, "-objcopy", have_exe, NULL);
534
535         if (stat(ldscriptpath, &buf) || !S_ISDIR(buf.st_mode))
536                 ldscriptpath = concat(tooldir, "/../lib", NULL);
537
538         parse_args(argc, argv);
539
540         if (flag_verbose) {
541                 fprintf(stderr, "argv[0]      = '%s'\n", argv[0]);
542                 fprintf(stderr, "tooldir      = '%s'\n", tooldir);
543                 fprintf(stderr, "linker       = '%s'\n", linker);
544                 fprintf(stderr, "elf2flt      = '%s'\n", elf2flt);
545                 fprintf(stderr, "nm           = '%s'\n", nm);
546                 fprintf(stderr, "objdump      = '%s'\n", objdump);
547                 fprintf(stderr, "objcopy      = '%s'\n", objcopy);
548                 fprintf(stderr, "ldscriptpath = '%s'\n", ldscriptpath);
549         }
550
551         /* Pass off to regular linker, if there's nothing elf2flt-like */
552         if (!have_elf2flt_options)
553                 return execute(linker, NULL, &all_options);
554
555         /* Pass off to regular linker, minus the elf2flt options, if it's
556            not the final link.  */
557         if (!flag_final)
558                 return execute(linker, NULL, &other_options, "-o", output_file);
559
560         if (want_shared && !shared_lib_id)
561                 fatal("-shared used without passing a shared library ID");
562
563         /* Otherwise link & convert to flt.  */
564         output_gdb = concat(output_file, ".gdb", NULL);
565         tmp_file = make_temp_file(NULL);
566         status = do_final_link();
567         if (!flag_verbose) {
568                 unlink(tmp_file);
569                 unlink(output_flt);
570                 unlink(output_elf);
571         } else {
572                 fprintf(stderr,
573                         "leaving elf2flt temp files behind:\n"
574                         "tmp_file   = %s\n"
575                         "output_flt = %s\n"
576                         "output_elf = %s\n",
577                         tmp_file, output_flt, output_elf);
578         }
579         return status;
580 }