OSDN Git Service

707e9e10fef4c195e294ebe99c4f828f3b87c4b4
[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    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].
86  */
87 static int
88 execute(const char *command, const char *output, const options_t *options, ...)
89 {
90         struct pex_obj *pex;
91         const char *errmsg;
92         int err;
93         int status;
94         va_list args;
95         const char *opt;
96         options_t opts;
97
98         debug("command=%s\n", command);
99
100         init_options(&opts);
101         append_option(&opts, command);
102         if (options)
103                 append_options(&opts, options);
104         va_start(args, options);
105         while ((opt = va_arg(args, const char *)))
106                 append_option(&opts, opt);
107         va_end(args);
108         append_option(&opts, NULL);
109
110         fflush(stdout);
111         fflush(stderr);
112
113         pex = pex_init(0, elf2flt_progname, NULL);
114         if (pex == NULL)
115                 fatal_perror("pex_init failed");
116
117         if (flag_verbose) {
118                 unsigned ix;
119
120                 fprintf(stderr, "Invoking:");
121                 for (ix = 0; ix != opts.num - 1; ix++)
122                         fprintf(stderr, " '%s'", opts.options[ix]);
123                 if (output)
124                         fprintf(stderr, " > '%s'", output);
125                 fprintf(stderr, "\n");
126         }
127
128         errmsg = pex_run(pex, PEX_LAST | PEX_SEARCH, command,
129                 (char *const *)opts.options, output, NULL, &err);
130         if (errmsg != NULL) {
131                 if (err != 0) {
132                         errno = err;
133                         fatal_perror(errmsg);
134                 } else
135                         fatal(errmsg);
136         }
137
138         if (!pex_get_status(pex, 1, &status))
139                 fatal_perror("can't get program status");
140         pex_free(pex);
141
142         if (status) {
143                 if (WIFSIGNALED(status)) {
144                         int sig = WTERMSIG(status);
145
146                         fatal("%s terminated with signal %d [%s]%s",
147                               command, sig, strsignal(sig),
148                               WCOREDUMP(status) ? ", core dumped" : "");
149                 }
150
151                 if (WIFEXITED(status))
152                         return WEXITSTATUS(status);
153         }
154         return 0;
155 }
156 /* Auto NULL terminate */
157 #define execute(...) execute(__VA_ARGS__, NULL)
158
159 /* Apply the sed commands in SED to file NAME_IN producing file NAME_OUT */
160 static void
161 do_sed(const sed_commands_t *sed, const char *name_in, const char *name_out)
162 {
163         FILE *in, *out;
164         size_t alloc = 0;
165         char *line = NULL;
166         ssize_t len;
167         const char *pattern, *replacement;
168         int ix;
169
170         if (flag_verbose) {
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];
175                         if (replacement)
176                                 fprintf(stderr, "\t-e 's/%s/%s/' \\\n", pattern, replacement);
177                         else
178                                 fprintf(stderr, "\t-e '/%s/d' \\\n", pattern);
179                 }
180                 fprintf(stderr, "\t%s > %s\n", name_in, name_out);
181         }
182
183         in = xfopen(name_in, "r");
184         out = xfopen(name_out, "w");
185
186         while ((len = getline(&line, &alloc, in)) > 0) {
187                 debug2("len=%2zi line=%s", len, line);
188
189                 for (ix = 0; ix != sed->pattern->num; ix++) {
190                         const char *ptr;
191                         int bol;
192                         size_t pat_len;
193
194                         pattern = sed->pattern->options[ix];
195                         replacement = sed->replacement->options[ix];
196                         ptr = line;
197                         bol = pattern[0] == '^';
198
199                         pattern += bol;
200                         pat_len = strlen(pattern);
201
202                         if (!bol) {
203                                 do {
204                                         ptr = strchr(ptr, pattern[0]);
205                                         if (!ptr) ;
206                                         else if (!strncmp(ptr, pattern, pat_len))
207                                                 goto found;
208                                         else
209                                                 ptr++;
210                                 }
211                                 while (ptr);
212                         } else if (!strncmp(ptr, pattern, pat_len)) {
213  found:
214                                 if (replacement) {
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),
220                                                out);
221                                 } else
222                                         debug2("   {dropped}\n");
223                                 goto next_line;
224                         }
225                 }
226
227                 debug2("(untouched)\n");
228                 fwrite(line, 1, len, out);
229  next_line:
230                 ;
231         }
232         fclose(in);
233         if (fclose(out))
234                 fatal_perror("error writing temporary script '%s'", name_out);
235         free(line);
236 }
237
238 /* Generate the flt binary along with any other necessary pieces.  */
239 #define exec_or_ret(...) \
240         do { \
241                 int status = execute(__VA_ARGS__); \
242                 if (status) return status; \
243         } while (0)
244 static int do_final_link(void)
245 {
246         sed_commands_t sed;
247         struct stat buf;
248         const char *script;
249         const char *rel_output;
250         int have_got = 0;
251         FILE *in;
252         char *line = NULL;
253         size_t alloc = 0;
254         ssize_t len;
255
256         init_sed(&sed);
257
258         if (flag_move_data) {
259                 FILE *in;
260
261                 /* See if the .rodata section contains any relocations.  */
262                 if (!output_flt)
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);
266
267                 in = xfopen(tmp_file, "r");
268                 while ((len = getline(&line, &alloc, in)) > 0) {
269                         const char *ptr = line;
270
271                         while (1) {
272                                 ptr = strchr(ptr, '.');
273                                 if (!ptr)
274                                         break;
275                                 if (streqn(ptr, ".rodata")) {
276                                         getline(&line, &alloc, in);
277                                         ptr = line;
278                                         while (1) {
279                                                 ptr = strchr(ptr, 'R');
280                                                 if (!ptr)
281                                                         break;
282                                                 if (streqn(ptr, "RELOC")) {
283                                                         flag_move_data = 0;
284                                                         fprintf(stderr, "warning: .rodata section contains relocations");
285                                                         break;
286                                                 } else
287                                                         ptr++;
288                                         }
289                                         break;
290                                 } else
291                                         ptr++;
292                         }
293                 }
294                 fclose(in);
295         }
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);
300
301         if (shared_lib_id) {
302                 const char *got_offset;
303                 int adj, id = strtol(shared_lib_id, NULL, 0);
304                 char buf[30];
305
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));
310                 if (id)
311                         append_sed(&sed, "ENTRY (" SYMBOL_PREFIX "_start)", "ENTRY (lib_main)");
312
313                 /* Provide the symbol specifying the library's data segment
314                    pointer offset.  */
315                 adj = 4;
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;
320                 else
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));
325         }
326
327         /* Locate the default linker script, if we don't have one provided. */
328         if (!linker_script)
329                 linker_script = concat(ldscriptpath, "/elf2flt.ld", NULL);
330
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))
338                                 script = NULL;
339                 }
340         }
341         /* And process it if we can -- if we can't find it, the user must
342            know what they are doing.  */
343         if (script) {
344                 do_sed(&sed, linker_script, tmp_file);
345                 linker_script = tmp_file;
346         }
347         free_sed(&sed);
348
349         if (USE_EMIT_RELOCS) {
350
351                 exec_or_ret(linker, NULL, &other_options,
352                         "-T", linker_script, "-q", "-o", output_gdb, emulation);
353
354                 append_option(&flt_options, "-a");
355                 rel_output = output_gdb;
356
357         } else if (NO_GOT_CHECK) {
358
359                 output_elf = make_temp_file(NULL);
360
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);
365
366                 rel_output = output_elf;
367
368         } else {
369
370                 output_flt = make_temp_file(NULL);
371                 exec_or_ret(linker, NULL, &other_options,
372                         "-r", "-d", "-o", output_flt, emulation);
373
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);
377
378                 exec_or_ret(linker, NULL, &search_dirs,
379                         "-T", linker_script, "-o", output_gdb, output_flt, emulation);
380
381                 rel_output = output_elf;
382
383         }
384
385         if (shared_lib_id && strtol(shared_lib_id, NULL, 0) != 0)
386                 exec_or_ret(objcopy, NULL, NULL, "--localize-hidden", "--weaken", output_gdb);
387
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")) {
393                         have_got = 1;
394                         break;
395                 }
396         }
397         fclose(in);
398         if (have_got)
399                 exec_or_ret(elf2flt, NULL, &flt_options,
400                         "-o", output_file, "-p", output_gdb, rel_output);
401         else
402                 exec_or_ret(elf2flt, NULL, &flt_options,
403                         "-o", output_file, "-r", rel_output);
404
405         return 0;
406 }
407
408 /* parse all the arguments provided to us */
409 static void parse_args(int argc, char **argv)
410 {
411         char *fltflags;
412         int argno;
413
414         for (argno = 1; argno < argc; argno++) {
415                 char const *arg = argv[argno];
416                 int to_all = argno;
417
418                 if (streq(arg, "-elf2flt")) {
419                         have_elf2flt_options = 1;
420                         to_all++;
421                 } else if (streqn(arg, "-elf2flt=")) {
422                         have_elf2flt_options = 1;
423                         append_option_str(&flt_options, &arg[9], "\t ");
424                         to_all++;
425                 } else if (streq(arg, "-move-rodata")) {
426                         flag_move_data = 1;
427                 } else if (streq(arg, "-shared-lib-id")) {
428                         shared_lib_id = argv[++argno];
429                 } else if (streq(arg, "-shared") || streq(arg, "-G")) {
430                         want_shared = 1;
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")) {
438                         const char *merged =
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")) {
446                         ;
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.  */
453                         ;
454                 } else if (streq(arg, "-r") || streq(arg, "-Ur")) {
455                         flag_final = 0;
456                         append_option(&other_options, arg);
457                 } else if (streq(arg, "-v") || streq(arg, "--verbose")) {
458                         flag_verbose = 1;
459                         append_option(&other_options, arg);
460                 } else if (streqn(arg, "-m")) {
461                         emulation = arg[2] ? arg : concat("-m", argv[++argno], NULL);
462                 } else
463                         append_option(&other_options, arg);
464
465                 while (to_all <= argno)
466                         append_option(&all_options, argv[to_all++]);
467         }
468
469         fltflags = getenv("FLTFLAGS");
470         if (fltflags)
471                 append_option_str(&flt_options, fltflags, "\t ");
472 }
473
474 int main(int argc, char *argv[])
475 {
476         const char *ptr;
477         char *tmp;
478         const char *argv0 = argv[0];
479         size_t len;
480         struct stat buf;
481         const char *have_exe = NULL;
482         int status;
483
484         len = strlen(argv0);
485 #ifdef __WIN32
486         /* Remove the .exe extension, if it's there.  */
487         if (len > 4 && streq(&argv0[len - 4], ".exe")) {
488                 have_exe = ".exe";
489                 len -= 4;
490                 argv0 = tmp = xstrdup(argv0);
491                 tmp[len] = 0;
492         }
493 #endif
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:
502
503                            bin/<TARGET_ALIAS>-foo
504                            lib/
505                            <TARGET_ALIAS>/bin/foo
506                            <TARGET_ALIAS>/lib
507
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))
513                                         tooldir = tmp;
514                         }
515                         break;
516                 }
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.
520          */
521         elf2flt_progname = concat(elf2flt_progname, " (ld-elf2flt)", NULL);
522
523         xmalloc_set_program_name(elf2flt_progname);
524
525         tmp = xmalloc(len + 16);
526         memcpy(tmp, argv0, len);
527         while (len && tmp[len - 1] != '-' && !IS_DIR_SEPARATOR(tmp[len - 1]))
528                 len--;
529         tmp[len] = 0;
530
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);
536
537         if (stat(ldscriptpath, &buf) || !S_ISDIR(buf.st_mode))
538                 ldscriptpath = concat(tooldir, "/../lib", NULL);
539
540         parse_args(argc, argv);
541
542         if (flag_verbose) {
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);
551         }
552
553         /* Pass off to regular linker, if there's nothing elf2flt-like */
554         if (!have_elf2flt_options)
555                 return execute(linker, NULL, &all_options);
556
557         /* Pass off to regular linker, minus the elf2flt options, if it's
558            not the final link.  */
559         if (!flag_final)
560                 return execute(linker, NULL, &other_options, "-o", output_file);
561
562         if (want_shared && !shared_lib_id)
563                 fatal("-shared used without passing a shared library ID");
564
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();
569         if (!flag_verbose) {
570                 unlink(tmp_file);
571                 unlink(output_flt);
572                 unlink(output_elf);
573         } else {
574                 fprintf(stderr,
575                         "leaving elf2flt temp files behind:\n"
576                         "tmp_file   = %s\n"
577                         "output_flt = %s\n"
578                         "output_elf = %s\n",
579                         tmp_file, output_flt, output_elf);
580         }
581         return status;
582 }