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 *ldscriptpath = BINUTILS_LDSCRIPTDIR;
51 /* A list of sed commands */
53 options_t *pattern; /* '^' for start of line match, everything else verbatim */
54 options_t *replacement; /* Delete line, if NULL */
57 /* Initialize a sed structure */
58 #define init_sed(DST) ( \
59 (DST)->pattern = xmalloc(sizeof(*(DST)->pattern)), \
60 (DST)->replacement = xmalloc(sizeof(*(DST)->replacement)), \
61 init_options((DST)->pattern), \
62 init_options((DST)->replacement) \
64 #define free_sed(DST) (free((DST)->pattern), free((DST)->replacement))
66 /* Append a slot for a new sed command. */
67 static void append_sed(sed_commands_t *dst, const char *pattern,
68 const char *replacement)
70 debug1("adding pattern '%s' with replacement '%s'\n",
71 pattern, replacement);
72 append_option(dst->pattern, pattern);
73 append_option(dst->replacement, replacement);
76 /* Execute an external program COMMAND. Write its stdout to OUTPUT,
77 unless that is NULL. Pass the trailing NULL terminated list of
78 options, followed by all those in OPTIONS, if that is non-NULL.
79 Order of options is important here as we may run on systems that
80 do not allow options after non-options (i.e. many BSDs). So the
81 final command line will look like:
82 <command> [options] [... va args ...]
83 This is because [options] will (should?) never contain non-options,
84 while non-options will always be passed via the [va args].
87 execute(const char *command, const char *output, const options_t *options, ...)
97 debug("command=%s\n", command);
100 append_option(&opts, command);
102 append_options(&opts, options);
103 va_start(args, options);
104 while ((opt = va_arg(args, const char *)))
105 append_option(&opts, opt);
107 append_option(&opts, NULL);
112 pex = pex_init(0, elf2flt_progname, NULL);
114 fatal_perror("pex_init failed");
119 fprintf(stderr, "Invoking:");
120 for (ix = 0; ix != opts.num - 1; ix++)
121 fprintf(stderr, " '%s'", opts.options[ix]);
123 fprintf(stderr, " > '%s'", output);
124 fprintf(stderr, "\n");
127 errmsg = pex_run(pex, PEX_LAST | PEX_SEARCH, command,
128 (char *const *)opts.options, output, NULL, &err);
129 if (errmsg != NULL) {
132 fatal_perror(errmsg);
137 if (!pex_get_status(pex, 1, &status))
138 fatal_perror("can't get program status");
142 if (WIFSIGNALED(status)) {
143 int sig = WTERMSIG(status);
145 fatal("%s terminated with signal %d [%s]%s",
146 command, sig, strsignal(sig),
147 WCOREDUMP(status) ? ", core dumped" : "");
150 if (WIFEXITED(status))
151 return WEXITSTATUS(status);
155 /* Auto NULL terminate */
156 #define execute(...) execute(__VA_ARGS__, NULL)
158 /* Apply the sed commands in SED to file NAME_IN producing file NAME_OUT */
160 do_sed(const sed_commands_t *sed, const char *name_in, const char *name_out)
166 const char *pattern, *replacement;
170 fprintf(stderr, "emulating: sed \\\n");
171 for (ix = 0; ix != sed->pattern->num; ix++) {
172 pattern = sed->pattern->options[ix];
173 replacement = sed->replacement->options[ix];
175 fprintf(stderr, "\t-e 's/%s/%s/' \\\n", pattern, replacement);
177 fprintf(stderr, "\t-e '/%s/d' \\\n", pattern);
179 fprintf(stderr, "\t%s > %s\n", name_in, name_out);
182 in = xfopen(name_in, "r");
183 out = xfopen(name_out, "w");
185 while ((len = getline(&line, &alloc, in)) > 0) {
186 debug2("len=%2zi line=%s", len, line);
188 for (ix = 0; ix != sed->pattern->num; ix++) {
193 pattern = sed->pattern->options[ix];
194 replacement = sed->replacement->options[ix];
196 bol = pattern[0] == '^';
199 pat_len = strlen(pattern);
203 ptr = strchr(ptr, pattern[0]);
205 else if (!strncmp(ptr, pattern, pat_len))
211 } else if (!strncmp(ptr, pattern, pat_len)) {
214 debug2(" [modified]\n");
215 fwrite(line, 1, ptr - line, out);
216 fwrite(replacement, 1, strlen(replacement), out);
217 fwrite(ptr + pat_len, 1,
218 len - pat_len - (ptr - line),
221 debug2(" {dropped}\n");
226 debug2("(untouched)\n");
227 fwrite(line, 1, len, out);
233 fatal_perror("error writing temporary script '%s'", name_out);
237 /* Generate the flt binary along with any other necessary pieces. */
238 #define exec_or_ret(...) \
240 int status = execute(__VA_ARGS__); \
241 if (status) return status; \
243 static int do_final_link(void)
248 const char *rel_output;
257 if (flag_move_data) {
260 /* See if the .rodata section contains any relocations. */
262 output_flt = make_temp_file(NULL);
263 exec_or_ret(linker, NULL, &other_options, "-r", "-d", "-o", output_flt);
264 exec_or_ret(objdump, tmp_file, NULL, "-h", output_flt);
266 in = xfopen(tmp_file, "r");
267 while ((len = getline(&line, &alloc, in)) > 0) {
268 const char *ptr = line;
271 ptr = strchr(ptr, '.');
274 if (streqn(ptr, ".rodata")) {
275 getline(&line, &alloc, in);
278 ptr = strchr(ptr, 'R');
281 if (streqn(ptr, "RELOC")) {
283 fprintf(stderr, "warning: .rodata section contains relocations");
295 append_sed(&sed, "^R_RODAT", flag_move_data ? NULL : "");
296 append_sed(&sed, "^W_RODAT", flag_move_data ? "" : NULL);
297 append_sed(&sed, "^SINGLE_LINK:", USE_EMIT_RELOCS ? "" : NULL);
298 append_sed(&sed, "^TOR:", EMIT_CTOR_DTOR ? "" : NULL);
301 const char *got_offset;
302 int adj, id = strtol(shared_lib_id, NULL, 0);
305 /* Replace addresses using the shared object id. */
306 sprintf(buf, "%.2X", id);
307 append_sed(&sed, "ORIGIN = 0x0,", concat("ORIGIN = 0x", buf, "000000,", NULL));
308 append_sed(&sed, ".text 0x0 :", concat(".text 0x0", buf, "000000 :", NULL));
310 append_sed(&sed, "ENTRY (" SYMBOL_PREFIX "_start)", "ENTRY (lib_main)");
312 /* Provide the symbol specifying the library's data segment
315 if (streq(TARGET_CPU, "h8300"))
316 got_offset = "__current_shared_library_er5_offset_";
317 else if (streq(TARGET_CPU, "bfin"))
318 got_offset = "_current_shared_library_p5_offset_", adj = 1;
320 got_offset = "_current_shared_library_a5_offset_";
321 append_option(&other_options, "-defsym");
322 sprintf(buf, "%d", id * -adj - adj);
323 append_option(&other_options, concat(got_offset, "=", buf, NULL));
326 /* Locate the default linker script, if we don't have one provided. */
328 linker_script = concat(ldscriptpath, "/elf2flt.ld", NULL);
330 /* Try and locate the linker script. */
331 script = linker_script;
332 if (stat(script, &buf) || !S_ISREG(buf.st_mode)) {
333 script = concat(ldscriptpath, "/", linker_script, NULL);
334 if (stat(script, &buf) || !S_ISREG(buf.st_mode)) {
335 script = concat(ldscriptpath, "/ldscripts/", linker_script, NULL);
336 if (stat(script, &buf) || !S_ISREG(buf.st_mode))
340 /* And process it if we can -- if we can't find it, the user must
341 know what they are doing. */
343 do_sed(&sed, linker_script, tmp_file);
344 linker_script = tmp_file;
348 if (USE_EMIT_RELOCS) {
350 exec_or_ret(linker, NULL, &other_options,
351 "-T", linker_script, "-q", "-o", output_gdb, emulation);
353 append_option(&flt_options, "-a");
354 rel_output = output_gdb;
356 } else if (NO_GOT_CHECK) {
358 output_elf = make_temp_file(NULL);
360 exec_or_ret(linker, NULL, &other_options,
361 "-T", linker_script, "-Ur", "-d", "-o", output_elf, emulation);
362 exec_or_ret(linker, NULL, &other_options,
363 "-T", linker_script, "-o", output_gdb, emulation);
365 rel_output = output_elf;
369 output_flt = make_temp_file(NULL);
370 exec_or_ret(linker, NULL, &other_options,
371 "-r", "-d", "-o", output_flt, emulation);
373 output_elf = make_temp_file(NULL);
374 exec_or_ret(linker, NULL, &search_dirs,
375 "-T", linker_script, "-Ur", "-o", output_elf, output_flt, emulation);
377 exec_or_ret(linker, NULL, &search_dirs,
378 "-T", linker_script, "-o", output_gdb, output_flt, emulation);
380 rel_output = output_elf;
384 if (shared_lib_id && strtol(shared_lib_id, NULL, 0) != 0)
385 exec_or_ret(objcopy, NULL, NULL, "--localize-hidden", "--weaken", output_gdb);
387 exec_or_ret(nm, tmp_file, NULL, "-p", output_gdb);
388 in = xfopen(tmp_file, "r");
389 while ((len = getline(&line, &alloc, in)) > 0) {
390 const char *ptr = strchr(line, '_');
391 if (ptr && streqn(ptr, "_GLOBAL_OFFSET_TABLE")) {
398 exec_or_ret(elf2flt, NULL, &flt_options,
399 "-o", output_file, "-p", output_gdb, rel_output);
401 exec_or_ret(elf2flt, NULL, &flt_options,
402 "-o", output_file, "-r", rel_output);
407 /* parse all the arguments provided to us */
408 static void parse_args(int argc, char **argv)
413 for (argno = 1; argno < argc; argno++) {
414 char const *arg = argv[argno];
417 if (streq(arg, "-elf2flt")) {
418 have_elf2flt_options = 1;
420 } else if (streqn(arg, "-elf2flt=")) {
421 have_elf2flt_options = 1;
422 append_option_str(&flt_options, &arg[9], "\t ");
424 } else if (streq(arg, "-move-rodata")) {
426 } else if (streq(arg, "-shared-lib-id")) {
427 shared_lib_id = argv[++argno];
428 } else if (streq(arg, "-shared") || streq(arg, "-G")) {
430 } else if (streqn(arg, "-o")) {
431 output_file = arg[2] ? &arg[2] : argv[++argno];
432 } else if (streqn(arg, "-T")) {
433 linker_script = arg[2] ? &arg[2] : argv[++argno];
434 } else if (streq(arg, "-c")) {
435 linker_script = argv[++argno];
436 } else if (streqn(arg, "-L")) {
438 (arg[2] ? arg : concat("-L", argv[++argno], NULL));
439 append_option(&other_options, merged);
440 append_option(&search_dirs, merged);
441 } else if (streq(arg, "-EB")) {
442 append_option(&other_options, arg);
443 append_option(&search_dirs, arg);
444 } else if (streq(arg, "-relax")) {
446 } else if (streq(arg, "-s") || streq(arg, "--strip-all") ||
447 streq(arg, "-S") || streq(arg, "--strip-debug")) {
448 /* Ignore these strip options for links involving elf2flt.
449 The final flat output will be stripped by definition, and we
450 don't want to strip the .gdb helper file. The strip options
451 are also incompatible with -r and --emit-relocs. */
453 } else if (streq(arg, "-r") || streq(arg, "-Ur")) {
455 append_option(&other_options, arg);
456 } else if (streq(arg, "-v") || streq(arg, "--verbose")) {
458 append_option(&other_options, arg);
459 } else if (streqn(arg, "-m")) {
460 emulation = arg[2] ? arg : concat("-m", argv[++argno], NULL);
462 append_option(&other_options, arg);
464 while (to_all <= argno)
465 append_option(&all_options, argv[to_all++]);
468 fltflags = getenv("FLTFLAGS");
470 append_option_str(&flt_options, fltflags, "\t ");
473 int main(int argc, char *argv[])
475 const char *argv0 = argv[0];
476 const char *argv0_dir = make_relative_prefix(argv0, "/", "/");
477 char *tooldir = argv0_dir;
478 char *bindir = argv0_dir;
481 const char *have_exe = NULL;
485 /* Remove the .exe extension, if it's there. */
486 size_t len = strlen(argv0);
487 if (len > 4 && streq(&argv0[len - 4], ".exe")) {
490 argv0 = tmp = xstrdup(argv0);
495 elf2flt_progname = lbasename(argv0);
497 /* The standard binutils tool layout has:
499 bin/<TARGET_ALIAS>-foo
501 <TARGET_ALIAS>/bin/foo
504 It's <TARGET_ALIAS>/ that we want here: files in lib/ are for
505 the host while those in <TARGET_ALIAS>/lib are for the target.
506 Make bindir point to the bin dir for bin/<TARGET_ALIAS>-foo.
507 Make tooldir point to the bin dir for <TARGET_ALIAS>/bin/foo. */
508 if (streqn(elf2flt_progname, TARGET_ALIAS)) {
509 tmp = concat(argv0_dir, "../" TARGET_ALIAS "/bin/", NULL);
510 if (stat(tmp, &buf) == 0 && S_ISDIR(buf.st_mode))
513 tmp = concat(argv0_dir, "../../bin/", NULL);
514 if (stat(tmp, &buf) == 0 && S_ISDIR(buf.st_mode))
518 /* Typically ld-elf2flt is invoked as `ld` which means error
519 * messages from it will look like "ld: " which is completely
520 * confusing. So append an identifier to keep things clear.
522 elf2flt_progname = concat(elf2flt_progname, " (ld-elf2flt)", NULL);
524 xmalloc_set_program_name(elf2flt_progname);
526 linker = concat(tooldir, "ld.real", have_exe, NULL);
527 elf2flt = concat(tooldir, "elf2flt", have_exe, NULL);
528 nm = concat(tooldir, "nm", have_exe, NULL);
529 objdump = concat(bindir, TARGET_ALIAS "-objdump", have_exe, NULL);
530 objcopy = concat(bindir, TARGET_ALIAS "-objcopy", have_exe, NULL);
532 if (stat(ldscriptpath, &buf) || !S_ISDIR(buf.st_mode))
533 ldscriptpath = concat(tooldir, "../lib", NULL);
535 parse_args(argc, argv);
538 fprintf(stderr, "argv[0] = '%s'\n", argv[0]);
539 fprintf(stderr, "bindir = '%s'\n", bindir);
540 fprintf(stderr, "tooldir = '%s'\n", tooldir);
541 fprintf(stderr, "linker = '%s'\n", linker);
542 fprintf(stderr, "elf2flt = '%s'\n", elf2flt);
543 fprintf(stderr, "nm = '%s'\n", nm);
544 fprintf(stderr, "objdump = '%s'\n", objdump);
545 fprintf(stderr, "objcopy = '%s'\n", objcopy);
546 fprintf(stderr, "ldscriptpath = '%s'\n", ldscriptpath);
549 /* Pass off to regular linker, if there's nothing elf2flt-like */
550 if (!have_elf2flt_options)
551 return execute(linker, NULL, &all_options);
553 /* Pass off to regular linker, minus the elf2flt options, if it's
554 not the final link. */
556 return execute(linker, NULL, &other_options, "-o", output_file);
558 if (want_shared && !shared_lib_id)
559 fatal("-shared used without passing a shared library ID");
561 /* Otherwise link & convert to flt. */
562 output_gdb = concat(output_file, ".gdb", NULL);
563 tmp_file = make_temp_file(NULL);
564 status = do_final_link();
571 "leaving elf2flt temp files behind:\n"
575 tmp_file, output_flt, output_elf);