view ticoff/disasm.c @ 118:193926ccd1ec

tiobjd: better handling of section-relative relocs
author Michael Spacefalcon <msokolov@ivan.Harhan.ORG>
date Thu, 03 Apr 2014 07:47:03 +0000
parents f9fde7f36ae3
children 4d8dfdbd2ea1
line wrap: on
line source

/*
 * Putting it all together: section-, symbol- and reloc-aware disassembly
 */

#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include "intstruct.h"
#include "coffconst.h"
#include "globals.h"

extern unsigned get_u16(), get_u32();

static void
find_better_symbol(symp, addp)
	struct internal_syment **symp;
	unsigned *addp;
{
	struct internal_scnhdr *sec;
	unsigned addr;
	struct internal_syment *sym, *symsel;
	unsigned n;

	sec = (*symp)->section;
	addr = *addp;
	symsel = 0;
	for (n = 0; n < sec->nsymbols; n++) {
		sym = sec->sorted_symbols[n];
		if (sym->value > addr)
			break;
		if (sym->class == C_EXT || sym->class == C_STAT)
			symsel = sym;
	}
	if (symsel) {
		*symp = symsel;
		*addp = addr - symsel->value;
	}
}

void
disasm_reloc_target(sec, rel, addend)
	struct internal_scnhdr *sec;
	struct internal_reloc *rel;
	unsigned addend;
{
	struct internal_syment *sym = rel->sym;

	addend -= sym->value;
	if (sym->section && !strcmp(sym->name, sym->section->name))
		find_better_symbol(&sym, &addend);
	if (addend >= 10)
		printf("%s+0x%x", sym->name, addend);
	else if (addend)
		printf("%s+%u", sym->name, addend);
	else
		fputs(sym->name, stdout);
}

void
disasm_word32_reloc(sec, rel)
	struct internal_scnhdr *sec;
	struct internal_reloc *rel;
{
	unsigned word;

	word = get_u32(filemap + sec->data_offset + rel->location);
	printf("%08x R\t.word\t", word);
	disasm_reloc_target(sec, rel, word);
	putchar('\n');
}

void
disasm_text_section(sec)
	struct internal_scnhdr *sec;
{
	unsigned symnum, relnum;
	unsigned pos, incr, headroom;
	int state = -1, linebrk = 0;
	struct internal_syment *sym;
	struct internal_reloc *rel;
	char *sym_comment;

	printf("Disassembling code section:\n");
	if (sec->nsymbols)
		sort_symbols_of_sec(sec);
	if (sec->nreloc)
		get_relocs_of_sec(sec);
	symnum = relnum = 0;
	for (pos = 0; pos < sec->size; pos += incr) {
		headroom = sec->size - pos;
		while (symnum < sec->nsymbols) {
			sym = sec->sorted_symbols[symnum];
			if (sym->value > pos) {
				if (sym->value - pos < headroom)
					headroom = sym->value - pos;
				break;
			}
			/* hit symbol */
			if (!linebrk) {
				putchar('\n');
				linebrk = 1;
			}
			switch (sym->class) {
			case C_EXT:
				sym_comment = "Global";
				break;
			case C_STAT:
				sym_comment = "static";
				break;
			case C_LABEL:
				sym_comment = "label";
				if (!strcmp(sym->name, "$CODE16"))
					state = 1;
				else if (!strcmp(sym->name, "$CODE32"))
					state = 0;
				break;
			default:
				sym_comment = "unexpected class!";
			}
			printf("%s:\t; %s\n", sym->name, sym_comment);
			symnum++;
		}
		if (relnum < sec->nreloc) {
			rel = sec->int_relocs + relnum;
			if (rel->location == pos)
				relnum++;	/* it's ours */
			else {
				if (rel->location - pos < headroom)
					headroom = rel->location - pos;
				rel = 0;	/* no reloc for current pos */
			}
		} else
			rel = 0;
		printf("%8x:\t", pos);
		if (rel && rel->type == RTYPE_LONG) {
			if (pos & 3) {
			  printf("MISALIGNED pos for word32 reloc, aborting\n");
				return;
			}
			disasm_word32_reloc(sec, rel);
			incr = 4;
			goto next;
		}
		switch (state) {
		case 0:		/* ARM */
			if (pos & 3) {
			   printf("MISALIGNED pos in CODE32 state, aborting\n");
				return;
			}
			if (rel) {
				if (rel->type != RTYPE_ARM_B) {
			printf("Wrong reloc type in CODE32 state, aborting\n");
					return;
				}
				arm_branch_reloc(sec, rel);
			} else
				arm_disasm_line(sec, pos);
			incr = 4;
			break;
		case 1:		/* Thumb */
			if (pos & 1) {
			   printf("MISALIGNED pos in CODE16 state, aborting\n");
				return;
			}
			if (rel) {
				if (rel->type != RTYPE_THUMB_BL) {
			printf("Wrong reloc type in CODE16 state, aborting\n");
					return;
				}
				thumb_bl_reloc(sec, rel);
				incr = 4;
			} else if (headroom >= 4 && thumb_check_bl(sec, pos))
				incr = 4;
			else {
				thumb_disasm_line(sec, pos);
				incr = 2;
			}
			break;
		default:
			printf("UNKNOWN T state, aborting\n");
			return;
		}
next:		linebrk = 0;
		if (incr > headroom) {
			printf("error: increment %u > headroom %u, aborting\n",
				incr, headroom);
			return;
		}
	}
}

void
disasm_sectype_by_name(sec)
	struct internal_scnhdr *sec;
{
	if (!strncmp(sec->name, ".text", 5))
		disasm_text_section(sec);
	/* other section types to be added */
	else
		printf("Unrecognized section type, skipped\n");
}

cmd_disasm()
{
	struct internal_scnhdr *sec;
	unsigned secnum;

	printf("%s:\n", objfilename);
	dump_filehdr_info();
	putchar('\n');
	get_int_section_table();
	get_int_symbol_table();
	extern_profile_report("Module");
	sort_symbols_of_all_sec();
	for (secnum = 0; secnum < nsections; secnum++) {
		sec = sections + secnum;
		printf("=== %s ===\n", sec->name);
		disasm_sectype_by_name(sec);
		putchar('\n');
	}
	exit(0);
}