view leo-obj/tool/disasm.c @ 133:daeaa5950d10

tiobjd: Thumb bl w/o reloc: find symbol if there is one
author Michael Spacefalcon <msokolov@ivan.Harhan.ORG>
date Mon, 07 Apr 2014 01:22:09 +0000
parents 87b82398a08b
children df432a4b1b84
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();
extern char *storage_class_to_string();

int auto_xlat_section_relocs = 1;

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

	addr = *addp;
	for (n = 0; n < sec->nsymbols; n++) {
		sym = sec->sorted_symbols[n];
		if (sym->value > addr)
			return;
		if (sym->class != C_EXT && sym->class != C_STAT)
			continue;
		delta = addr - sym->value;
		if (sym->name[0] == '_' && !delta ||
		    sym->name[0] == '$' && delta <= 1) {
			*symp = sym;
			*addp = delta;
		}
	}
}

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

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

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_end_of_section(sec, symnum)
	struct internal_scnhdr *sec;
	unsigned symnum;
{
	struct internal_syment *sym;
	char *sym_comment;

	putchar('\n');
	while (symnum < sec->nsymbols) {
		sym = sec->sorted_symbols[symnum];
		if (sym->value != sec->size) {
			printf("error: expecting symbol at end of section\n");
			return;
		}
		switch (sym->class) {
		case C_EXT:
			sym_comment = "Global";
			break;
		case C_STAT:
			sym_comment = "static";
			break;
		case C_LABEL:
			sym_comment = "label";
			break;
		default:
			sym_comment = "unexpected class!";
		}
		printf("%s:\t; %s\n", sym->name, sym_comment);
		symnum++;
	}
	printf("%8x:\t<end of section>\n", sec->size);
}

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;
		}
		if (pos & 1 || headroom < 2) {
			if (rel) {
				printf("error: reloc at byte pos, aborting\n");
				return;
			}
			printf("%02x\n", filemap[sec->data_offset + pos]);
			incr = 1;
			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;
		}
	}
	if (symnum < sec->nsymbols)
		disasm_end_of_section(sec, symnum);
}

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

	printf("Disassembling data 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";
				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) {
			if (rel->type != RTYPE_LONG) {
		printf("error: reloc other than word32 in data section\n");
				return;
			}
			if (pos & 3) {
			  printf("MISALIGNED pos for word32 reloc, aborting\n");
				return;
			}
			disasm_word32_reloc(sec, rel);
			incr = 4;
		} else if (pos & 1 || headroom < 2) {
			printf("%02x\n", filemap[sec->data_offset + pos]);
			incr = 1;
		} else if (pos & 2 || headroom < 4) {
			printf("%04x\n",
				get_u16(filemap + sec->data_offset + pos));
			incr = 2;
		} else {
			printf("%08x\n",
				get_u32(filemap + sec->data_offset + pos));
			incr = 4;
		}
		linebrk = 0;
		if (incr > headroom) {
			printf("error: increment %u > headroom %u, aborting\n",
				incr, headroom);
			return;
		}
	}
	if (symnum < sec->nsymbols)
		disasm_end_of_section(sec, symnum);
}

void
disasm_bss(sec)
	struct internal_scnhdr *sec;
{
	unsigned m;
	struct internal_syment *sym;
	char classbuf[8];

	putchar('\n');
	for (m = 0; m < sec->nsymbols; m++) {
		sym = sec->sorted_symbols[m];
		printf("%08X %-7s %s\n", sym->value,
			storage_class_to_string(sym->class, classbuf),
			sym->name);
	}
	printf("%08X <end of section>\n", sec->size);
}

void
disasm_sec_by_type(sec)
	struct internal_scnhdr *sec;
{
	switch (sec->disasm_mode) {
	case DISASM_MODE_CODE:
		disasm_text_section(sec);
		return;
	case DISASM_MODE_DATA:
		disasm_data_section(sec);
		return;
	case DISASM_MODE_BSS:
		disasm_bss(sec);
		return;
	default:
		printf("Unrecognized section type, skipped\n");
	}
}

cmd_disasm(argc, argv)
	char **argv;
{
	extern char *optarg;
	char *hintsfile = 0;
	struct internal_scnhdr *sec;
	unsigned secnum;
	int c;

	while ((c = getopt(argc, argv, "h:s")) != EOF)
		switch (c) {
		case 'h':
			hintsfile = optarg;
			continue;
		case 's':
			auto_xlat_section_relocs = 0;
			continue;
		default:
			/* error msg already printed */
			exit(1);
		}

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