view leo-obj/tool/armdis.c @ 392:35009c936a4a

compal/melody-extr: first attempt at actual melody extraction
author Mychaela Falconia <falcon@freecalypso.org>
date Fri, 01 Apr 2022 06:03:47 +0000
parents 2767ff8d26d5
children
line wrap: on
line source

/*
 * ARM state disassembly
 */

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

extern unsigned get_u16(), get_u32();
extern struct internal_reloc *find_word32_reloc();

extern char *regnames[16], *condition_decode[16], *shift_types[4];

static char *dataproc_ops[16] = {"and", "eor", "sub", "rsb",
				 "add", "adc", "sbc", "rsc",
				 "tst", "teq", "cmp", "cmn",
				 "orr", "mov", "bic", "mvn"};

static void
arm_branch(off, word)
	unsigned off, word;
{
	unsigned dest;

	dest = (word & 0x00FFFFFF) << 2;
	if (dest & 0x02000000)
		dest |= 0xFC000000;
	dest += off + 8;
	printf("b%s%s\t0x%x\n", word&0x1000000 ? "l" : "",
		condition_decode[word>>28], dest);
}

static void
op2_immed(word)
	unsigned word;
{
	unsigned low8, rot, val;

	low8 = word & 0xFF;
	rot = (word & 0xF00) >> 7;
	val = (low8 << (32 - rot)) | (low8 >> rot);
	if (val <= 9)
		printf("#%u\n", val);
	else
		printf("#%u\t; 0x%x\n", val, val);
}

static void
op2_regbyconst(word)
	unsigned word;
{
	unsigned c, t;

	c = (word >> 7) & 0x1F;
	t = (word >> 5) & 3;
	if (!c) {
		switch (t) {
		case 0:
			printf("%s", regnames[word&0xF]);
			return;
		case 3:
			printf("%s, rrx", regnames[word&0xF]);
			return;
		default:
			c = 32;
		}
	}
	printf("%s, %s #%u", regnames[word&0xF], shift_types[t], c);
}

static void
op2_regbyreg(word)
	unsigned word;
{
	printf("%s, %s %s", regnames[word&0xF], shift_types[(word>>5)&3],
		regnames[(word>>8)&0xF]);
}

static void
op2_regshift(word)
	unsigned word;
{
	if (word & 0x10)
		op2_regbyreg(word);
	else
		op2_regbyconst(word);
	putchar('\n');
}

static void
dataproc_op2(word)
	unsigned word;
{
	if (word & 0x02000000)
		op2_immed(word);
	else
		op2_regshift(word);
}

static void
dataproc_tstcmp_overlay(word)
	unsigned word;
{
	char msrmask[5], *cp;

	if ((word & 0x0FFFFFF0) == 0x012FFF10) {
		printf("bx%s\t%s\n", condition_decode[word>>28],
			regnames[word&0xF]);
		return;
	} else if ((word & 0x0FBF0FFF) == 0x010F0000) {
		printf("mrs%s\t%s, %cPSR\n", condition_decode[word>>28],
			regnames[(word>>12)&0xF], word&0x400000 ? 'S' : 'C');
		return;
	} else if ((word & 0x0DB0F000) == 0x0120F000) {
		if (!(word & 0x02000000) && (word & 0xFF0)) {
			printf("<invalid MSR>\n");
			return;
		}
		if (word & 0xF0000) {
			cp = msrmask;
			if (word & 0x80000)
				*cp++ = 'f';
			if (word & 0x40000)
				*cp++ = 's';
			if (word & 0x20000)
				*cp++ = 'x';
			if (word & 0x10000)
				*cp++ = 'c';
			*cp = '\0';
		} else
			strcpy(msrmask, "null");
		printf("msr%s\t%cPSR_%s, ", condition_decode[word>>28],
			word&0x400000 ? 'S' : 'C', msrmask);
		dataproc_op2(word);
		return;
	}
	printf("<invalid BX/MRS/MSR>\n");
}

static void
dataproc(word)
	unsigned word;
{
	unsigned opc;

	opc = (word >> 21) & 0xF;
	switch (opc) {
	case 0:
	case 1:
	case 2:
	case 3:
	case 4:
	case 5:
	case 6:
	case 7:
	case 0xC:
	case 0xE:
		printf("%s%s%s\t%s, %s, ", dataproc_ops[opc],
			condition_decode[word>>28], word&0x100000 ? "s" : "",
			regnames[(word>>12)&0xF], regnames[(word>>16)&0xF]);
		dataproc_op2(word);
		return;
	case 0xD:
	case 0xF:
		printf("%s%s%s\t%s, ", dataproc_ops[opc],
			condition_decode[word>>28], word&0x100000 ? "s" : "",
			regnames[(word>>12)&0xF]);
		dataproc_op2(word);
		return;
	case 8:
	case 9:
	case 0xA:
	case 0xB:
		if (word & 0x100000) {
			printf("%s%s\t%s, ", dataproc_ops[opc],
				condition_decode[word>>28],
				regnames[(word>>16)&0xF]);
			dataproc_op2(word);
		} else
			dataproc_tstcmp_overlay(word);
		return;
	}
}

static void
multiply(word)
	unsigned word;
{
	if ((word & 0x0FE000F0) == 0x90)
		printf("mul%s%s\t%s, %s, %s\n", condition_decode[word>>28],
			word&0x100000 ? "s" : "", regnames[(word>>16)&0xF],
			regnames[word&0xF], regnames[(word>>8)&0xF]);
	else if ((word & 0x0FE000F0) == 0x00200090)
		printf("mla%s%s\t%s, %s, %s, %s\n", condition_decode[word>>28],
			word&0x100000 ? "s" : "", regnames[(word>>16)&0xF],
			regnames[word&0xF], regnames[(word>>8)&0xF],
			regnames[(word>>12)&0xF]);
	else if ((word & 0x0F8000F0) == 0x00800090)
		printf("%c%sl%s%s\t%s, %s, %s, %s\n",
			word&0x400000 ? 's' : 'u',
			word&0x200000 ? "mla" : "mul",
			condition_decode[word>>28],
			word&0x100000 ? "s" : "",
			regnames[(word>>12)&0xF], regnames[(word>>16)&0xF],
			regnames[word&0xF], regnames[(word>>8)&0xF]);
	else if ((word & 0x0FB00FF0) == 0x01000090)
		printf("swp%s%s\t%s, %s, [%s]\n", condition_decode[word>>28],
			word&0x400000 ? "b" : "", regnames[(word>>12)&0xF],
			regnames[word&0xF], regnames[(word>>16)&0xF]);
	else
		printf("<invalid multiply>\n");
}

static int
check_ldr_litpool(sec, off, word, loff, size)
	struct internal_scnhdr *sec;
	unsigned off, word, loff;
{
	unsigned litoff, datum;
	struct internal_reloc *rel;

	/* base reg must be 15 */
	if (((word >> 16) & 0xF) != 15)
		return(0);
	/* must be a load */
	if (!(word & 0x100000))
		return(0);
	/* no writeback allowed */
	if (word & 0x200000)
		return(0);
	/* alignment */
	if (loff & (size - 1))
		return(0);
	/* range */
	off += 8;
	if (word & 0x800000)
		litoff = off + loff;
	else {
		if (loff > off)
			return(0);
		litoff = off - loff;
	}
	if (litoff >= sec->size)
		return(0);
	/* all checks passed, proceed */
	rel = find_word32_reloc(sec, litoff);
	switch (size) {
	case 1:
		datum = filemap[sec->data_offset + litoff];
		break;
	case 2:
		datum = get_u16(filemap + sec->data_offset + litoff);
		break;
	case 4:
		datum = get_u32(filemap + sec->data_offset + litoff);
		break;
	}
	putchar('=');
	if (rel)
		disasm_reloc_target(sec, rel, datum);
	else
		printf("0x%x", datum);
	printf("\t; via 0x%x\n", litoff);
	return(1);
}

static void
ldr_str_imm_pre(sec, off, word)
	struct internal_scnhdr *sec;
	unsigned off, word;
{
	unsigned loff = word & 0xFFF;

	printf("%s%s%s\t%s, ", word&0x100000 ? "ldr" : "str",
		condition_decode[word>>28], word&0x400000 ? "b" : "",
		regnames[(word>>12)&0xF]);
	if (check_ldr_litpool(sec, off, word, loff, word&0x400000 ? 1 : 4))
		return;
	printf("[%s", regnames[(word>>16)&0xF]);
	if (loff || word&0x200000)
		printf(", #%s%u", word&0x800000 ? "" : "-", loff);
	putchar(']');
	if (word & 0x200000)
		putchar('!');
	if (loff >= 10)
		printf("\t; 0x%x", loff);
	putchar('\n');
}

static void
ldr_str_imm_post(word)
	unsigned word;
{
	unsigned loff = word & 0xFFF;

	printf("%s%s%s%s\t%s, [%s], #%s%u", word&0x100000 ? "ldr" : "str",
		condition_decode[word>>28], word&0x400000 ? "b" : "",
		word&0x200000 ? "t" : "",
		regnames[(word>>12)&0xF], regnames[(word>>16)&0xF],
		word&0x800000 ? "" : "-", loff);
	if (loff >= 10)
		printf("\t; 0x%x", loff);
	putchar('\n');
}

static void
ldr_str_reg_pre(word)
	unsigned word;
{
	if (word & 0x10) {
		printf("<invalid ldr/str: offset reg shift by reg>\n");
		return;
	}
	printf("%s%s%s\t%s, [%s, ", word&0x100000 ? "ldr" : "str",
		condition_decode[word>>28], word&0x400000 ? "b" : "",
		regnames[(word>>12)&0xF], regnames[(word>>16)&0xF]);
	if (!(word & 0x800000))
		putchar('-');
	op2_regbyconst(word);
	putchar(']');
	if (word & 0x200000)
		putchar('!');
	putchar('\n');
}

static void
ldr_str_reg_post(word)
	unsigned word;
{
	if (word & 0x10) {
		printf("<invalid ldr/str: offset reg shift by reg>\n");
		return;
	}
	printf("%s%s%s%s\t%s, [%s], ", word&0x100000 ? "ldr" : "str",
		condition_decode[word>>28], word&0x400000 ? "b" : "",
		word&0x200000 ? "t" : "",
		regnames[(word>>12)&0xF], regnames[(word>>16)&0xF]);
	if (!(word & 0x800000))
		putchar('-');
	op2_regbyconst(word);
	putchar('\n');
}

static void
ldr_str_ext(sec, off, word)
	struct internal_scnhdr *sec;
	unsigned off, word;
{
	unsigned loff;

	if (!(word&0x01000000) && word&0x200000) {
		printf("<invalid ldrh/strh: P=0, W=1>\n");
		return;
	}
	if (!(word&0x400000) && word&0xF00) {
		printf("<invalid ldrh/strh: SBZ!=0>\n");
		return;
	}
	printf("%s%s%s%c\t%s, ", word&0x100000 ? "ldr" : "str",
		condition_decode[word>>28],
		word&0x40 ? "s" : "",
		word&0x20 ? 'h' : 'b',
		regnames[(word>>12)&0xF]);
	if (word & 0x400000)
		loff = ((word & 0xF00) >> 4) | (word & 0xF);
	switch (word & 0x01400000) {
	case 0:
		/* reg post */
		printf("[%s], %s%s", regnames[(word>>16)&0xF],
			word&0x800000 ? "" : "-", regnames[word&0xF]);
		break;
	case 0x400000:
		/* imm post */
		printf("[%s], #%s%u", regnames[(word>>16)&0xF],
			word&0x800000 ? "" : "-", loff);
		if (loff >= 10)
			printf("\t; 0x%x", loff);
		break;
	case 0x01000000:
		/* reg pre */
		printf("[%s, %s%s]%s", regnames[(word>>16)&0xF],
			word&0x800000 ? "" : "-", regnames[word&0xF],
			word&0x200000 ? "!" : "");
		break;
	case 0x01400000:
		/* imm pre */
		if (check_ldr_litpool(sec, off, word, loff, word&0x20 ? 2 : 1))
			return;
		printf("[%s", regnames[(word>>16)&0xF]);
		if (loff || word&0x200000)
			printf(", #%s%u", word&0x800000 ? "" : "-", loff);
		putchar(']');
		if (word & 0x200000)
			putchar('!');
		if (loff >= 10)
			printf("\t; 0x%x", loff);
		break;
	}
	putchar('\n');
}

static void
dataproc_74_overlay(sec, off, word)
	struct internal_scnhdr *sec;
	unsigned off, word;
{
	if (word & 0x60)
		ldr_str_ext(sec, off, word);
	else
		multiply(word);
}

static void
ldm_stm(word)
	unsigned word;
{
	int r, flag;

	printf("%s%s%c%c\t%s", word&0x100000 ? "ldm" : "stm",
		condition_decode[word>>28],
		word&0x800000 ? 'i' : 'd', word&0x01000000 ? 'b' : 'a',
		regnames[(word>>16)&0xF]);
	if (word & 0x200000)
		putchar('!');
	fputs(", {", stdout);
	flag = 0;
	for (r = 0; r < 16; r++)
		if (word & (1 << r)) {
			if (flag)
				fputs(", ", stdout);
			fputs(regnames[r], stdout);
			flag = 1;
		}
	putchar('}');
	if (word & 0x400000)
		putchar('^');
	putchar('\n');
}

void
arm_disasm_line(sec, off)
	struct internal_scnhdr *sec;
	unsigned off;
{
	unsigned word;

	word = get_u32(filemap + sec->data_offset + off);
	printf("%08x\t", word);
	if ((word >> 28) == 0xF) {
		printf("<invalid-F>\n");
		return;
	}
	switch ((word >> 24) & 0xF) {
	case 0:
	case 1:
		if ((word & 0x90) == 0x90)
			dataproc_74_overlay(sec, off, word);
		else
			dataproc(word);
		return;
	case 2:
	case 3:
		dataproc(word);
		return;
	case 4:
		ldr_str_imm_post(word);
		return;
	case 5:
		ldr_str_imm_pre(sec, off, word);
		return;
	case 6:
		ldr_str_reg_post(word);
		return;
	case 7:
		ldr_str_reg_pre(word);
		return;
	case 8:
	case 9:
		ldm_stm(word);
		return;
	case 0xA:
	case 0xB:
		arm_branch(off, word);
		return;
	case 0xC:
	case 0xD:
	case 0xE:
		printf("<COPROCESSOR>\n");
		return;
	case 0xF:
		printf("swi%s\t0x%x\n", condition_decode[word>>28],
			word & 0xFFFFFF);
		return;
	}
}

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

	word = get_u32(filemap + sec->data_offset + rel->location);
	printf("%08x R\t", word);
	if ((word & 0x0E000000) != 0x0A000000) {
		printf("<invalid ARM_B reloc: opcode not B or BL>\n");
		return;
	}
	dest = (word & 0x00FFFFFF) << 2;
	if (dest & 0x02000000)
		dest |= 0xFC000000;
	dest += rel->location + 8;
	printf("b%s%s\t", word&0x1000000 ? "l" : "",
		condition_decode[word>>28]);
	disasm_reloc_target(sec, rel, dest);
	putchar('\n');
}