OSDN Git Service

the "all" target should not be depending on "ld-elf2flt"
[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 static int
81 execute(const char *command, const char *output, const options_t *options, ...)
82 {
83         struct pex_obj *pex;
84         const char *errmsg;
85         int err;
86         int status;
87         va_list args;
88         const char *opt;
89         options_t opts;
90
91         debug("command=%s\n", command);
92
93         init_options(&opts);
94         append_option(&opts, command);
95         va_start(args, options);
96         while ((opt = va_arg(args, const char *)))
97                 append_option(&opts, opt);
98         va_end(args);
99         if (options)
100                 append_options(&opts, options);
101         append_option(&opts, NULL);
102
103         fflush(stdout);
104         fflush(stderr);
105
106         pex = pex_init(0, elf2flt_progname, NULL);
107         if (pex == NULL)
108                 fatal_perror("pex_init failed");
109
110         if (flag_verbose) {
111                 unsigned ix;
112
113                 fprintf(stderr, "Invoking:");
114                 for (ix = 0; ix != opts.num - 1; ix++)
115                         fprintf(stderr, " '%s'", opts.options[ix]);
116                 fprintf(stderr, "\n");
117         }
118
119         errmsg = pex_run(pex, PEX_LAST | PEX_SEARCH, command,
120                 (char *const *)opts.options, output, NULL, &err);
121         if (errmsg != NULL) {
122                 if (err != 0) {
123                         errno = err;
124                         fatal_perror(errmsg);
125                 } else
126                         fatal(errmsg);
127         }
128
129         if (!pex_get_status(pex, 1, &status))
130                 fatal_perror("can't get program status");
131         pex_free(pex);
132
133         if (status) {
134                 if (WIFSIGNALED(status)) {
135                         int sig = WTERMSIG(status);
136
137                         fatal("%s terminated with signal %d [%s]%s",
138                               command, sig, strsignal(sig),
139                               WCOREDUMP(status) ? ", core dumped" : "");
140                 }
141
142                 if (WIFEXITED(status))
143                         return WEXITSTATUS(status);
144         }
145         return 0;
146 }
147 /* Auto NULL terminate */
148 #define execute(...) execute(__VA_ARGS__, NULL)
149
150 /* Apply the sed commands in SED to file NAME_IN producing file NAME_OUT */
151 static void
152 do_sed(const sed_commands_t *sed, const char *name_in, const char *name_out)
153 {
154         FILE *in, *out;
155         size_t alloc = 0;
156         char *line = NULL;
157         ssize_t len;
158         const char *pattern, *replacement;
159         int ix;
160
161         if (flag_verbose) {
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];
166                         if (replacement)
167                                 fprintf(stderr, "\t-e 's/%s/%s/' \\\n", pattern, replacement);
168                         else
169                                 fprintf(stderr, "\t-e 'd/%s/' \\\n", pattern);
170                 }
171                 fprintf(stderr, "\t%s > %s\n", name_in, name_out);
172         }
173
174         in = xfopen(name_in, "r");
175         out = xfopen(name_out, "w");
176
177         while ((len = getline(&line, &alloc, in)) > 0) {
178                 debug2("len=%2zi line=%s", len, line);
179
180                 for (ix = 0; ix != sed->pattern->num; ix++) {
181                         const char *ptr;
182                         int bol;
183                         size_t pat_len;
184
185                         pattern = sed->pattern->options[ix];
186                         replacement = sed->replacement->options[ix];
187                         ptr = line;
188                         bol = pattern[0] == '^';
189
190                         pattern += bol;
191                         pat_len = strlen(pattern);
192
193                         if (!bol) {
194                                 do {
195                                         ptr = strchr(ptr, pattern[0]);
196                                         if (!ptr) ;
197                                         else if (!strncmp(ptr, pattern, pat_len))
198                                                 goto found;
199                                         else
200                                                 ptr++;
201                                 }
202                                 while (ptr);
203                         } else if (!strncmp(ptr, pattern, pat_len)) {
204  found:
205                                 if (replacement) {
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),
211                                                out);
212                                 } else
213                                         debug2("   {dropped}\n");
214                                 goto next_line;
215                         }
216                 }
217
218                 debug2("(untouched)\n");
219                 fwrite(line, 1, len, out);
220  next_line:
221                 ;
222         }
223         fclose(in);
224         if (fclose(out))
225                 fatal_perror("error writing temporary script '%s'", name_out);
226         free(line);
227 }
228
229 /* Generate the flt binary along with any other necessary pieces.  */
230 #define exec_or_ret(...) \
231         do { \
232                 int status = execute(__VA_ARGS__); \
233                 if (status) return status; \
234         } while (0)
235 static int do_final_link(void)
236 {
237         sed_commands_t sed;
238         struct stat buf;
239         const char *script;
240         const char *rel_output;
241         int have_got = 0;
242         FILE *in;
243         char *line = NULL;
244         size_t alloc = 0;
245         ssize_t len;
246
247         init_sed(&sed);
248
249         if (flag_move_data) {
250                 FILE *in;
251
252                 /* See if the .rodata section contains any relocations.  */
253                 if (!output_flt)
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);
257
258                 in = xfopen(tmp_file, "r");
259                 while ((len = getline(&line, &alloc, in)) > 0) {
260                         const char *ptr = line;
261
262                         while (1) {
263                                 ptr = strchr(ptr, '.');
264                                 if (!ptr)
265                                         break;
266                                 if (streqn(ptr, ".rodata")) {
267                                         getline(&line, &alloc, in);
268                                         ptr = line;
269                                         while (1) {
270                                                 ptr = strchr(ptr, 'R');
271                                                 if (!ptr)
272                                                         break;
273                                                 if (streqn(ptr, "RELOC")) {
274                                                         flag_move_data = 0;
275                                                         fprintf(stderr, "warning: .rodata section contains relocations");
276                                                         break;
277                                                 } else
278                                                         ptr++;
279                                         }
280                                         break;
281                                 } else
282                                         ptr++;
283                         }
284                 }
285                 fclose(in);
286         }
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);
291
292         if (shared_lib_id) {
293                 const char *got_offset;
294                 int adj, id = strtol(shared_lib_id, NULL, 0);
295                 char buf[30];
296
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));
301                 if (id)
302                         append_sed(&sed, "ENTRY (" SYMBOL_PREFIX "_start)", "ENTRY (lib_main)");
303
304                 /* Provide the symbol specifying the library's data segment
305                    pointer offset.  */
306                 adj = 4;
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;
311                 else
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));
316         }
317
318         /* Locate the default linker script, if we don't have one provided. */
319         if (!linker_script)
320                 linker_script = concat(ldscriptpath, "/elf2flt.ld", NULL);
321
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))
329                                 script = NULL;
330                 }
331         }
332         /* And process it if we can -- if we can't find it, the user must
333            know what they are doing.  */
334         if (script) {
335                 do_sed(&sed, linker_script, tmp_file);
336                 linker_script = tmp_file;
337         }
338         free_sed(&sed);
339
340         if (USE_EMIT_RELOCS) {
341
342                 exec_or_ret(linker, NULL, &other_options,
343                         "-T", linker_script, "-q", "-o", output_gdb, emulation);
344
345                 append_option(&flt_options, "-a");
346                 rel_output = output_gdb;
347
348         } else if (NO_GOT_CHECK) {
349
350                 output_elf = make_temp_file(NULL);
351
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);
356
357                 rel_output = output_elf;
358
359         } else {
360
361                 output_flt = make_temp_file(NULL);
362                 exec_or_ret(linker, NULL, &other_options,
363                         "-r", "-d", "-o", output_flt, emulation);
364
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);
368
369                 exec_or_ret(linker, NULL, &search_dirs,
370                         "-T", linker_script, "-o", output_gdb, output_flt, emulation);
371
372                 rel_output = output_elf;
373
374         }
375
376         if (shared_lib_id && strtol(shared_lib_id, NULL, 0) != 0)
377                 exec_or_ret(objcopy, NULL, NULL, "--localize-hidden", "--weaken", output_gdb);
378
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")) {
384                         have_got = 1;
385                         break;
386                 }
387         }
388         fclose(in);
389         if (have_got)
390                 exec_or_ret(elf2flt, NULL, &flt_options,
391                         "-o", output_file, "-p", output_gdb, rel_output);
392         else
393                 exec_or_ret(elf2flt, NULL, &flt_options,
394                         "-o", output_file, "-r", rel_output);
395
396         return 0;
397 }
398
399 /* parse all the arguments provided to us */
400 static void parse_args(int argc, char **argv)
401 {
402         char *fltflags;
403         int argno;
404
405         for (argno = 1; argno < argc; argno++) {
406                 char const *arg = argv[argno];
407                 int to_all = argno;
408
409                 if (streq(arg, "-elf2flt")) {
410                         have_elf2flt_options = 1;
411                         to_all++;
412                 } else if (streqn(arg, "-elf2flt=")) {
413                         have_elf2flt_options = 1;
414                         append_option_str(&flt_options, &arg[9], "\t ");
415                         to_all++;
416                 } else if (streq(arg, "-move-rodata")) {
417                         flag_move_data = 1;
418                 } else if (streq(arg, "-shared-lib-id")) {
419                         shared_lib_id = argv[++argno];
420                 } else if (streq(arg, "-shared") || streq(arg, "-G")) {
421                         want_shared = 1;
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")) {
429                         const char *merged =
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")) {
437                         ;
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.  */
444                         ;
445                 } else if (streq(arg, "-r") || streq(arg, "-Ur")) {
446                         flag_final = 0;
447                         append_option(&other_options, arg);
448                 } else if (streq(arg, "-v") || streq(arg, "--verbose")) {
449                         flag_verbose = 1;
450                         append_option(&other_options, arg);
451                 } else if (streqn(arg, "-m")) {
452                         emulation = arg[2] ? arg : concat("-m", argv[++argno], NULL);
453                 } else
454                         append_option(&other_options, arg);
455
456                 while (to_all <= argno)
457                         append_option(&all_options, argv[to_all++]);
458         }
459
460         fltflags = getenv("FLTFLAGS");
461         if (fltflags)
462                 append_option_str(&flt_options, fltflags, "\t ");
463 }
464
465 int main(int argc, char *argv[])
466 {
467         const char *ptr;
468         char *tmp;
469         const char *argv0 = argv[0];
470         size_t len;
471         struct stat buf;
472         const char *have_exe = NULL;
473         int status;
474
475         len = strlen(argv0);
476 #ifdef __WIN32
477         /* Remove the .exe extension, if it's there.  */
478         if (len > 4 && streq(&argv0[len - 4], ".exe")) {
479                 have_exe = ".exe";
480                 len -= 4;
481                 argv0 = tmp = xstrdup(argv0);
482                 tmp[len] = 0;
483         }
484 #endif
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:
493
494                            bin/<TARGET_ALIAS>-foo
495                            lib/
496                            <TARGET_ALIAS>/bin/foo
497                            <TARGET_ALIAS>/lib
498
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))
504                                         tooldir = tmp;
505                         }
506                         break;
507                 }
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.
511          */
512         elf2flt_progname = concat(elf2flt_progname, " (ld-elf2flt)", NULL);
513
514         xmalloc_set_program_name(elf2flt_progname);
515
516         tmp = xmalloc(len + 16);
517         memcpy(tmp, argv0, len);
518         while (len && tmp[len - 1] != '-' && !IS_DIR_SEPARATOR(tmp[len - 1]))
519                 len--;
520         tmp[len] = 0;
521
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);
527
528         if (stat(ldscriptpath, &buf) || !S_ISDIR(buf.st_mode))
529                 ldscriptpath = concat(tooldir, "/../lib", NULL);
530
531         parse_args(argc, argv);
532
533         if (flag_verbose) {
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);
542         }
543
544         /* Pass off to regular linker, if there's nothing elf2flt-like */
545         if (!have_elf2flt_options)
546                 return execute(linker, NULL, &all_options);
547
548         /* Pass off to regular linker, minus the elf2flt options, if it's
549            not the final link.  */
550         if (!flag_final)
551                 return execute(linker, NULL, &other_options, "-o", output_file);
552
553         if (want_shared && !shared_lib_id)
554                 fatal("-shared used without passing a shared library ID");
555
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();
560         if (!flag_verbose) {
561                 unlink(tmp_file);
562                 unlink(output_flt);
563                 unlink(output_elf);
564         } else {
565                 fprintf(stderr,
566                         "leaving elf2flt temp files behind:\n"
567                         "tmp_file   = %s\n"
568                         "output_flt = %s\n"
569                         "output_elf = %s\n",
570                         tmp_file, output_flt, output_elf);
571         }
572         return status;
573 }