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]);
123 fprintf(stderr, "\n");
126 errmsg = pex_run(pex, PEX_LAST | PEX_SEARCH, command,
127 (char *const *)opts.options, output, NULL, &err);
128 if (errmsg != NULL) {
131 fatal_perror(errmsg);
136 if (!pex_get_status(pex, 1, &status))
137 fatal_perror("can't get program status");
141 if (WIFSIGNALED(status)) {
142 int sig = WTERMSIG(status);
144 fatal("%s terminated with signal %d [%s]%s",
145 command, sig, strsignal(sig),
146 WCOREDUMP(status) ? ", core dumped" : "");
149 if (WIFEXITED(status))
150 return WEXITSTATUS(status);
154 /* Auto NULL terminate */
155 #define execute(...) execute(__VA_ARGS__, NULL)
157 /* Apply the sed commands in SED to file NAME_IN producing file NAME_OUT */
159 do_sed(const sed_commands_t *sed, const char *name_in, const char *name_out)
165 const char *pattern, *replacement;
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];
174 fprintf(stderr, "\t-e 's/%s/%s/' \\\n", pattern, replacement);
176 fprintf(stderr, "\t-e 'd/%s/' \\\n", pattern);
178 fprintf(stderr, "\t%s > %s\n", name_in, name_out);
181 in = xfopen(name_in, "r");
182 out = xfopen(name_out, "w");
184 while ((len = getline(&line, &alloc, in)) > 0) {
185 debug2("len=%2zi line=%s", len, line);
187 for (ix = 0; ix != sed->pattern->num; ix++) {
192 pattern = sed->pattern->options[ix];
193 replacement = sed->replacement->options[ix];
195 bol = pattern[0] == '^';
198 pat_len = strlen(pattern);
202 ptr = strchr(ptr, pattern[0]);
204 else if (!strncmp(ptr, pattern, pat_len))
210 } else if (!strncmp(ptr, pattern, pat_len)) {
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),
220 debug2(" {dropped}\n");
225 debug2("(untouched)\n");
226 fwrite(line, 1, len, out);
232 fatal_perror("error writing temporary script '%s'", name_out);
236 /* Generate the flt binary along with any other necessary pieces. */
237 #define exec_or_ret(...) \
239 int status = execute(__VA_ARGS__); \
240 if (status) return status; \
242 static int do_final_link(void)
247 const char *rel_output;
256 if (flag_move_data) {
259 /* See if the .rodata section contains any relocations. */
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);
265 in = xfopen(tmp_file, "r");
266 while ((len = getline(&line, &alloc, in)) > 0) {
267 const char *ptr = line;
270 ptr = strchr(ptr, '.');
273 if (streqn(ptr, ".rodata")) {
274 getline(&line, &alloc, in);
277 ptr = strchr(ptr, 'R');
280 if (streqn(ptr, "RELOC")) {
282 fprintf(stderr, "warning: .rodata section contains relocations");
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);
300 const char *got_offset;
301 int adj, id = strtol(shared_lib_id, NULL, 0);
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));
309 append_sed(&sed, "ENTRY (" SYMBOL_PREFIX "_start)", "ENTRY (lib_main)");
311 /* Provide the symbol specifying the library's data segment
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;
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));
325 /* Locate the default linker script, if we don't have one provided. */
327 linker_script = concat(ldscriptpath, "/elf2flt.ld", NULL);
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))
339 /* And process it if we can -- if we can't find it, the user must
340 know what they are doing. */
342 do_sed(&sed, linker_script, tmp_file);
343 linker_script = tmp_file;
347 if (USE_EMIT_RELOCS) {
349 exec_or_ret(linker, NULL, &other_options,
350 "-T", linker_script, "-q", "-o", output_gdb, emulation);
352 append_option(&flt_options, "-a");
353 rel_output = output_gdb;
355 } else if (NO_GOT_CHECK) {
357 output_elf = make_temp_file(NULL);
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);
364 rel_output = output_elf;
368 output_flt = make_temp_file(NULL);
369 exec_or_ret(linker, NULL, &other_options,
370 "-r", "-d", "-o", output_flt, emulation);
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);
376 exec_or_ret(linker, NULL, &search_dirs,
377 "-T", linker_script, "-o", output_gdb, output_flt, emulation);
379 rel_output = output_elf;
383 if (shared_lib_id && strtol(shared_lib_id, NULL, 0) != 0)
384 exec_or_ret(objcopy, NULL, NULL, "--localize-hidden", "--weaken", output_gdb);
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")) {
397 exec_or_ret(elf2flt, NULL, &flt_options,
398 "-o", output_file, "-p", output_gdb, rel_output);
400 exec_or_ret(elf2flt, NULL, &flt_options,
401 "-o", output_file, "-r", rel_output);
406 /* parse all the arguments provided to us */
407 static void parse_args(int argc, char **argv)
412 for (argno = 1; argno < argc; argno++) {
413 char const *arg = argv[argno];
416 if (streq(arg, "-elf2flt")) {
417 have_elf2flt_options = 1;
419 } else if (streqn(arg, "-elf2flt=")) {
420 have_elf2flt_options = 1;
421 append_option_str(&flt_options, &arg[9], "\t ");
423 } else if (streq(arg, "-move-rodata")) {
425 } else if (streq(arg, "-shared-lib-id")) {
426 shared_lib_id = argv[++argno];
427 } else if (streq(arg, "-shared") || streq(arg, "-G")) {
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")) {
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")) {
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. */
452 } else if (streq(arg, "-r") || streq(arg, "-Ur")) {
454 append_option(&other_options, arg);
455 } else if (streq(arg, "-v") || streq(arg, "--verbose")) {
457 append_option(&other_options, arg);
458 } else if (streqn(arg, "-m")) {
459 emulation = arg[2] ? arg : concat("-m", argv[++argno], NULL);
461 append_option(&other_options, arg);
463 while (to_all <= argno)
464 append_option(&all_options, argv[to_all++]);
467 fltflags = getenv("FLTFLAGS");
469 append_option_str(&flt_options, fltflags, "\t ");
472 int main(int argc, char *argv[])
476 const char *argv0 = argv[0];
479 const char *have_exe = NULL;
484 /* Remove the .exe extension, if it's there. */
485 if (len > 4 && streq(&argv0[len - 4], ".exe")) {
488 argv0 = tmp = xstrdup(argv0);
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:
501 bin/<TARGET_ALIAS>-foo
503 <TARGET_ALIAS>/bin/foo
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))
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.
519 elf2flt_progname = concat(elf2flt_progname, " (ld-elf2flt)", NULL);
521 xmalloc_set_program_name(elf2flt_progname);
523 tmp = xmalloc(len + 16);
524 memcpy(tmp, argv0, len);
525 while (len && tmp[len - 1] != '-' && !IS_DIR_SEPARATOR(tmp[len - 1]))
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);
535 if (stat(ldscriptpath, &buf) || !S_ISDIR(buf.st_mode))
536 ldscriptpath = concat(tooldir, "/../lib", NULL);
538 parse_args(argc, argv);
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);
551 /* Pass off to regular linker, if there's nothing elf2flt-like */
552 if (!have_elf2flt_options)
553 return execute(linker, NULL, &all_options);
555 /* Pass off to regular linker, minus the elf2flt options, if it's
556 not the final link. */
558 return execute(linker, NULL, &other_options, "-o", output_file);
560 if (want_shared && !shared_lib_id)
561 fatal("-shared used without passing a shared library ID");
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();
573 "leaving elf2flt temp files behind:\n"
577 tmp_file, output_flt, output_elf);