Bug 494424 - Thumb disassembler issues
diff --git a/agent/machine/arm/tcf/disassembler-thumb.c b/agent/machine/arm/tcf/disassembler-thumb.c
index 59ea5e6..a680732 100644
--- a/agent/machine/arm/tcf/disassembler-thumb.c
+++ b/agent/machine/arm/tcf/disassembler-thumb.c
@@ -151,7 +151,7 @@
add_char(' ');
if (offset < 0) {
add_char('-');
- add_dec_uint32(-offset);
+ add_dec_uint32((-offset & 0xffffffff));
}
else {
add_char('+');
@@ -198,6 +198,7 @@
add_reg_name((instr >> 3) & 7);
if (imm || op) {
add_str(", #");
+ if (op >= 1 && imm == 0) imm = 32;
add_dec_uint32(imm);
}
}
@@ -247,6 +248,7 @@
add_reg_name(instr & 7);
add_str(", ");
add_reg_name((instr >> 3) & 7);
+ if (op == 9) add_str(", #0");
return;
}
@@ -420,22 +422,25 @@
add_str(instr & (1 << 4) ? "id" : "ie");
if (it_cond_name) add_str(it_cond_name);
add_char(' ');
- if (instr & (1 << 0)) add_char('f');
- if (instr & (1 << 1)) add_char('i');
if (instr & (1 << 2)) add_char('a');
+ if (instr & (1 << 1)) add_char('i');
+ if (instr & (1 << 0)) add_char('f');
return;
}
if ((instr & 0xf500) == 0xb100) {
uint32_t offs = (instr >> 3) & 0x1f;
if (instr & (1 << 9)) offs |= 0x20;
+ offs = offs << 1;
add_str("cb");
if (instr & (1 << 11)) add_char('n');
add_char('z');
add_char(' ');
add_reg_name(instr & 7);
- add_str(", ");
- add_hex_uint32(instr_addr + 4 + (offs << 1));
+ add_str(", +");
+ add_dec_uint32(offs);
+ add_char(' ');
+ add_addr(instr_addr + offs + 4);
return;
}
@@ -542,7 +547,10 @@
if (it_cond_name) add_str(it_cond_name);
add_char(' ');
add_reg_name(rn);
- if ((regs & (1 << rn)) == 0) add_char('!');
+ if (((regs & (1 << rn)) == 0) ||
+ ((instr & (1 << 11)) == 0)) {
+ add_char('!');
+ }
add_str(", {");
for (reg = 0; reg < 8; reg++) {
if ((regs & (1 << reg)) == 0) continue;
@@ -555,6 +563,8 @@
}
static void disassemble_load_store_32(uint16_t suffix) {
+ int W = (instr & (1u << 5)) != 0;
+
if ((instr & (1 << 6)) == 0) {
/* Load/store multiple */
unsigned i, j;
@@ -678,7 +688,6 @@
uint32_t imm = (suffix & 0xff) << 2;
int P = (instr & (1u << 8)) != 0;
int U = (instr & (1u << 7)) != 0;
- int W = (instr & (1u << 5)) != 0;
add_reg_name(instr & 0xf);
if (P) {
if (imm != 0 || W) {
@@ -703,6 +712,7 @@
add_char(U ? '+' : '-');
add_dec_uint32(imm);
add_char(']');
+ if (W) add_char('!');
add_addr(U ? instr_addr + imm + 4 : instr_addr - imm + 4);
}
return;
@@ -727,27 +737,45 @@
static void disassemble_data_processing_32(uint16_t suffix) {
uint32_t op_code = (instr >> 5) & 0xf;
+ uint32_t shift_imm = ((suffix >> 10) & 0x1c) | ((suffix >> 6) & 3);
+ uint32_t shift_type = (suffix >> 4) & 3;
int I = (instr & (1 << 9)) == 0;
int S = (instr & (1 << 4)) != 0;
uint32_t rn = instr & 0xf;
uint32_t rd = (suffix >> 8) & 0xf;
int no_rd = 0;
int no_rn = 0;
+ int no_S = 0;
+ int no_shift = 0;
switch (op_code) {
case 0:
- if (rd == 15) {
- if (!S) return;
- S = 0;
- no_rd = 1;
- }
- add_str(rd == 15 ? "tst" : "and");
+ add_str(((rd == 15) && S) ? "tst" : "and");
+ no_S = rd == 15 && S;
+ no_rd = no_S;
break;
case 1:
add_str("bic");
break;
case 2:
- add_str(rn == 15 ? "mov" : "orr");
+ /*
+ * ASR<c>.W {<Rd>,} <Rm>, <Rs>
+ * is equivalent to
+ * MOV{<c>}{<q>} <Rd>, <Rm>, ASR <Rs>
+ * and is always the preferred disassembly.
+ */
+ if (!I && (shift_type != 0 || shift_imm != 0) && (rn == 15)) {
+ no_shift = 1;
+ if (shift_type == 3 && shift_imm == 0) {
+ add_str("rrx");
+ }
+ else {
+ add_str(shift_names[shift_type]);
+ }
+ }
+ else {
+ add_str(rn == 15 ? "mov" : "orr");
+ }
no_rn = rn == 15;
break;
case 3:
@@ -755,23 +783,20 @@
no_rn = rn == 15;
break;
case 4:
- if (rd == 15) {
- if (!S) return;
- S = 0;
- no_rd = 1;
- }
- add_str(rd == 15 ? "teq" : "eor");
+ add_str((rd == 15 && S)? "teq" : "eor");
+ no_S = rd == 15 && S;
+ no_rd = no_S;
break;
case 6:
add_str("pkh");
break;
case 8:
- if (rd == 15) {
- if (!S) return;
+ if (rd == 15 && S) {
+ add_str("cmn");
S = 0;
no_rd = 1;
}
- add_str(rd == 15 ? "cmn" : "add");
+ else add_str("add");
break;
case 10:
add_str("adc");
@@ -780,12 +805,9 @@
add_str("sbc");
break;
case 13:
- if (rd == 15) {
- if (!S) return;
- S = 0;
- no_rd = 1;
- }
- add_str(rd == 15 ? "cmp" : "sub");
+ add_str((rd == 15 && S) ? "cmp" : "sub");
+ if (rd == 15 && S) no_rd = 1;
+ no_S = no_rd;
break;
case 14:
add_str("rsb");
@@ -794,9 +816,15 @@
return;
}
- if (S) add_char('s');
+ if (S && !no_S) add_char('s');
if (it_cond_name) add_str(it_cond_name);
- if (op_code != 14 && (op_code != 4 || rd != 15) && (op_code != 3 || rn == 15)) add_str(".w");
+ if ((op_code != 14) && (op_code != 6) && (op_code != 4 || (rd != 15 || !S)) &&
+ (op_code != 3 || rn == 15)) add_str(".w");
+ if (op_code == 6) {
+ /* add bt/tb */
+ if ((suffix >> 5) & 1) add_str("tb");
+ else add_str("bt");
+ }
add_char(' ');
if (!no_rd) {
@@ -811,19 +839,21 @@
if (!I) {
uint8_t rm = (suffix & 0xf);
- uint32_t shift_imm = ((suffix >> 10) & 0x1c) | ((suffix >> 6) & 3);
- uint32_t shift_type = (suffix >> 4) & 3;
add_reg_name(rm);
if (shift_type != 0 || shift_imm != 0) {
if (shift_type == 3 && shift_imm == 0) {
- add_str(", ");
- add_str("rrx");
+ if (!no_shift) {
+ add_str(", ");
+ add_str("rrx");
+ }
}
else {
add_str(", ");
- add_str(shift_names[shift_type]);
- add_char(' ');
+ if (!no_shift) {
+ add_str(shift_names[shift_type]);
+ add_char(' ');
+ }
add_char('#');
if (shift_type >= 1 && shift_imm == 0) shift_imm = 32;
add_dec_uint32(shift_imm);
@@ -839,6 +869,7 @@
uint32_t op_code = (instr >> 4) & 0x1f;
uint32_t rn = instr & 0xf;
uint32_t imm = suffix & 0xff;
+ int sat_inst = 0;
imm |= (suffix & 0x7000) >> 4;
if (instr & (1 << 10)) imm |= 0x800;
@@ -858,9 +889,11 @@
break;
case 16:
add_str("ssat");
+ sat_inst = 1;
break;
case 18:
add_str(suffix & 0x70c0 ? "ssat" : "ssat16");
+ sat_inst = 1;
break;
case 20:
add_str("sbfx");
@@ -870,9 +903,11 @@
break;
case 24:
add_str("usat");
+ sat_inst = 1;
break;
case 26:
add_str(suffix & 0x70c0 ? "usat" : "usat16");
+ sat_inst = 1;
break;
case 28:
add_str("ubfx");
@@ -885,17 +920,30 @@
if (it_cond_name) add_str(it_cond_name);
add_char(' ');
add_reg_name((suffix >> 8) & 0xf);
+
+ if (sat_inst) {
+ /* SSAT, SSAT16, USAT, USAT16 */
+ uint32_t sat_imm = suffix & 0x1f;
+ add_str(", #");
+ if (op_code == 16 || op_code == 18) add_dec_uint32(sat_imm + 1);
+ else add_dec_uint32(sat_imm);
+ }
+
if (op_code == 4) {
imm |= rn << 12;
}
else if (op_code == 22 && rn == 15) {
/* Nothing */
}
+ else if (op_code == 12) {
+ imm = ((instr & 0xf) << 12) + (((instr >> 10) & 1) << 11) +
+ (((suffix >> 12) & 0x7) << 8) + (suffix & 0xff);
+ }
else {
add_str(", ");
add_reg_name(rn);
}
- add_str(", #");
+ if (!sat_inst) add_str(", #");
if (op_code == 22) {
imm = (suffix >> 12) & 7;
imm = (imm << 2) | ((suffix >> 6) & 3);
@@ -910,6 +958,21 @@
add_str(", #");
add_dec_uint32((suffix & 0x1f) + 1);
}
+ else if (sat_inst) {
+ /* SSAT, SSAT16, USAT, USAT16 */
+ uint32_t sh = (instr >> 5) & 1;
+ uint32_t imm3 = (suffix >> 12) & 7;
+ uint32_t imm2 = (suffix >> 6) & 3;
+ if (sh == 1 && imm2 == 0 && imm3 == 0) {
+ /* SSAT16, USAT16 : nothing to add. */
+ return;
+ } else if (sh == 1) {
+ add_str(", asr #");
+ } else {
+ add_str(", lsl #");
+ }
+ add_dec_uint32((imm3 << 2) + imm2);
+ }
else {
add_dec_uint32(imm);
}
@@ -925,8 +988,8 @@
int S = (instr & (1 << 10)) != 0;
int32_t offset = suffix & 0x7ff;
offset |= (instr & 0x3f) << 11;
- if (J2) offset |= 1 << 17;
- if (J1) offset |= 1 << 18;
+ if (J1) offset |= 1 << 17;
+ if (J2) offset |= 1 << 18;
if (S) offset |= 0xfff80000;
offset = offset << 1;
add_char('b');
@@ -962,23 +1025,21 @@
/* Change Processor State, and hints */
uint32_t imod = (suffix >> 9) & 3;
int M = (suffix & (1 << 8)) != 0;
+ /* hint instructions if imod == 00 && M == 0 */
if (imod || M) {
- uint32_t mode = instr & 0x1f;
+ uint32_t mode = suffix & 0x1f;
add_str("cps");
+ if (imod >= 2) add_str(imod == 2 ? "ie" : "id");
if (imod >= 2) {
- add_str(imod == 2 ? "ie" : "id");
- add_str(".w");
- add_char(' ');
- if (instr & (1 << 7)) add_char('a');
- if (instr & (1 << 6)) add_char('i');
- if (instr & (1 << 5)) add_char('f');
- if (M) {
- add_str(", #");
- add_dec_uint32(mode);
- }
+ add_str(M ? " " : ".w ");
+ if (suffix & (1 << 7)) add_char('a');
+ if (suffix & (1 << 6)) add_char('i');
+ if (suffix & (1 << 5)) add_char('f');
+ if (((suffix >> 5) & 0x7) != 0 && M) add_str(", ");
}
- else {
- add_str(".w #");
+ else add_char(' ');
+ if (M) {
+ add_char('#');
add_dec_uint32(mode);
}
return;
@@ -996,7 +1057,7 @@
return;
}
if ((suffix & 0x00f0) == 0x00f0) {
- add_str("bdg");
+ add_str("dbg");
if (it_cond_name) add_str(it_cond_name);
add_str(" #");
add_dec_uint32(suffix & 0xf);
@@ -1015,18 +1076,33 @@
case 5: add_str("dmb"); break;
case 6: add_str("isb"); break;
}
- if (op >= 4 && op <= 6) {
+ if (op >= 4 && op < 6) {
if (it_cond_name) add_str(it_cond_name);
add_char(' ');
switch (suffix & 0xf) {
case 15: add_str("sy"); break;
case 14: add_str("st"); break;
+ case 13: add_str("ld"); break;
case 11: add_str("ish"); break;
case 10: add_str("ishst"); break;
+ case 9: add_str("ishld"); break;
case 7: add_str("nsh"); break;
case 6: add_str("nshst"); break;
+ case 5: add_str("nshld"); break;
case 3: add_str("osh"); break;
case 2: add_str("oshst"); break;
+ case 1: add_str("oshld"); break;
+ default:
+ add_str(" #");
+ add_dec_uint32(suffix & 0xf);
+ }
+ return;
+ }
+ if (op == 6) {
+ if (it_cond_name) add_str(it_cond_name);
+ add_char(' ');
+ switch (suffix & 0xf) {
+ case 15: add_str("sy"); break;
default:
add_str(" #");
add_dec_uint32(suffix & 0xf);
@@ -1037,6 +1113,8 @@
}
if (op == 0x3c) {
/* Branch and Exchange Jazelle */
+ add_str("bxj ");
+ add_reg_name(instr & 0x0f);
return;
}
if (op == 0x3d) {
@@ -1090,7 +1168,7 @@
add_str("b");
w = 1;
}
- else if ((suffix & 0xf800) == 0xe800) {
+ else if ((suffix & 0xd000) == 0xc000) {
add_str("blx");
}
else {
@@ -1104,12 +1182,12 @@
}
static void disassemble_memory_hints(uint16_t suffix) {
- if ((instr & 0xff50) == 0xf810) {
+ if ((instr & 0xfe50) == 0xf810) {
int U = instr & (1 << 7);
int W = instr & (1 << 5);
uint32_t rn = instr & 0xf;
uint32_t imm = suffix & 0xfff;
- add_str("pld");
+ add_str(((instr >>8) & 1) ? "pli" : "pld");
if (W) add_char('w');
add_str(" [");
add_reg_name(rn);
@@ -1134,6 +1212,7 @@
if (imm != 0 || !U) {
add_str(", #");
if (!U) add_char('-');
+ else add_char('+');
add_dec_uint32(imm);
}
add_char(']');
@@ -1300,6 +1379,7 @@
uint32_t ra = (suffix >> 12) & 0xf;
uint32_t op2 = (suffix >> 4) & 3;
int no_ra = 0;
+ int no_size = 0;
switch (op1) {
case 0:
if (op2 == 0) {
@@ -1315,6 +1395,7 @@
if (ra != 15) add_str("smla");
else add_str("smul");
no_ra = ra == 15;
+ no_size = ra == 15;
add_char(suffix & (1 << 5) ? 't' : 'b');
add_char(suffix & (1 << 4) ? 't' : 'b');
break;
@@ -1323,6 +1404,7 @@
if (ra != 15) add_str("smlad");
else add_str("smuad");
no_ra = ra == 15;
+ no_size = 1;
if (suffix & (1 << 4)) add_char('x');
}
break;
@@ -1331,6 +1413,7 @@
if (ra != 15) add_str("smlaw");
else add_str("smulw");
no_ra = ra == 15;
+ no_size = 1;
add_char(suffix & (1 << 4) ? 't' : 'b');
}
break;
@@ -1339,6 +1422,7 @@
if (ra != 15) add_str("smlsd");
else add_str("smusd");
no_ra = ra == 15;
+ no_size = 1;
if (suffix & (1 << 4)) add_char('x');
}
break;
@@ -1347,6 +1431,7 @@
if (ra != 15) add_str("smmla");
else add_str("smmul");
no_ra = ra == 15;
+ no_size = 1;
if (suffix & (1 << 4)) add_char('r');
}
break;
@@ -1354,13 +1439,15 @@
if (op2 < 2) {
add_str("smmls");
if (suffix & (1 << 4)) add_char('r');
+ no_size = 1;
}
break;
case 7:
if (op2 == 0) {
- if (ra != 15) add_str("usad8");
- else add_str("usada8");
+ if (ra != 15) add_str("usada8");
+ else add_str("usad8");
no_ra = ra == 15;
+ no_size = no_ra;
}
break;
}
@@ -1369,6 +1456,7 @@
uint32_t rd = (suffix >> 8) & 0xf;
uint32_t rm = (suffix >> 0) & 0xf;
if (it_cond_name) add_str(it_cond_name);
+ if (op2 == 0 && ra == 15 && no_size == 0) add_str(".w");
add_char(' ');
add_reg_name(rd);
add_str(", ");
@@ -1411,6 +1499,7 @@
}
else if ((op2 >> 1) == 6) {
add_str("smlald");
+ if (suffix & (1 << 4)) add_char('x');
}
break;
case 5:
@@ -1499,23 +1588,51 @@
}
if ((instr & 0xfe00) == 0xf800) {
+ uint32_t rn = (instr & 0xf);
int T = 0;
- if ((instr & 0x0070) == 0x0010 && (suffix & 0xf000) == 0xf000) {
- /* Memory hints */
+ /*
+ * PLD = 0xf890f000
+ * 0xf810fc00
+ * 0xf81ff000
+ * 0xf810f000
+ * PLI = 0xf990f000
+ * 0xf910fc00
+ * 0xf91ff000
+ * 0xf910f000
+ */
+ if ((((instr & 0xffd0) == 0xf890) && ((suffix & 0xf000) == 0xf000)) ||
+ (((instr & 0xffd0) == 0xf810) && ((suffix & 0xff00) == 0xfc00)) ||
+ (((instr & 0xff7f) == 0xf81f) && ((suffix & 0xf000) == 0xf000)) ||
+ (((instr & 0xffd0) == 0xf810) && ((suffix & 0xffc0) == 0xf000)) ||
+ (((instr & 0xfff0) == 0xf990) && ((suffix & 0xf000) == 0xf000)) ||
+ (((instr & 0xfff0) == 0xf910) && ((suffix & 0xff00) == 0xfc00)) ||
+ (((instr & 0xff7f) == 0xf91f) && ((suffix & 0xf000) == 0xf000)) ||
+ (((instr & 0xfff0) == 0xf910) && ((suffix & 0xffc0) == 0xf000))) {
+ /* Memory hints (PLI/PLD) */
disassemble_memory_hints(suffix);
return;
}
+ if (((instr & 0xf84d) == 0xf84d) &&
+ (((suffix & 0x0bff) == 0x0b04) || ((suffix & 0x0dff) == 0x0d04))) {
+ if ((suffix & 0x0bff) == 0x0b04) add_str("pop.w {");
+ else add_str("push.w {");
+ add_reg_name((suffix >> 12) & 0xf);
+ add_char('}');
+ return;
+ }
+
/* Load/Store single data item */
+
T = (suffix & 0x0f00) == 0x0e00 && (instr & (1 << 7)) == 0;
add_str(instr & (1 << 4) ? "ldr" : "str");
if ((instr & (1 << 6)) == 0) {
if (instr & (1 << 8)) add_char('s');
add_char(instr & (1 << 5) ? 'h' : 'b');
}
- if (T) add_char('t');
+ if (T && rn != 15) add_char('t');
if (it_cond_name) add_str(it_cond_name);
- if (!T) add_str(".w");
+ if (!T || rn == 15) add_str(".w");
add_char(' ');
add_reg_name((suffix >> 12) & 0xf);
add_str(", [");