relocs.c 19.4 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <elf.h>
#include <byteswap.h>
#define USE_BSD
#include <endian.h>
12
#include <regex.h>
13
#include <tools/le_byteshift.h>
14 15

static void die(char *fmt, ...);
16

17
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
18 19 20
static Elf32_Ehdr ehdr;
static unsigned long reloc_count, reloc_idx;
static unsigned long *relocs;
21 22
static unsigned long reloc16_count, reloc16_idx;
static unsigned long *relocs16;
23

24 25 26 27 28 29 30 31 32
struct section {
	Elf32_Shdr     shdr;
	struct section *link;
	Elf32_Sym      *symtab;
	Elf32_Rel      *reltab;
	char           *strtab;
};
static struct section *secs;

33 34 35 36 37 38 39 40 41
enum symtype {
	S_ABS,
	S_REL,
	S_SEG,
	S_LIN,
	S_NSYMTYPES
};

static const char * const sym_regex_kernel[S_NSYMTYPES] = {
42 43 44 45 46 47
/*
 * Following symbols have been audited. There values are constant and do
 * not change if bzImage is loaded at a different physical address than
 * the address for which it has been compiled. Don't warn user about
 * absolute relocations present w.r.t these symbols.
 */
48
	[S_ABS] =
49 50 51
	"^(xen_irq_disable_direct_reloc$|"
	"xen_save_fl_direct_reloc$|"
	"VDSO|"
52
	"__crc_)",
53

54 55 56 57
/*
 * These symbols are known to be relative, even if the linker marks them
 * as absolute (typically defined outside any section in the linker script.)
 */
58
	[S_REL] =
59 60 61 62
	"^(__init_(begin|end)|"
	"__x86_cpu_dev_(start|end)|"
	"(__parainstructions|__alt_instructions)(|_end)|"
	"(__iommu_table|__apicdrivers|__smp_locks)(|_end)|"
63 64 65 66 67 68 69 70 71 72 73
	"__(start|end)_pci_.*|"
	"__(start|end)_builtin_fw|"
	"__(start|stop)___ksymtab(|_gpl|_unused|_unused_gpl|_gpl_future)|"
	"__(start|stop)___kcrctab(|_gpl|_unused|_unused_gpl|_gpl_future)|"
	"__(start|stop)___param|"
	"__(start|stop)___modver|"
	"__(start|stop)___bug_table|"
	"__tracedata_(start|end)|"
	"__(start|stop)_notes|"
	"__end_rodata|"
	"__initramfs_start|"
74
	"(jiffies|jiffies_64)|"
75
	"_end)$"
76 77 78 79
};


static const char * const sym_regex_realmode[S_NSYMTYPES] = {
80 81 82 83 84 85 86
/*
 * These symbols are known to be relative, even if the linker marks them
 * as absolute (typically defined outside any section in the linker script.)
 */
	[S_REL] =
	"^pa_",

87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
/*
 * These are 16-bit segment symbols when compiling 16-bit code.
 */
	[S_SEG] =
	"^real_mode_seg$",

/*
 * These are offsets belonging to segments, as opposed to linear addresses,
 * when compiling 16-bit code.
 */
	[S_LIN] =
	"^pa_",
};

static const char * const *sym_regex;

static regex_t sym_regex_c[S_NSYMTYPES];
static int is_reloc(enum symtype type, const char *sym_name)
105
{
106 107
	return sym_regex[type] &&
		!regexec(&sym_regex_c[type], sym_name, 0, NULL, 0);
108
}
109

110
static void regex_init(int use_real_mode)
111 112 113
{
        char errbuf[128];
        int err;
114
	int i;
115

116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
	if (use_real_mode)
		sym_regex = sym_regex_realmode;
	else
		sym_regex = sym_regex_kernel;

	for (i = 0; i < S_NSYMTYPES; i++) {
		if (!sym_regex[i])
			continue;

		err = regcomp(&sym_regex_c[i], sym_regex[i],
			      REG_EXTENDED|REG_NOSUB);

		if (err) {
			regerror(err, &sym_regex_c[i], errbuf, sizeof errbuf);
			die("%s", errbuf);
		}
132
        }
133 134
}

135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
static void die(char *fmt, ...)
{
	va_list ap;
	va_start(ap, fmt);
	vfprintf(stderr, fmt, ap);
	va_end(ap);
	exit(1);
}

static const char *sym_type(unsigned type)
{
	static const char *type_name[] = {
#define SYM_TYPE(X) [X] = #X
		SYM_TYPE(STT_NOTYPE),
		SYM_TYPE(STT_OBJECT),
		SYM_TYPE(STT_FUNC),
		SYM_TYPE(STT_SECTION),
		SYM_TYPE(STT_FILE),
		SYM_TYPE(STT_COMMON),
		SYM_TYPE(STT_TLS),
#undef SYM_TYPE
	};
	const char *name = "unknown sym type name";
158
	if (type < ARRAY_SIZE(type_name)) {
159 160 161 162 163 164 165 166 167 168 169 170 171 172 173
		name = type_name[type];
	}
	return name;
}

static const char *sym_bind(unsigned bind)
{
	static const char *bind_name[] = {
#define SYM_BIND(X) [X] = #X
		SYM_BIND(STB_LOCAL),
		SYM_BIND(STB_GLOBAL),
		SYM_BIND(STB_WEAK),
#undef SYM_BIND
	};
	const char *name = "unknown sym bind name";
174
	if (bind < ARRAY_SIZE(bind_name)) {
175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190
		name = bind_name[bind];
	}
	return name;
}

static const char *sym_visibility(unsigned visibility)
{
	static const char *visibility_name[] = {
#define SYM_VISIBILITY(X) [X] = #X
		SYM_VISIBILITY(STV_DEFAULT),
		SYM_VISIBILITY(STV_INTERNAL),
		SYM_VISIBILITY(STV_HIDDEN),
		SYM_VISIBILITY(STV_PROTECTED),
#undef SYM_VISIBILITY
	};
	const char *name = "unknown sym visibility name";
191
	if (visibility < ARRAY_SIZE(visibility_name)) {
192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211
		name = visibility_name[visibility];
	}
	return name;
}

static const char *rel_type(unsigned type)
{
	static const char *type_name[] = {
#define REL_TYPE(X) [X] = #X
		REL_TYPE(R_386_NONE),
		REL_TYPE(R_386_32),
		REL_TYPE(R_386_PC32),
		REL_TYPE(R_386_GOT32),
		REL_TYPE(R_386_PLT32),
		REL_TYPE(R_386_COPY),
		REL_TYPE(R_386_GLOB_DAT),
		REL_TYPE(R_386_JMP_SLOT),
		REL_TYPE(R_386_RELATIVE),
		REL_TYPE(R_386_GOTOFF),
		REL_TYPE(R_386_GOTPC),
212 213 214 215
		REL_TYPE(R_386_8),
		REL_TYPE(R_386_PC8),
		REL_TYPE(R_386_16),
		REL_TYPE(R_386_PC16),
216 217 218
#undef REL_TYPE
	};
	const char *name = "unknown type rel type name";
219
	if (type < ARRAY_SIZE(type_name) && type_name[type]) {
220 221 222 223 224 225 226 227 228
		name = type_name[type];
	}
	return name;
}

static const char *sec_name(unsigned shndx)
{
	const char *sec_strtab;
	const char *name;
229
	sec_strtab = secs[ehdr.e_shstrndx].strtab;
230 231
	name = "<noname>";
	if (shndx < ehdr.e_shnum) {
232
		name = sec_strtab + secs[shndx].shdr.sh_name;
233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250
	}
	else if (shndx == SHN_ABS) {
		name = "ABSOLUTE";
	}
	else if (shndx == SHN_COMMON) {
		name = "COMMON";
	}
	return name;
}

static const char *sym_name(const char *sym_strtab, Elf32_Sym *sym)
{
	const char *name;
	name = "<noname>";
	if (sym->st_name) {
		name = sym_strtab + sym->st_name;
	}
	else {
251
		name = sec_name(sym->st_shndx);
252 253 254 255 256 257
	}
	return name;
}



258
#if BYTE_ORDER == LITTLE_ENDIAN
259 260 261
#define le16_to_cpu(val) (val)
#define le32_to_cpu(val) (val)
#endif
262
#if BYTE_ORDER == BIG_ENDIAN
263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282
#define le16_to_cpu(val) bswap_16(val)
#define le32_to_cpu(val) bswap_32(val)
#endif

static uint16_t elf16_to_cpu(uint16_t val)
{
	return le16_to_cpu(val);
}

static uint32_t elf32_to_cpu(uint32_t val)
{
	return le32_to_cpu(val);
}

static void read_ehdr(FILE *fp)
{
	if (fread(&ehdr, sizeof(ehdr), 1, fp) != 1) {
		die("Cannot read ELF header: %s\n",
			strerror(errno));
	}
283
	if (memcmp(ehdr.e_ident, ELFMAG, SELFMAG) != 0) {
284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335
		die("No ELF magic\n");
	}
	if (ehdr.e_ident[EI_CLASS] != ELFCLASS32) {
		die("Not a 32 bit executable\n");
	}
	if (ehdr.e_ident[EI_DATA] != ELFDATA2LSB) {
		die("Not a LSB ELF executable\n");
	}
	if (ehdr.e_ident[EI_VERSION] != EV_CURRENT) {
		die("Unknown ELF version\n");
	}
	/* Convert the fields to native endian */
	ehdr.e_type      = elf16_to_cpu(ehdr.e_type);
	ehdr.e_machine   = elf16_to_cpu(ehdr.e_machine);
	ehdr.e_version   = elf32_to_cpu(ehdr.e_version);
	ehdr.e_entry     = elf32_to_cpu(ehdr.e_entry);
	ehdr.e_phoff     = elf32_to_cpu(ehdr.e_phoff);
	ehdr.e_shoff     = elf32_to_cpu(ehdr.e_shoff);
	ehdr.e_flags     = elf32_to_cpu(ehdr.e_flags);
	ehdr.e_ehsize    = elf16_to_cpu(ehdr.e_ehsize);
	ehdr.e_phentsize = elf16_to_cpu(ehdr.e_phentsize);
	ehdr.e_phnum     = elf16_to_cpu(ehdr.e_phnum);
	ehdr.e_shentsize = elf16_to_cpu(ehdr.e_shentsize);
	ehdr.e_shnum     = elf16_to_cpu(ehdr.e_shnum);
	ehdr.e_shstrndx  = elf16_to_cpu(ehdr.e_shstrndx);

	if ((ehdr.e_type != ET_EXEC) && (ehdr.e_type != ET_DYN)) {
		die("Unsupported ELF header type\n");
	}
	if (ehdr.e_machine != EM_386) {
		die("Not for x86\n");
	}
	if (ehdr.e_version != EV_CURRENT) {
		die("Unknown ELF version\n");
	}
	if (ehdr.e_ehsize != sizeof(Elf32_Ehdr)) {
		die("Bad Elf header size\n");
	}
	if (ehdr.e_phentsize != sizeof(Elf32_Phdr)) {
		die("Bad program header entry\n");
	}
	if (ehdr.e_shentsize != sizeof(Elf32_Shdr)) {
		die("Bad section header entry\n");
	}
	if (ehdr.e_shstrndx >= ehdr.e_shnum) {
		die("String table index out of bounds\n");
	}
}

static void read_shdrs(FILE *fp)
{
	int i;
336 337 338 339 340 341
	Elf32_Shdr shdr;

	secs = calloc(ehdr.e_shnum, sizeof(struct section));
	if (!secs) {
		die("Unable to allocate %d section headers\n",
		    ehdr.e_shnum);
342 343 344 345 346
	}
	if (fseek(fp, ehdr.e_shoff, SEEK_SET) < 0) {
		die("Seek to %d failed: %s\n",
			ehdr.e_shoff, strerror(errno));
	}
347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363
	for (i = 0; i < ehdr.e_shnum; i++) {
		struct section *sec = &secs[i];
		if (fread(&shdr, sizeof shdr, 1, fp) != 1)
			die("Cannot read ELF section headers %d/%d: %s\n",
			    i, ehdr.e_shnum, strerror(errno));
		sec->shdr.sh_name      = elf32_to_cpu(shdr.sh_name);
		sec->shdr.sh_type      = elf32_to_cpu(shdr.sh_type);
		sec->shdr.sh_flags     = elf32_to_cpu(shdr.sh_flags);
		sec->shdr.sh_addr      = elf32_to_cpu(shdr.sh_addr);
		sec->shdr.sh_offset    = elf32_to_cpu(shdr.sh_offset);
		sec->shdr.sh_size      = elf32_to_cpu(shdr.sh_size);
		sec->shdr.sh_link      = elf32_to_cpu(shdr.sh_link);
		sec->shdr.sh_info      = elf32_to_cpu(shdr.sh_info);
		sec->shdr.sh_addralign = elf32_to_cpu(shdr.sh_addralign);
		sec->shdr.sh_entsize   = elf32_to_cpu(shdr.sh_entsize);
		if (sec->shdr.sh_link < ehdr.e_shnum)
			sec->link = &secs[sec->shdr.sh_link];
364 365 366 367 368 369 370
	}

}

static void read_strtabs(FILE *fp)
{
	int i;
371 372 373
	for (i = 0; i < ehdr.e_shnum; i++) {
		struct section *sec = &secs[i];
		if (sec->shdr.sh_type != SHT_STRTAB) {
374 375
			continue;
		}
376 377
		sec->strtab = malloc(sec->shdr.sh_size);
		if (!sec->strtab) {
378
			die("malloc of %d bytes for strtab failed\n",
379
				sec->shdr.sh_size);
380
		}
381
		if (fseek(fp, sec->shdr.sh_offset, SEEK_SET) < 0) {
382
			die("Seek to %d failed: %s\n",
383
				sec->shdr.sh_offset, strerror(errno));
384
		}
385 386
		if (fread(sec->strtab, 1, sec->shdr.sh_size, fp)
		    != sec->shdr.sh_size) {
387 388 389 390 391 392 393 394 395
			die("Cannot read symbol table: %s\n",
				strerror(errno));
		}
	}
}

static void read_symtabs(FILE *fp)
{
	int i,j;
396 397 398
	for (i = 0; i < ehdr.e_shnum; i++) {
		struct section *sec = &secs[i];
		if (sec->shdr.sh_type != SHT_SYMTAB) {
399 400
			continue;
		}
401 402
		sec->symtab = malloc(sec->shdr.sh_size);
		if (!sec->symtab) {
403
			die("malloc of %d bytes for symtab failed\n",
404
				sec->shdr.sh_size);
405
		}
406
		if (fseek(fp, sec->shdr.sh_offset, SEEK_SET) < 0) {
407
			die("Seek to %d failed: %s\n",
408
				sec->shdr.sh_offset, strerror(errno));
409
		}
410 411
		if (fread(sec->symtab, 1, sec->shdr.sh_size, fp)
		    != sec->shdr.sh_size) {
412 413 414
			die("Cannot read symbol table: %s\n",
				strerror(errno));
		}
415 416 417 418 419 420
		for (j = 0; j < sec->shdr.sh_size/sizeof(Elf32_Sym); j++) {
			Elf32_Sym *sym = &sec->symtab[j];
			sym->st_name  = elf32_to_cpu(sym->st_name);
			sym->st_value = elf32_to_cpu(sym->st_value);
			sym->st_size  = elf32_to_cpu(sym->st_size);
			sym->st_shndx = elf16_to_cpu(sym->st_shndx);
421 422 423 424 425 426 427 428
		}
	}
}


static void read_relocs(FILE *fp)
{
	int i,j;
429 430 431
	for (i = 0; i < ehdr.e_shnum; i++) {
		struct section *sec = &secs[i];
		if (sec->shdr.sh_type != SHT_REL) {
432 433
			continue;
		}
434 435
		sec->reltab = malloc(sec->shdr.sh_size);
		if (!sec->reltab) {
436
			die("malloc of %d bytes for relocs failed\n",
437
				sec->shdr.sh_size);
438
		}
439
		if (fseek(fp, sec->shdr.sh_offset, SEEK_SET) < 0) {
440
			die("Seek to %d failed: %s\n",
441
				sec->shdr.sh_offset, strerror(errno));
442
		}
443 444
		if (fread(sec->reltab, 1, sec->shdr.sh_size, fp)
		    != sec->shdr.sh_size) {
445 446 447
			die("Cannot read symbol table: %s\n",
				strerror(errno));
		}
448 449 450 451
		for (j = 0; j < sec->shdr.sh_size/sizeof(Elf32_Rel); j++) {
			Elf32_Rel *rel = &sec->reltab[j];
			rel->r_offset = elf32_to_cpu(rel->r_offset);
			rel->r_info   = elf32_to_cpu(rel->r_info);
452 453 454 455 456 457 458 459 460 461
		}
	}
}


static void print_absolute_symbols(void)
{
	int i;
	printf("Absolute symbols\n");
	printf(" Num:    Value Size  Type       Bind        Visibility  Name\n");
462 463
	for (i = 0; i < ehdr.e_shnum; i++) {
		struct section *sec = &secs[i];
464 465
		char *sym_strtab;
		int j;
466 467

		if (sec->shdr.sh_type != SHT_SYMTAB) {
468 469
			continue;
		}
470 471
		sym_strtab = sec->link->strtab;
		for (j = 0; j < sec->shdr.sh_size/sizeof(Elf32_Sym); j++) {
472 473
			Elf32_Sym *sym;
			const char *name;
474
			sym = &sec->symtab[j];
475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491
			name = sym_name(sym_strtab, sym);
			if (sym->st_shndx != SHN_ABS) {
				continue;
			}
			printf("%5d %08x %5d %10s %10s %12s %s\n",
				j, sym->st_value, sym->st_size,
				sym_type(ELF32_ST_TYPE(sym->st_info)),
				sym_bind(ELF32_ST_BIND(sym->st_info)),
				sym_visibility(ELF32_ST_VISIBILITY(sym->st_other)),
				name);
		}
	}
	printf("\n");
}

static void print_absolute_relocs(void)
{
492 493
	int i, printed = 0;

494 495 496
	for (i = 0; i < ehdr.e_shnum; i++) {
		struct section *sec = &secs[i];
		struct section *sec_applies, *sec_symtab;
497 498 499
		char *sym_strtab;
		Elf32_Sym *sh_symtab;
		int j;
500
		if (sec->shdr.sh_type != SHT_REL) {
501 502
			continue;
		}
503 504 505
		sec_symtab  = sec->link;
		sec_applies = &secs[sec->shdr.sh_info];
		if (!(sec_applies->shdr.sh_flags & SHF_ALLOC)) {
506 507
			continue;
		}
508 509 510
		sh_symtab  = sec_symtab->symtab;
		sym_strtab = sec_symtab->link->strtab;
		for (j = 0; j < sec->shdr.sh_size/sizeof(Elf32_Rel); j++) {
511 512 513
			Elf32_Rel *rel;
			Elf32_Sym *sym;
			const char *name;
514
			rel = &sec->reltab[j];
515 516 517 518 519
			sym = &sh_symtab[ELF32_R_SYM(rel->r_info)];
			name = sym_name(sym_strtab, sym);
			if (sym->st_shndx != SHN_ABS) {
				continue;
			}
520 521 522 523 524 525 526 527 528 529 530 531 532 533

			/* Absolute symbols are not relocated if bzImage is
			 * loaded at a non-compiled address. Display a warning
			 * to user at compile time about the absolute
			 * relocations present.
			 *
			 * User need to audit the code to make sure
			 * some symbols which should have been section
			 * relative have not become absolute because of some
			 * linker optimization or wrong programming usage.
			 *
			 * Before warning check if this absolute symbol
			 * relocation is harmless.
			 */
534
			if (is_reloc(S_ABS, name) || is_reloc(S_REL, name))
535 536 537 538 539 540 541 542 543 544
				continue;

			if (!printed) {
				printf("WARNING: Absolute relocations"
					" present\n");
				printf("Offset     Info     Type     Sym.Value "
					"Sym.Name\n");
				printed = 1;
			}

545 546 547 548 549 550 551 552
			printf("%08x %08x %10s %08x  %s\n",
				rel->r_offset,
				rel->r_info,
				rel_type(ELF32_R_TYPE(rel->r_info)),
				sym->st_value,
				name);
		}
	}
553 554 555

	if (printed)
		printf("\n");
556 557
}

558 559
static void walk_relocs(void (*visit)(Elf32_Rel *rel, Elf32_Sym *sym),
			int use_real_mode)
560 561 562
{
	int i;
	/* Walk through the relocations */
563
	for (i = 0; i < ehdr.e_shnum; i++) {
564 565
		char *sym_strtab;
		Elf32_Sym *sh_symtab;
566
		struct section *sec_applies, *sec_symtab;
567
		int j;
568 569 570
		struct section *sec = &secs[i];

		if (sec->shdr.sh_type != SHT_REL) {
571 572
			continue;
		}
573 574 575
		sec_symtab  = sec->link;
		sec_applies = &secs[sec->shdr.sh_info];
		if (!(sec_applies->shdr.sh_flags & SHF_ALLOC)) {
576 577
			continue;
		}
578
		sh_symtab = sec_symtab->symtab;
579
		sym_strtab = sec_symtab->link->strtab;
580
		for (j = 0; j < sec->shdr.sh_size/sizeof(Elf32_Rel); j++) {
581 582 583
			Elf32_Rel *rel;
			Elf32_Sym *sym;
			unsigned r_type;
584
			const char *symname;
585 586
			int shn_abs;

587
			rel = &sec->reltab[j];
588 589
			sym = &sh_symtab[ELF32_R_SYM(rel->r_info)];
			r_type = ELF32_R_TYPE(rel->r_info);
590

591 592
			shn_abs = sym->st_shndx == SHN_ABS;

593 594 595
			switch (r_type) {
			case R_386_NONE:
			case R_386_PC32:
596 597
			case R_386_PC16:
			case R_386_PC8:
598 599 600 601
				/*
				 * NONE can be ignored and and PC relative
				 * relocations don't need to be adjusted.
				 */
602
				break;
603 604 605 606 607

			case R_386_16:
				symname = sym_name(sym_strtab, sym);
				if (!use_real_mode)
					goto bad;
608
				if (shn_abs) {
609 610 611 612 613 614 615 616 617 618 619 620 621
					if (is_reloc(S_ABS, symname))
						break;
					else if (!is_reloc(S_SEG, symname))
						goto bad;
				} else {
					if (is_reloc(S_LIN, symname))
						goto bad;
					else
						break;
				}
				visit(rel, sym);
				break;

622
			case R_386_32:
623
				symname = sym_name(sym_strtab, sym);
624
				if (shn_abs) {
625 626 627 628 629 630 631 632 633
					if (is_reloc(S_ABS, symname))
						break;
					else if (!is_reloc(S_REL, symname))
						goto bad;
				} else {
					if (use_real_mode &&
					    !is_reloc(S_LIN, symname))
						break;
				}
634
				visit(rel, sym);
635 636 637 638 639
				break;
			default:
				die("Unsupported relocation type: %s (%d)\n",
				    rel_type(r_type), r_type);
				break;
640 641
			bad:
				symname = sym_name(sym_strtab, sym);
642 643
				die("Invalid %s %s relocation: %s\n",
				    shn_abs ? "absolute" : "relative",
644
				    rel_type(r_type), symname);
645 646 647 648 649 650 651
			}
		}
	}
}

static void count_reloc(Elf32_Rel *rel, Elf32_Sym *sym)
{
652 653 654 655
	if (ELF32_R_TYPE(rel->r_info) == R_386_16)
		reloc16_count++;
	else
		reloc_count++;
656 657 658 659 660
}

static void collect_reloc(Elf32_Rel *rel, Elf32_Sym *sym)
{
	/* Remember the address that needs to be adjusted. */
661 662 663 664
	if (ELF32_R_TYPE(rel->r_info) == R_386_16)
		relocs16[reloc16_idx++] = rel->r_offset;
	else
		relocs[reloc_idx++] = rel->r_offset;
665 666 667 668 669 670 671 672 673
}

static int cmp_relocs(const void *va, const void *vb)
{
	const unsigned long *a, *b;
	a = va; b = vb;
	return (*a == *b)? 0 : (*a > *b)? 1 : -1;
}

674 675 676 677 678 679 680 681 682
static int write32(unsigned int v, FILE *f)
{
	unsigned char buf[4];

	put_unaligned_le32(v, buf);
	return fwrite(buf, 1, 4, f) == 4 ? 0 : -1;
}

static void emit_relocs(int as_text, int use_real_mode)
683 684 685 686
{
	int i;
	/* Count how many relocations I have and allocate space for them. */
	reloc_count = 0;
687
	walk_relocs(count_reloc, use_real_mode);
688 689 690 691 692
	relocs = malloc(reloc_count * sizeof(relocs[0]));
	if (!relocs) {
		die("malloc of %d entries for relocs failed\n",
			reloc_count);
	}
693 694 695 696 697 698

	relocs16 = malloc(reloc16_count * sizeof(relocs[0]));
	if (!relocs16) {
		die("malloc of %d entries for relocs16 failed\n",
			reloc16_count);
	}
699 700
	/* Collect up the relocations */
	reloc_idx = 0;
701 702 703 704
	walk_relocs(collect_reloc, use_real_mode);

	if (reloc16_count && !use_real_mode)
		die("Segment relocations found but --realmode not specified\n");
705 706 707

	/* Order the relocations for more efficient processing */
	qsort(relocs, reloc_count, sizeof(relocs[0]), cmp_relocs);
708
	qsort(relocs16, reloc16_count, sizeof(relocs16[0]), cmp_relocs);
709 710 711 712 713 714 715 716

	/* Print the relocations */
	if (as_text) {
		/* Print the relocations in a form suitable that
		 * gas will like.
		 */
		printf(".section \".data.reloc\",\"a\"\n");
		printf(".balign 4\n");
717 718 719 720 721 722 723 724 725 726 727 728 729 730
		if (use_real_mode) {
			printf("\t.long %lu\n", reloc16_count);
			for (i = 0; i < reloc16_count; i++)
				printf("\t.long 0x%08lx\n", relocs16[i]);
			printf("\t.long %lu\n", reloc_count);
			for (i = 0; i < reloc_count; i++) {
				printf("\t.long 0x%08lx\n", relocs[i]);
			}
		} else {
			/* Print a stop */
			printf("\t.long 0x%08lx\n", (unsigned long)0);
			for (i = 0; i < reloc_count; i++) {
				printf("\t.long 0x%08lx\n", relocs[i]);
			}
731
		}
732

733 734 735
		printf("\n");
	}
	else {
736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752
		if (use_real_mode) {
			write32(reloc16_count, stdout);
			for (i = 0; i < reloc16_count; i++)
				write32(relocs16[i], stdout);
			write32(reloc_count, stdout);

			/* Now print each relocation */
			for (i = 0; i < reloc_count; i++)
				write32(relocs[i], stdout);
		} else {
			/* Print a stop */
			write32(0, stdout);

			/* Now print each relocation */
			for (i = 0; i < reloc_count; i++) {
				write32(relocs[i], stdout);
			}
753 754 755 756 757 758
		}
	}
}

static void usage(void)
{
759
	die("relocs [--abs-syms|--abs-relocs|--text|--realmode] vmlinux\n");
760 761 762 763
}

int main(int argc, char **argv)
{
764
	int show_absolute_syms, show_absolute_relocs;
765
	int as_text, use_real_mode;
766 767 768 769
	const char *fname;
	FILE *fp;
	int i;

770 771
	show_absolute_syms = 0;
	show_absolute_relocs = 0;
772
	as_text = 0;
773
	use_real_mode = 0;
774
	fname = NULL;
775
	for (i = 1; i < argc; i++) {
776 777
		char *arg = argv[i];
		if (*arg == '-') {
778
			if (strcmp(arg, "--abs-syms") == 0) {
779 780 781
				show_absolute_syms = 1;
				continue;
			}
782
			if (strcmp(arg, "--abs-relocs") == 0) {
783
				show_absolute_relocs = 1;
784 785
				continue;
			}
786
			if (strcmp(arg, "--text") == 0) {
787 788 789
				as_text = 1;
				continue;
			}
790 791 792 793
			if (strcmp(arg, "--realmode") == 0) {
				use_real_mode = 1;
				continue;
			}
794 795 796 797 798 799 800 801 802 803
		}
		else if (!fname) {
			fname = arg;
			continue;
		}
		usage();
	}
	if (!fname) {
		usage();
	}
804
	regex_init(use_real_mode);
805 806 807 808 809 810 811 812 813 814
	fp = fopen(fname, "r");
	if (!fp) {
		die("Cannot open %s: %s\n",
			fname, strerror(errno));
	}
	read_ehdr(fp);
	read_shdrs(fp);
	read_strtabs(fp);
	read_symtabs(fp);
	read_relocs(fp);
815
	if (show_absolute_syms) {
816
		print_absolute_symbols();
817
		goto out;
818 819
	}
	if (show_absolute_relocs) {
820
		print_absolute_relocs();
821
		goto out;
822
	}
823
	emit_relocs(as_text, use_real_mode);
824 825
out:
	fclose(fp);
826 827
	return 0;
}