OSDN Git Service

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