Subversion Repositories freemyipod

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
445 theseven 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
}