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. */
81 execute(const char *command, const char *output, const options_t *options, ...)
91 debug("command=%s\n", command);
94 append_option(&opts, command);
95 va_start(args, options);
96 while ((opt = va_arg(args, const char *)))
97 append_option(&opts, opt);
100 append_options(&opts, options);
101 append_option(&opts, NULL);
106 pex = pex_init(0, elf2flt_progname, NULL);
108 fatal_perror("pex_init failed");
113 fprintf(stderr, "Invoking:");
114 for (ix = 0; ix != opts.num - 1; ix++)
115 fprintf(stderr, " '%s'", opts.options[ix]);
116 fprintf(stderr, "\n");
119 errmsg = pex_run(pex, PEX_LAST | PEX_SEARCH, command,
120 (char *const *)opts.options, output, NULL, &err);
121 if (errmsg != NULL) {
124 fatal_perror(errmsg);
129 if (!pex_get_status(pex, 1, &status))
130 fatal_perror("can't get program status");
134 if (WIFSIGNALED(status)) {
135 int sig = WTERMSIG(status);
137 fatal("%s terminated with signal %d [%s]%s",
138 command, sig, strsignal(sig),
139 WCOREDUMP(status) ? ", core dumped" : "");
142 if (WIFEXITED(status))
143 return WEXITSTATUS(status);
147 /* Auto NULL terminate */
148 #define execute(...) execute(__VA_ARGS__, NULL)
150 /* Apply the sed commands in SED to file NAME_IN producing file NAME_OUT */
152 do_sed(const sed_commands_t *sed, const char *name_in, const char *name_out)
158 const char *pattern, *replacement;
162 fprintf(stderr, "emulating: sed \\\n");
163 for (ix = 0; ix != sed->pattern->num; ix++) {
164 pattern = sed->pattern->options[ix];
165 replacement = sed->replacement->options[ix];
167 fprintf(stderr, "\t-e 's/%s/%s/' \\\n", pattern, replacement);
169 fprintf(stderr, "\t-e 'd/%s/' \\\n", pattern);
171 fprintf(stderr, "\t%s > %s\n", name_in, name_out);
174 in = xfopen(name_in, "r");
175 out = xfopen(name_out, "w");
177 while ((len = getline(&line, &alloc, in)) > 0) {
178 debug2("len=%2zi line=%s", len, line);
180 for (ix = 0; ix != sed->pattern->num; ix++) {
185 pattern = sed->pattern->options[ix];
186 replacement = sed->replacement->options[ix];
188 bol = pattern[0] == '^';
191 pat_len = strlen(pattern);
195 ptr = strchr(ptr, pattern[0]);
197 else if (!strncmp(ptr, pattern, pat_len))
203 } else if (!strncmp(ptr, pattern, pat_len)) {
206 debug2(" [modified]\n");
207 fwrite(line, 1, ptr - line, out);
208 fwrite(replacement, 1, strlen(replacement), out);
209 fwrite(ptr + pat_len, 1,
210 len - pat_len - (ptr - line),
213 debug2(" {dropped}\n");
218 debug2("(untouched)\n");
219 fwrite(line, 1, len, out);
225 fatal_perror("error writing temporary script '%s'", name_out);
229 /* Generate the flt binary along with any other necessary pieces. */
230 #define exec_or_ret(...) \
232 int status = execute(__VA_ARGS__); \
233 if (status) return status; \
235 static int do_final_link(void)
240 const char *rel_output;
249 if (flag_move_data) {
252 /* See if the .rodata section contains any relocations. */
254 output_flt = make_temp_file(NULL);
255 exec_or_ret(linker, NULL, &other_options, "-r", "-d", "-o", output_flt);
256 exec_or_ret(objdump, tmp_file, NULL, "-h", output_flt);
258 in = xfopen(tmp_file, "r");
259 while ((len = getline(&line, &alloc, in)) > 0) {
260 const char *ptr = line;
263 ptr = strchr(ptr, '.');
266 if (streqn(ptr, ".rodata")) {
267 getline(&line, &alloc, in);
270 ptr = strchr(ptr, 'R');
273 if (streqn(ptr, "RELOC")) {
275 fprintf(stderr, "warning: .rodata section contains relocations");
287 append_sed(&sed, "^R_RODAT", flag_move_data ? NULL : "");
288 append_sed(&sed, "^W_RODAT", flag_move_data ? "" : NULL);
289 append_sed(&sed, "^SINGLE_LINK:", USE_EMIT_RELOCS ? "" : NULL);
290 append_sed(&sed, "^TOR:", EMIT_CTOR_DTOR ? "" : NULL);
293 const char *got_offset;
294 int adj, id = strtol(shared_lib_id, NULL, 0);
297 /* Replace addresses using the shared object id. */
298 sprintf(buf, "%.2X", id);
299 append_sed(&sed, "ORIGIN = 0x0,", concat("ORIGIN = 0x", buf, "000000,", NULL));
300 append_sed(&sed, ".text 0x0 :", concat(".text 0x0", buf, "000000 :", NULL));
302 append_sed(&sed, "ENTRY (" SYMBOL_PREFIX "_start)", "ENTRY (lib_main)");
304 /* Provide the symbol specifying the library's data segment
307 if (streq(TARGET_CPU, "h8300"))
308 got_offset = "__current_shared_library_er5_offset_";
309 else if (streq(TARGET_CPU, "bfin"))
310 got_offset = "_current_shared_library_p5_offset_", adj = 1;
312 got_offset = "_current_shared_library_a5_offset_";
313 append_option(&other_options, "-defsym");
314 sprintf(buf, "%d", id * -adj - adj);
315 append_option(&other_options, concat(got_offset, "=", buf, NULL));
318 /* Locate the default linker script, if we don't have one provided. */
320 linker_script = concat(ldscriptpath, "/elf2flt.ld", NULL);
322 /* Try and locate the linker script. */
323 script = linker_script;
324 if (stat(script, &buf) || !S_ISREG(buf.st_mode)) {
325 script = concat(ldscriptpath, "/", linker_script, NULL);
326 if (stat(script, &buf) || !S_ISREG(buf.st_mode)) {
327 script = concat(ldscriptpath, "/ldscripts/", linker_script, NULL);
328 if (stat(script, &buf) || !S_ISREG(buf.st_mode))
332 /* And process it if we can -- if we can't find it, the user must
333 know what they are doing. */
335 do_sed(&sed, linker_script, tmp_file);
336 linker_script = tmp_file;
340 if (USE_EMIT_RELOCS) {
342 exec_or_ret(linker, NULL, &other_options,
343 "-T", linker_script, "-q", "-o", output_gdb, emulation);
345 append_option(&flt_options, "-a");
346 rel_output = output_gdb;
348 } else if (NO_GOT_CHECK) {
350 output_elf = make_temp_file(NULL);
352 exec_or_ret(linker, NULL, &other_options,
353 "-T", linker_script, "-Ur", "-d", "-o", output_elf, emulation);
354 exec_or_ret(linker, NULL, &other_options,
355 "-T", linker_script, "-o", output_gdb, emulation);
357 rel_output = output_elf;
361 output_flt = make_temp_file(NULL);
362 exec_or_ret(linker, NULL, &other_options,
363 "-r", "-d", "-o", output_flt, emulation);
365 output_elf = make_temp_file(NULL);
366 exec_or_ret(linker, NULL, &search_dirs,
367 "-T", linker_script, "-Ur", "-o", output_elf, output_flt, emulation);
369 exec_or_ret(linker, NULL, &search_dirs,
370 "-T", linker_script, "-o", output_gdb, output_flt, emulation);
372 rel_output = output_elf;
376 if (shared_lib_id && strtol(shared_lib_id, NULL, 0) != 0)
377 exec_or_ret(objcopy, NULL, NULL, "--localize-hidden", "--weaken", output_gdb);
379 exec_or_ret(nm, tmp_file, NULL, "-p", output_gdb);
380 in = xfopen(tmp_file, "r");
381 while ((len = getline(&line, &alloc, in)) > 0) {
382 const char *ptr = strchr(line, '_');
383 if (ptr && streqn(ptr, "_GLOBAL_OFFSET_TABLE")) {
390 exec_or_ret(elf2flt, NULL, &flt_options,
391 "-o", output_file, "-p", output_gdb, rel_output);
393 exec_or_ret(elf2flt, NULL, &flt_options,
394 "-o", output_file, "-r", rel_output);
399 /* parse all the arguments provided to us */
400 static void parse_args(int argc, char **argv)
405 for (argno = 1; argno < argc; argno++) {
406 char const *arg = argv[argno];
409 if (streq(arg, "-elf2flt")) {
410 have_elf2flt_options = 1;
412 } else if (streqn(arg, "-elf2flt=")) {
413 have_elf2flt_options = 1;
414 append_option_str(&flt_options, &arg[9], "\t ");
416 } else if (streq(arg, "-move-rodata")) {
418 } else if (streq(arg, "-shared-lib-id")) {
419 shared_lib_id = argv[++argno];
420 } else if (streq(arg, "-shared") || streq(arg, "-G")) {
422 } else if (streqn(arg, "-o")) {
423 output_file = arg[2] ? &arg[2] : argv[++argno];
424 } else if (streqn(arg, "-T")) {
425 linker_script = arg[2] ? &arg[2] : argv[++argno];
426 } else if (streq(arg, "-c")) {
427 linker_script = argv[++argno];
428 } else if (streqn(arg, "-L")) {
430 (arg[2] ? arg : concat("-L", argv[++argno], NULL));
431 append_option(&other_options, merged);
432 append_option(&search_dirs, merged);
433 } else if (streq(arg, "-EB")) {
434 append_option(&other_options, arg);
435 append_option(&search_dirs, arg);
436 } else if (streq(arg, "-relax")) {
438 } else if (streq(arg, "-s") || streq(arg, "--strip-all") ||
439 streq(arg, "-S") || streq(arg, "--strip-debug")) {
440 /* Ignore these strip options for links involving elf2flt.
441 The final flat output will be stripped by definition, and we
442 don't want to strip the .gdb helper file. The strip options
443 are also incompatible with -r and --emit-relocs. */
445 } else if (streq(arg, "-r") || streq(arg, "-Ur")) {
447 append_option(&other_options, arg);
448 } else if (streq(arg, "-v") || streq(arg, "--verbose")) {
450 append_option(&other_options, arg);
451 } else if (streqn(arg, "-m")) {
452 emulation = arg[2] ? arg : concat("-m", argv[++argno], NULL);
454 append_option(&other_options, arg);
456 while (to_all <= argno)
457 append_option(&all_options, argv[to_all++]);
460 fltflags = getenv("FLTFLAGS");
462 append_option_str(&flt_options, fltflags, "\t ");
465 int main(int argc, char *argv[])
469 const char *argv0 = argv[0];
472 const char *have_exe = NULL;
477 /* Remove the .exe extension, if it's there. */
478 if (len > 4 && streq(&argv0[len - 4], ".exe")) {
481 argv0 = tmp = xstrdup(argv0);
485 elf2flt_progname = argv0;
486 for (ptr = elf2flt_progname + len; ptr != elf2flt_progname; ptr--)
487 if (IS_DIR_SEPARATOR(ptr[-1])) {
488 tooldir = tmp = xmalloc(len);
489 memcpy(tmp, argv0, len);
490 tmp[ptr - elf2flt_progname - 1] = 0;
491 elf2flt_progname = ptr;
492 /* The standard binutils tool layout has:
494 bin/<TARGET_ALIAS>-foo
496 <TARGET_ALIAS>/bin/foo
499 It's <TARGET_ALIAS>/ that we want here: files in lib/ are for
500 the host while those in <TARGET_ALIAS>/lib are for the target. */
501 if (streqn(elf2flt_progname, TARGET_ALIAS)) {
502 tmp = concat(tooldir, "/../" TARGET_ALIAS "/bin", NULL);
503 if (stat(tmp, &buf) == 0 && S_ISDIR(buf.st_mode))
508 /* Typically ld-elf2flt is invoked as `ld` which means error
509 * messages from it will look like "ld: " which is completely
510 * confusing. So append an identifier to keep things clear.
512 elf2flt_progname = concat(elf2flt_progname, " (ld-elf2flt)", NULL);
514 xmalloc_set_program_name(elf2flt_progname);
516 tmp = xmalloc(len + 16);
517 memcpy(tmp, argv0, len);
518 while (len && tmp[len - 1] != '-' && !IS_DIR_SEPARATOR(tmp[len - 1]))
522 linker = concat(tmp, "ld.real", have_exe, NULL);
523 elf2flt = concat(tmp, "elf2flt", have_exe, NULL);
524 nm = concat(tmp, "nm", have_exe, NULL);
525 objdump = concat(tooldir, "/../../bin/", TARGET_ALIAS, "-objdump", have_exe, NULL);
526 objcopy = concat(tooldir, "/../../bin/", TARGET_ALIAS, "-objcopy", have_exe, NULL);
528 if (stat(ldscriptpath, &buf) || !S_ISDIR(buf.st_mode))
529 ldscriptpath = concat(tooldir, "/../lib", NULL);
531 parse_args(argc, argv);
534 fprintf(stderr, "argv[0] = '%s'\n", argv[0]);
535 fprintf(stderr, "tooldir = '%s'\n", tooldir);
536 fprintf(stderr, "linker = '%s'\n", linker);
537 fprintf(stderr, "elf2flt = '%s'\n", elf2flt);
538 fprintf(stderr, "nm = '%s'\n", nm);
539 fprintf(stderr, "objdump = '%s'\n", objdump);
540 fprintf(stderr, "objcopy = '%s'\n", objcopy);
541 fprintf(stderr, "ldscriptpath = '%s'\n", ldscriptpath);
544 /* Pass off to regular linker, if there's nothing elf2flt-like */
545 if (!have_elf2flt_options)
546 return execute(linker, NULL, &all_options);
548 /* Pass off to regular linker, minus the elf2flt options, if it's
549 not the final link. */
551 return execute(linker, NULL, &other_options, "-o", output_file);
553 if (want_shared && !shared_lib_id)
554 fatal("-shared used without passing a shared library ID");
556 /* Otherwise link & convert to flt. */
557 output_gdb = concat(output_file, ".gdb", NULL);
558 tmp_file = make_temp_file(NULL);
559 status = do_final_link();
566 "leaving elf2flt temp files behind:\n"
570 tmp_file, output_flt, output_elf);