FreeCalypso > hg > freecalypso-reveng
view ticoff/armdis.c @ 121:d88f2f40e3ae
tiobjd: handling of symbol-less relocs
author | Michael Spacefalcon <msokolov@ivan.Harhan.ORG> |
---|---|
date | Fri, 04 Apr 2014 07:19:15 +0000 |
parents | 2f23301d2f86 |
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'); }