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.
7 * Converted from ld-elf2flt.in by Nathan Sidwell, nathan@codesourcery.com.
8 * Updated to latest elf2flt code by Mike Frysinger, vapier@gentoo.org.
10 * This is Free Software, under the GNU General Public License V2 or greater.
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>
22 #include <sys/types.h>
26 #include <libiberty.h>
27 #include <filenames.h>
30 const char *elf2flt_progname;
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;
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;
52 /* A list of sed commands */
54 options_t *pattern; /* '^' for start of line match, everything else verbatim */
55 options_t *replacement; /* Delete line, if NULL */
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) \
65 #define free_sed(DST) (free((DST)->pattern), free((DST)->replacement))
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)
71 debug1("adding pattern '%s' with replacement '%s'\n",
72 pattern, replacement);
73 append_option(dst->pattern, pattern);
74 append_option(dst->replacement, replacement);
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].
88 execute(const char *command, const char *output, const options_t *options, ...)
98 debug("command=%s\n", command);
101 append_option(&opts, command);
103 append_options(&opts, options);
104 va_start(args, options);
105 while ((opt = va_arg(args, const char *)))
106 append_option(&opts, opt);
108 append_option(&opts, NULL);
113 pex = pex_init(0, elf2flt_progname, NULL);
115 fatal_perror("pex_init failed");
120 fprintf(stderr, "Invoking:");
121 for (ix = 0; ix != opts.num - 1; ix++)
122 fprintf(stderr, " '%s'", opts.options[ix]);
124 fprintf(stderr, " > '%s'", output);
125 fprintf(stderr, "\n");
128 errmsg = pex_run(pex, PEX_LAST | PEX_SEARCH, command,
129 (char *const *)opts.options, output, NULL, &err);
130 if (errmsg != NULL) {
133 fatal_perror(errmsg);
138 if (!pex_get_status(pex, 1, &status))
139 fatal_perror("can't get program status");
143 if (WIFSIGNALED(status)) {
144 int sig = WTERMSIG(status);
146 fatal("%s terminated with signal %d [%s]%s",
147 command, sig, strsignal(sig),
148 WCOREDUMP(status) ? ", core dumped" : "");
151 if (WIFEXITED(status))
152 return WEXITSTATUS(status);
156 /* Auto NULL terminate */
157 #define execute(...) execute(__VA_ARGS__, NULL)
159 /* Apply the sed commands in SED to file NAME_IN producing file NAME_OUT */
161 do_sed(const sed_commands_t *sed, const char *name_in, const char *name_out)
167 const char *pattern, *replacement;
171 fprintf(stderr, "emulating: sed \\\n");
172 for (ix = 0; ix != sed->pattern->num; ix++) {
173 pattern = sed->pattern->options[ix];
174 replacement = sed->replacement->options[ix];
176 fprintf(stderr, "\t-e 's/%s/%s/' \\\n", pattern, replacement);
178 fprintf(stderr, "\t-e '/%s/d' \\\n", pattern);
180 fprintf(stderr, "\t%s > %s\n", name_in, name_out);
183 in = xfopen(name_in, "r");
184 out = xfopen(name_out, "w");
186 while ((len = getline(&line, &alloc, in)) > 0) {
187 debug2("len=%2zi line=%s", len, line);
189 for (ix = 0; ix != sed->pattern->num; ix++) {
194 pattern = sed->pattern->options[ix];
195 replacement = sed->replacement->options[ix];
197 bol = pattern[0] == '^';
200 pat_len = strlen(pattern);
204 ptr = strchr(ptr, pattern[0]);
206 else if (!strncmp(ptr, pattern, pat_len))
212 } else if (!strncmp(ptr, pattern, pat_len)) {
215 debug2(" [modified]\n");
216 fwrite(line, 1, ptr - line, out);
217 fwrite(replacement, 1, strlen(replacement), out);
218 fwrite(ptr + pat_len, 1,
219 len - pat_len - (ptr - line),
222 debug2(" {dropped}\n");
227 debug2("(untouched)\n");
228 fwrite(line, 1, len, out);
234 fatal_perror("error writing temporary script '%s'", name_out);
238 /* Generate the flt binary along with any other necessary pieces. */
239 #define exec_or_ret(...) \
241 int status = execute(__VA_ARGS__); \
242 if (status) return status; \
244 static int do_final_link(void)
249 const char *rel_output;
258 if (flag_move_data) {
261 /* See if the .rodata section contains any relocations. */
263 output_flt = make_temp_file(NULL);
264 exec_or_ret(linker, NULL, &other_options, "-r", "-d", "-o", output_flt);
265 exec_or_ret(objdump, tmp_file, NULL, "-h", output_flt);
267 in = xfopen(tmp_file, "r");
268 while ((len = getline(&line, &alloc, in)) > 0) {
269 const char *ptr = line;
272 ptr = strchr(ptr, '.');
275 if (streqn(ptr, ".rodata")) {
276 getline(&line, &alloc, in);
279 ptr = strchr(ptr, 'R');
282 if (streqn(ptr, "RELOC")) {
284 fprintf(stderr, "warning: .rodata section contains relocations");
296 append_sed(&sed, "^R_RODAT", flag_move_data ? NULL : "");
297 append_sed(&sed, "^W_RODAT", flag_move_data ? "" : NULL);
298 append_sed(&sed, "^SINGLE_LINK:", USE_EMIT_RELOCS ? "" : NULL);
299 append_sed(&sed, "^TOR:", EMIT_CTOR_DTOR ? "" : NULL);
302 const char *got_offset;
303 int adj, id = strtol(shared_lib_id, NULL, 0);
306 /* Replace addresses using the shared object id. */
307 sprintf(buf, "%.2X", id);
308 append_sed(&sed, "ORIGIN = 0x0,", concat("ORIGIN = 0x", buf, "000000,", NULL));
309 append_sed(&sed, ".text 0x0 :", concat(".text 0x0", buf, "000000 :", NULL));
311 append_sed(&sed, "ENTRY (" SYMBOL_PREFIX "_start)", "ENTRY (lib_main)");
313 /* Provide the symbol specifying the library's data segment
316 if (streq(TARGET_CPU, "h8300"))
317 got_offset = "__current_shared_library_er5_offset_";
318 else if (streq(TARGET_CPU, "bfin"))
319 got_offset = "_current_shared_library_p5_offset_", adj = 1;
321 got_offset = "_current_shared_library_a5_offset_";
322 append_option(&other_options, "-defsym");
323 sprintf(buf, "%d", id * -adj - adj);
324 append_option(&other_options, concat(got_offset, "=", buf, NULL));
327 /* Locate the default linker script, if we don't have one provided. */
329 linker_script = concat(ldscriptpath, "/elf2flt.ld", NULL);
331 /* Try and locate the linker script. */
332 script = linker_script;
333 if (stat(script, &buf) || !S_ISREG(buf.st_mode)) {
334 script = concat(ldscriptpath, "/", linker_script, NULL);
335 if (stat(script, &buf) || !S_ISREG(buf.st_mode)) {
336 script = concat(ldscriptpath, "/ldscripts/", linker_script, NULL);
337 if (stat(script, &buf) || !S_ISREG(buf.st_mode))
341 /* And process it if we can -- if we can't find it, the user must
342 know what they are doing. */
344 do_sed(&sed, linker_script, tmp_file);
345 linker_script = tmp_file;
349 if (USE_EMIT_RELOCS) {
351 exec_or_ret(linker, NULL, &other_options,
352 "-T", linker_script, "-q", "-o", output_gdb, emulation);
354 append_option(&flt_options, "-a");
355 rel_output = output_gdb;
357 } else if (NO_GOT_CHECK) {
359 output_elf = make_temp_file(NULL);
361 exec_or_ret(linker, NULL, &other_options,
362 "-T", linker_script, "-Ur", "-d", "-o", output_elf, emulation);
363 exec_or_ret(linker, NULL, &other_options,
364 "-T", linker_script, "-o", output_gdb, emulation);
366 rel_output = output_elf;
370 output_flt = make_temp_file(NULL);
371 exec_or_ret(linker, NULL, &other_options,
372 "-r", "-d", "-o", output_flt, emulation);
374 output_elf = make_temp_file(NULL);
375 exec_or_ret(linker, NULL, &search_dirs,
376 "-T", linker_script, "-Ur", "-o", output_elf, output_flt, emulation);
378 exec_or_ret(linker, NULL, &search_dirs,
379 "-T", linker_script, "-o", output_gdb, output_flt, emulation);
381 rel_output = output_elf;
385 if (shared_lib_id && strtol(shared_lib_id, NULL, 0) != 0)
386 exec_or_ret(objcopy, NULL, NULL, "--localize-hidden", "--weaken", output_gdb);
388 exec_or_ret(nm, tmp_file, NULL, "-p", output_gdb);
389 in = xfopen(tmp_file, "r");
390 while ((len = getline(&line, &alloc, in)) > 0) {
391 const char *ptr = strchr(line, '_');
392 if (ptr && streqn(ptr, "_GLOBAL_OFFSET_TABLE")) {
399 exec_or_ret(elf2flt, NULL, &flt_options,
400 "-o", output_file, "-p", output_gdb, rel_output);
402 exec_or_ret(elf2flt, NULL, &flt_options,
403 "-o", output_file, "-r", rel_output);
408 /* parse all the arguments provided to us */
409 static void parse_args(int argc, char **argv)
414 for (argno = 1; argno < argc; argno++) {
415 char const *arg = argv[argno];
418 if (streq(arg, "-elf2flt")) {
419 have_elf2flt_options = 1;
421 } else if (streqn(arg, "-elf2flt=")) {
422 have_elf2flt_options = 1;
423 append_option_str(&flt_options, &arg[9], "\t ");
425 } else if (streq(arg, "-move-rodata")) {
427 } else if (streq(arg, "-shared-lib-id")) {
428 shared_lib_id = argv[++argno];
429 } else if (streq(arg, "-shared") || streq(arg, "-G")) {
431 } else if (streqn(arg, "-o")) {
432 output_file = arg[2] ? &arg[2] : argv[++argno];
433 } else if (streqn(arg, "-T")) {
434 linker_script = arg[2] ? &arg[2] : argv[++argno];
435 } else if (streq(arg, "-c")) {
436 linker_script = argv[++argno];
437 } else if (streqn(arg, "-L")) {
439 (arg[2] ? arg : concat("-L", argv[++argno], NULL));
440 append_option(&other_options, merged);
441 append_option(&search_dirs, merged);
442 } else if (streq(arg, "-EB")) {
443 append_option(&other_options, arg);
444 append_option(&search_dirs, arg);
445 } else if (streq(arg, "-relax")) {
447 } else if (streq(arg, "-s") || streq(arg, "--strip-all") ||
448 streq(arg, "-S") || streq(arg, "--strip-debug")) {
449 /* Ignore these strip options for links involving elf2flt.
450 The final flat output will be stripped by definition, and we
451 don't want to strip the .gdb helper file. The strip options
452 are also incompatible with -r and --emit-relocs. */
454 } else if (streq(arg, "-r") || streq(arg, "-Ur")) {
456 append_option(&other_options, arg);
457 } else if (streq(arg, "-v") || streq(arg, "--verbose")) {
459 append_option(&other_options, arg);
460 } else if (streqn(arg, "-m")) {
461 emulation = arg[2] ? arg : concat("-m", argv[++argno], NULL);
463 append_option(&other_options, arg);
465 while (to_all <= argno)
466 append_option(&all_options, argv[to_all++]);
469 fltflags = getenv("FLTFLAGS");
471 append_option_str(&flt_options, fltflags, "\t ");
474 int main(int argc, char *argv[])
478 const char *argv0 = argv[0];
481 const char *have_exe = NULL;
486 /* Remove the .exe extension, if it's there. */
487 if (len > 4 && streq(&argv0[len - 4], ".exe")) {
490 argv0 = tmp = xstrdup(argv0);
494 elf2flt_progname = argv0;
495 for (ptr = elf2flt_progname + len; ptr != elf2flt_progname; ptr--)
496 if (IS_DIR_SEPARATOR(ptr[-1])) {
497 tooldir = tmp = xmalloc(len);
498 memcpy(tmp, argv0, len);
499 tmp[ptr - elf2flt_progname - 1] = 0;
500 elf2flt_progname = ptr;
501 /* The standard binutils tool layout has:
503 bin/<TARGET_ALIAS>-foo
505 <TARGET_ALIAS>/bin/foo
508 It's <TARGET_ALIAS>/ that we want here: files in lib/ are for
509 the host while those in <TARGET_ALIAS>/lib are for the target. */
510 if (streqn(elf2flt_progname, TARGET_ALIAS)) {
511 tmp = concat(tooldir, "/../" TARGET_ALIAS "/bin", NULL);
512 if (stat(tmp, &buf) == 0 && S_ISDIR(buf.st_mode))
517 /* Typically ld-elf2flt is invoked as `ld` which means error
518 * messages from it will look like "ld: " which is completely
519 * confusing. So append an identifier to keep things clear.
521 elf2flt_progname = concat(elf2flt_progname, " (ld-elf2flt)", NULL);
523 xmalloc_set_program_name(elf2flt_progname);
525 tmp = xmalloc(len + 16);
526 memcpy(tmp, argv0, len);
527 while (len && tmp[len - 1] != '-' && !IS_DIR_SEPARATOR(tmp[len - 1]))
531 linker = concat(tmp, "ld.real", have_exe, NULL);
532 elf2flt = concat(tmp, "elf2flt", have_exe, NULL);
533 nm = concat(tmp, "nm", have_exe, NULL);
534 objdump = concat(tooldir, "/../../bin/", TARGET_ALIAS, "-objdump", have_exe, NULL);
535 objcopy = concat(tooldir, "/../../bin/", TARGET_ALIAS, "-objcopy", have_exe, NULL);
537 if (stat(ldscriptpath, &buf) || !S_ISDIR(buf.st_mode))
538 ldscriptpath = concat(tooldir, "/../lib", NULL);
540 parse_args(argc, argv);
543 fprintf(stderr, "argv[0] = '%s'\n", argv[0]);
544 fprintf(stderr, "tooldir = '%s'\n", tooldir);
545 fprintf(stderr, "linker = '%s'\n", linker);
546 fprintf(stderr, "elf2flt = '%s'\n", elf2flt);
547 fprintf(stderr, "nm = '%s'\n", nm);
548 fprintf(stderr, "objdump = '%s'\n", objdump);
549 fprintf(stderr, "objcopy = '%s'\n", objcopy);
550 fprintf(stderr, "ldscriptpath = '%s'\n", ldscriptpath);
553 /* Pass off to regular linker, if there's nothing elf2flt-like */
554 if (!have_elf2flt_options)
555 return execute(linker, NULL, &all_options);
557 /* Pass off to regular linker, minus the elf2flt options, if it's
558 not the final link. */
560 return execute(linker, NULL, &other_options, "-o", output_file);
562 if (want_shared && !shared_lib_id)
563 fatal("-shared used without passing a shared library ID");
565 /* Otherwise link & convert to flt. */
566 output_gdb = concat(output_file, ".gdb", NULL);
567 tmp_file = make_temp_file(NULL);
568 status = do_final_link();
575 "leaving elf2flt temp files behind:\n"
579 tmp_file, output_flt, output_elf);