• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2006 The Android Open Source Project
2 
3 #include <stdio.h>
4 #include <string.h>
5 #include "armdis.h"
6 #include "opcode.h"
7 
8 static const char *cond_names[] = {
9     "eq",
10     "ne",
11     "cs",
12     "cc",
13     "mi",
14     "pl",
15     "vs",
16     "vc",
17     "hi",
18     "ls",
19     "ge",
20     "lt",
21     "gt",
22     "le",
23     "",
24     "RESERVED"
25 };
26 
27 // Indexed by the shift type (bits 6-5)
28 static const char *shift_names[] = {
29     "LSL",
30     "LSR",
31     "ASR",
32     "ROR"
33 };
34 
cond_to_str(int cond)35 static const char* cond_to_str(int cond) {
36     return cond_names[cond];
37 }
38 
disasm(uint32_t addr,uint32_t insn,char * result)39 char *Arm::disasm(uint32_t addr, uint32_t insn, char *result)
40 {
41     static char   buf[80];
42     char          *ptr;
43 
44     ptr = result ? result : buf;
45     Opcode opcode = decode(insn);
46     switch (opcode) {
47         case OP_INVALID:
48             sprintf(ptr, "Invalid");
49             return ptr;
50         case OP_UNDEFINED:
51             sprintf(ptr, "Undefined");
52             return ptr;
53         case OP_ADC:
54         case OP_ADD:
55         case OP_AND:
56         case OP_BIC:
57         case OP_CMN:
58         case OP_CMP:
59         case OP_EOR:
60         case OP_MOV:
61         case OP_MVN:
62         case OP_ORR:
63         case OP_RSB:
64         case OP_RSC:
65         case OP_SBC:
66         case OP_SUB:
67         case OP_TEQ:
68         case OP_TST:
69             return disasm_alu(opcode, insn, ptr);
70         case OP_B:
71         case OP_BL:
72             return disasm_branch(addr, opcode, insn, ptr);
73         case OP_BKPT:
74             return disasm_bkpt(insn, ptr);
75         case OP_BLX:
76             // not supported yet
77             break;
78         case OP_BX:
79             return disasm_bx(insn, ptr);
80         case OP_CDP:
81             sprintf(ptr, "cdp");
82             return ptr;
83         case OP_CLZ:
84             return disasm_clz(insn, ptr);
85         case OP_LDC:
86             sprintf(ptr, "ldc");
87             return ptr;
88         case OP_LDM:
89         case OP_STM:
90             return disasm_memblock(opcode, insn, ptr);
91         case OP_LDR:
92         case OP_LDRB:
93         case OP_LDRBT:
94         case OP_LDRT:
95         case OP_STR:
96         case OP_STRB:
97         case OP_STRBT:
98         case OP_STRT:
99             return disasm_mem(insn, ptr);
100         case OP_LDRH:
101         case OP_LDRSB:
102         case OP_LDRSH:
103         case OP_STRH:
104             return disasm_memhalf(insn, ptr);
105         case OP_MCR:
106         case OP_MRC:
107             return disasm_mcr(opcode, insn, ptr);
108         case OP_MLA:
109             return disasm_mla(opcode, insn, ptr);
110         case OP_MRS:
111             return disasm_mrs(insn, ptr);
112         case OP_MSR:
113             return disasm_msr(insn, ptr);
114         case OP_MUL:
115             return disasm_mul(opcode, insn, ptr);
116         case OP_PLD:
117             return disasm_pld(insn, ptr);
118         case OP_STC:
119             sprintf(ptr, "stc");
120             return ptr;
121         case OP_SWI:
122             return disasm_swi(insn, ptr);
123         case OP_SWP:
124         case OP_SWPB:
125             return disasm_swp(opcode, insn, ptr);
126         case OP_UMLAL:
127         case OP_UMULL:
128         case OP_SMLAL:
129         case OP_SMULL:
130             return disasm_umlal(opcode, insn, ptr);
131         default:
132             sprintf(ptr, "Error");
133             return ptr;
134     }
135     return NULL;
136 }
137 
disasm_alu(Opcode opcode,uint32_t insn,char * ptr)138 char *Arm::disasm_alu(Opcode opcode, uint32_t insn, char *ptr)
139 {
140     static const uint8_t kNoOperand1 = 1;
141     static const uint8_t kNoDest = 2;
142     static const uint8_t kNoSbit = 4;
143 
144     char rn_str[20];
145     char rd_str[20];
146     uint8_t flags = 0;
147     uint8_t cond = (insn >> 28) & 0xf;
148     uint8_t is_immed = (insn >> 25) & 0x1;
149     uint8_t bit_s = (insn >> 20) & 1;
150     uint8_t rn = (insn >> 16) & 0xf;
151     uint8_t rd = (insn >> 12) & 0xf;
152     uint8_t immed = insn & 0xff;
153 
154     const char *opname = opcode_names[opcode];
155     switch (opcode) {
156         case OP_CMN:
157         case OP_CMP:
158         case OP_TEQ:
159         case OP_TST:
160             flags = kNoDest | kNoSbit;
161             break;
162         case OP_MOV:
163         case OP_MVN:
164             flags = kNoOperand1;
165             break;
166         default:
167             break;
168     }
169 
170     // The "mov" instruction ignores the first operand (rn).
171     rn_str[0] = 0;
172     if ((flags & kNoOperand1) == 0) {
173         sprintf(rn_str, "r%d, ", rn);
174     }
175 
176     // The following instructions do not write the result register (rd):
177     // tst, teq, cmp, cmn.
178     rd_str[0] = 0;
179     if ((flags & kNoDest) == 0) {
180         sprintf(rd_str, "r%d, ", rd);
181     }
182 
183     const char *sbit_str = "";
184     if (bit_s && !(flags & kNoSbit))
185         sbit_str = "s";
186 
187     if (is_immed) {
188         sprintf(ptr, "%s%s%s\t%s%s#%u  ; 0x%x",
189                 opname, cond_to_str(cond), sbit_str, rd_str, rn_str, immed, immed);
190         return ptr;
191     }
192 
193     uint8_t shift_is_reg = (insn >> 4) & 1;
194     uint8_t rotate = (insn >> 8) & 0xf;
195     uint8_t rm = insn & 0xf;
196     uint8_t shift_type = (insn >> 5) & 0x3;
197     uint8_t rs = (insn >> 8) & 0xf;
198     uint8_t shift_amount = (insn >> 7) & 0x1f;
199     uint32_t rotated_val = immed;
200     uint8_t rotate2 = rotate << 1;
201     rotated_val = (rotated_val >> rotate2) | (rotated_val << (32 - rotate2));
202 
203     if (!shift_is_reg && shift_type == 0 && shift_amount == 0) {
204         sprintf(ptr, "%s%s%s\t%s%sr%d",
205                 opname, cond_to_str(cond), sbit_str, rd_str, rn_str, rm);
206         return ptr;
207     }
208 
209     const char *shift_name = shift_names[shift_type];
210     if (shift_is_reg) {
211         sprintf(ptr, "%s%s%s\t%s%sr%d, %s r%d",
212                 opname, cond_to_str(cond), sbit_str, rd_str, rn_str, rm,
213                 shift_name, rs);
214         return ptr;
215     }
216     if (shift_amount == 0) {
217         if (shift_type == 3) {
218             sprintf(ptr, "%s%s%s\t%s%sr%d, RRX",
219                     opname, cond_to_str(cond), sbit_str, rd_str, rn_str, rm);
220             return ptr;
221         }
222         shift_amount = 32;
223     }
224     sprintf(ptr, "%s%s%s\t%s%sr%d, %s #%u",
225             opname, cond_to_str(cond), sbit_str, rd_str, rn_str, rm,
226             shift_name, shift_amount);
227     return ptr;
228 }
229 
disasm_branch(uint32_t addr,Opcode opcode,uint32_t insn,char * ptr)230 char *Arm::disasm_branch(uint32_t addr, Opcode opcode, uint32_t insn, char *ptr)
231 {
232     uint8_t cond = (insn >> 28) & 0xf;
233     uint32_t offset = insn & 0xffffff;
234     // Sign-extend the 24-bit offset
235     if ((offset >> 23) & 1)
236         offset |= 0xff000000;
237 
238     // Pre-compute the left-shift and the prefetch offset
239     offset <<= 2;
240     offset += 8;
241     addr += offset;
242     const char *opname = opcode_names[opcode];
243     sprintf(ptr, "%s%s\t0x%x", opname, cond_to_str(cond), addr);
244     return ptr;
245 }
246 
disasm_bx(uint32_t insn,char * ptr)247 char *Arm::disasm_bx(uint32_t insn, char *ptr)
248 {
249     uint8_t cond = (insn >> 28) & 0xf;
250     uint8_t rn = insn & 0xf;
251     sprintf(ptr, "bx%s\tr%d", cond_to_str(cond), rn);
252     return ptr;
253 }
254 
disasm_bkpt(uint32_t insn,char * ptr)255 char *Arm::disasm_bkpt(uint32_t insn, char *ptr)
256 {
257     uint32_t immed = (((insn >> 8) & 0xfff) << 4) | (insn & 0xf);
258     sprintf(ptr, "bkpt\t#%d", immed);
259     return ptr;
260 }
261 
disasm_clz(uint32_t insn,char * ptr)262 char *Arm::disasm_clz(uint32_t insn, char *ptr)
263 {
264     uint8_t cond = (insn >> 28) & 0xf;
265     uint8_t rd = (insn >> 12) & 0xf;
266     uint8_t rm = insn & 0xf;
267     sprintf(ptr, "clz%s\tr%d, r%d", cond_to_str(cond), rd, rm);
268     return ptr;
269 }
270 
disasm_memblock(Opcode opcode,uint32_t insn,char * ptr)271 char *Arm::disasm_memblock(Opcode opcode, uint32_t insn, char *ptr)
272 {
273     char tmp_reg[10], tmp_list[80];
274 
275     uint8_t cond = (insn >> 28) & 0xf;
276     uint8_t write_back = (insn >> 21) & 0x1;
277     uint8_t bit_s = (insn >> 22) & 0x1;
278     uint8_t is_up = (insn >> 23) & 0x1;
279     uint8_t is_pre = (insn >> 24) & 0x1;
280     uint8_t rn = (insn >> 16) & 0xf;
281     uint16_t reg_list = insn & 0xffff;
282 
283     const char *opname = opcode_names[opcode];
284 
285     const char *bang = "";
286     if (write_back)
287         bang = "!";
288 
289     const char *carret = "";
290     if (bit_s)
291         carret = "^";
292 
293     const char *comma = "";
294     tmp_list[0] = 0;
295     for (int ii = 0; ii < 16; ++ii) {
296         if (reg_list & (1 << ii)) {
297             sprintf(tmp_reg, "%sr%d", comma, ii);
298             strcat(tmp_list, tmp_reg);
299             comma = ",";
300         }
301     }
302 
303     const char *addr_mode = "";
304     if (is_pre) {
305         if (is_up) {
306             addr_mode = "ib";
307         } else {
308             addr_mode = "db";
309         }
310     } else {
311         if (is_up) {
312             addr_mode = "ia";
313         } else {
314             addr_mode = "da";
315         }
316     }
317 
318     sprintf(ptr, "%s%s%s\tr%d%s, {%s}%s",
319             opname, cond_to_str(cond), addr_mode, rn, bang, tmp_list, carret);
320     return ptr;
321 }
322 
disasm_mem(uint32_t insn,char * ptr)323 char *Arm::disasm_mem(uint32_t insn, char *ptr)
324 {
325     uint8_t cond = (insn >> 28) & 0xf;
326     uint8_t is_reg = (insn >> 25) & 0x1;
327     uint8_t is_load = (insn >> 20) & 0x1;
328     uint8_t write_back = (insn >> 21) & 0x1;
329     uint8_t is_byte = (insn >> 22) & 0x1;
330     uint8_t is_up = (insn >> 23) & 0x1;
331     uint8_t is_pre = (insn >> 24) & 0x1;
332     uint8_t rn = (insn >> 16) & 0xf;
333     uint8_t rd = (insn >> 12) & 0xf;
334     uint16_t offset = insn & 0xfff;
335 
336     const char *opname = "ldr";
337     if (!is_load)
338         opname = "str";
339 
340     const char *bang = "";
341     if (write_back)
342         bang = "!";
343 
344     const char *minus = "";
345     if (is_up == 0)
346         minus = "-";
347 
348     const char *byte = "";
349     if (is_byte)
350         byte = "b";
351 
352     if (is_reg == 0) {
353         if (is_pre) {
354             if (offset == 0) {
355                 sprintf(ptr, "%s%s%s\tr%d, [r%d]",
356                         opname, cond_to_str(cond), byte, rd, rn);
357             } else {
358                 sprintf(ptr, "%s%s%s\tr%d, [r%d, #%s%u]%s",
359                         opname, cond_to_str(cond), byte, rd, rn, minus, offset, bang);
360             }
361         } else {
362             const char *transfer = "";
363             if (write_back)
364                 transfer = "t";
365             sprintf(ptr, "%s%s%s%s\tr%d, [r%d], #%s%u",
366                     opname, cond_to_str(cond), byte, transfer, rd, rn, minus, offset);
367         }
368         return ptr;
369     }
370 
371     uint8_t rm = insn & 0xf;
372     uint8_t shift_type = (insn >> 5) & 0x3;
373     uint8_t shift_amount = (insn >> 7) & 0x1f;
374 
375     const char *shift_name = shift_names[shift_type];
376 
377     if (is_pre) {
378         if (shift_amount == 0) {
379             if (shift_type == 0) {
380                 sprintf(ptr, "%s%s%s\tr%d, [r%d, %sr%d]%s",
381                         opname, cond_to_str(cond), byte, rd, rn, minus, rm, bang);
382                 return ptr;
383             }
384             if (shift_type == 3) {
385                 sprintf(ptr, "%s%s%s\tr%d, [r%d, %sr%d, RRX]%s",
386                         opname, cond_to_str(cond), byte, rd, rn, minus, rm, bang);
387                 return ptr;
388             }
389             shift_amount = 32;
390         }
391         sprintf(ptr, "%s%s%s\tr%d, [r%d, %sr%d, %s #%u]%s",
392                 opname, cond_to_str(cond), byte, rd, rn, minus, rm,
393                 shift_name, shift_amount, bang);
394         return ptr;
395     }
396 
397     const char *transfer = "";
398     if (write_back)
399         transfer = "t";
400 
401     if (shift_amount == 0) {
402         if (shift_type == 0) {
403             sprintf(ptr, "%s%s%s%s\tr%d, [r%d], %sr%d",
404                     opname, cond_to_str(cond), byte, transfer, rd, rn, minus, rm);
405             return ptr;
406         }
407         if (shift_type == 3) {
408             sprintf(ptr, "%s%s%s%s\tr%d, [r%d], %sr%d, RRX",
409                     opname, cond_to_str(cond), byte, transfer, rd, rn, minus, rm);
410             return ptr;
411         }
412         shift_amount = 32;
413     }
414 
415     sprintf(ptr, "%s%s%s%s\tr%d, [r%d], %sr%d, %s #%u",
416             opname, cond_to_str(cond), byte, transfer, rd, rn, minus, rm,
417             shift_name, shift_amount);
418     return ptr;
419 }
420 
disasm_memhalf(uint32_t insn,char * ptr)421 char *Arm::disasm_memhalf(uint32_t insn, char *ptr)
422 {
423     uint8_t cond = (insn >> 28) & 0xf;
424     uint8_t is_load = (insn >> 20) & 0x1;
425     uint8_t write_back = (insn >> 21) & 0x1;
426     uint8_t is_immed = (insn >> 22) & 0x1;
427     uint8_t is_up = (insn >> 23) & 0x1;
428     uint8_t is_pre = (insn >> 24) & 0x1;
429     uint8_t rn = (insn >> 16) & 0xf;
430     uint8_t rd = (insn >> 12) & 0xf;
431     uint8_t bits_65 = (insn >> 5) & 0x3;
432     uint8_t rm = insn & 0xf;
433     uint8_t offset = (((insn >> 8) & 0xf) << 4) | (insn & 0xf);
434 
435     const char *opname = "ldr";
436     if (is_load == 0)
437         opname = "str";
438 
439     const char *width = "";
440     if (bits_65 == 1)
441         width = "h";
442     else if (bits_65 == 2)
443         width = "sb";
444     else
445         width = "sh";
446 
447     const char *bang = "";
448     if (write_back)
449         bang = "!";
450     const char *minus = "";
451     if (is_up == 0)
452         minus = "-";
453 
454     if (is_immed) {
455         if (is_pre) {
456             if (offset == 0) {
457                 sprintf(ptr, "%s%sh\tr%d, [r%d]", opname, cond_to_str(cond), rd, rn);
458             } else {
459                 sprintf(ptr, "%s%sh\tr%d, [r%d, #%s%u]%s",
460                         opname, cond_to_str(cond), rd, rn, minus, offset, bang);
461             }
462         } else {
463             sprintf(ptr, "%s%sh\tr%d, [r%d], #%s%u",
464                     opname, cond_to_str(cond), rd, rn, minus, offset);
465         }
466         return ptr;
467     }
468 
469     if (is_pre) {
470         sprintf(ptr, "%s%sh\tr%d, [r%d, %sr%d]%s",
471                 opname, cond_to_str(cond), rd, rn, minus, rm, bang);
472     } else {
473         sprintf(ptr, "%s%sh\tr%d, [r%d], %sr%d",
474                 opname, cond_to_str(cond), rd, rn, minus, rm);
475     }
476     return ptr;
477 }
478 
disasm_mcr(Opcode opcode,uint32_t insn,char * ptr)479 char *Arm::disasm_mcr(Opcode opcode, uint32_t insn, char *ptr)
480 {
481     uint8_t cond = (insn >> 28) & 0xf;
482     uint8_t crn = (insn >> 16) & 0xf;
483     uint8_t crd = (insn >> 12) & 0xf;
484     uint8_t cpnum = (insn >> 8) & 0xf;
485     uint8_t opcode2 = (insn >> 5) & 0x7;
486     uint8_t crm = insn & 0xf;
487 
488     const char *opname = opcode_names[opcode];
489     sprintf(ptr, "%s%s\t%d, 0, r%d, cr%d, cr%d, {%d}",
490             opname, cond_to_str(cond), cpnum, crd, crn, crm, opcode2);
491     return ptr;
492 }
493 
disasm_mla(Opcode opcode,uint32_t insn,char * ptr)494 char *Arm::disasm_mla(Opcode opcode, uint32_t insn, char *ptr)
495 {
496     uint8_t cond = (insn >> 28) & 0xf;
497     uint8_t rd = (insn >> 16) & 0xf;
498     uint8_t rn = (insn >> 12) & 0xf;
499     uint8_t rs = (insn >> 8) & 0xf;
500     uint8_t rm = insn & 0xf;
501     uint8_t bit_s = (insn >> 20) & 1;
502 
503     const char *opname = opcode_names[opcode];
504     sprintf(ptr, "%s%s%s\tr%d, r%d, r%d, r%d",
505             opname, cond_to_str(cond), bit_s ? "s" : "", rd, rm, rs, rn);
506     return ptr;
507 }
508 
disasm_umlal(Opcode opcode,uint32_t insn,char * ptr)509 char *Arm::disasm_umlal(Opcode opcode, uint32_t insn, char *ptr)
510 {
511     uint8_t cond = (insn >> 28) & 0xf;
512     uint8_t rdhi = (insn >> 16) & 0xf;
513     uint8_t rdlo = (insn >> 12) & 0xf;
514     uint8_t rs = (insn >> 8) & 0xf;
515     uint8_t rm = insn & 0xf;
516     uint8_t bit_s = (insn >> 20) & 1;
517 
518     const char *opname = opcode_names[opcode];
519     sprintf(ptr, "%s%s%s\tr%d, r%d, r%d, r%d",
520             opname, cond_to_str(cond), bit_s ? "s" : "", rdlo, rdhi, rm, rs);
521     return ptr;
522 }
523 
disasm_mul(Opcode opcode,uint32_t insn,char * ptr)524 char *Arm::disasm_mul(Opcode opcode, uint32_t insn, char *ptr)
525 {
526     uint8_t cond = (insn >> 28) & 0xf;
527     uint8_t rd = (insn >> 16) & 0xf;
528     uint8_t rs = (insn >> 8) & 0xf;
529     uint8_t rm = insn & 0xf;
530     uint8_t bit_s = (insn >> 20) & 1;
531 
532     const char *opname = opcode_names[opcode];
533     sprintf(ptr, "%s%s%s\tr%d, r%d, r%d",
534             opname, cond_to_str(cond), bit_s ? "s" : "", rd, rm, rs);
535     return ptr;
536 }
537 
disasm_mrs(uint32_t insn,char * ptr)538 char *Arm::disasm_mrs(uint32_t insn, char *ptr)
539 {
540     uint8_t cond = (insn >> 28) & 0xf;
541     uint8_t rd = (insn >> 12) & 0xf;
542     uint8_t ps = (insn >> 22) & 1;
543 
544     sprintf(ptr, "mrs%s\tr%d, %s", cond_to_str(cond), rd, ps ? "spsr" : "cpsr");
545     return ptr;
546 }
547 
disasm_msr(uint32_t insn,char * ptr)548 char *Arm::disasm_msr(uint32_t insn, char *ptr)
549 {
550     char flags[8];
551     int flag_index = 0;
552     uint8_t cond = (insn >> 28) & 0xf;
553     uint8_t is_immed = (insn >> 25) & 0x1;
554     uint8_t pd = (insn >> 22) & 1;
555     uint8_t mask = (insn >> 16) & 0xf;
556 
557     if (mask & 1)
558         flags[flag_index++] = 'c';
559     if (mask & 2)
560         flags[flag_index++] = 'x';
561     if (mask & 4)
562         flags[flag_index++] = 's';
563     if (mask & 8)
564         flags[flag_index++] = 'f';
565     flags[flag_index] = 0;
566 
567     if (is_immed) {
568         uint32_t immed = insn & 0xff;
569         uint8_t rotate = (insn >> 8) & 0xf;
570         uint8_t rotate2 = rotate << 1;
571         uint32_t rotated_val = (immed >> rotate2) | (immed << (32 - rotate2));
572         sprintf(ptr, "msr%s\t%s_%s, #0x%x",
573                 cond_to_str(cond), pd ? "spsr" : "cpsr", flags, rotated_val);
574         return ptr;
575     }
576 
577     uint8_t rm = insn & 0xf;
578 
579     sprintf(ptr, "msr%s\t%s_%s, r%d",
580             cond_to_str(cond), pd ? "spsr" : "cpsr", flags, rm);
581     return ptr;
582 }
583 
disasm_pld(uint32_t insn,char * ptr)584 char *Arm::disasm_pld(uint32_t insn, char *ptr)
585 {
586     uint8_t is_reg = (insn >> 25) & 0x1;
587     uint8_t is_up = (insn >> 23) & 0x1;
588     uint8_t rn = (insn >> 16) & 0xf;
589 
590     const char *minus = "";
591     if (is_up == 0)
592         minus = "-";
593 
594     if (is_reg) {
595         uint8_t rm = insn & 0xf;
596         sprintf(ptr, "pld\t[r%d, %sr%d]", rn, minus, rm);
597         return ptr;
598     }
599 
600     uint16_t offset = insn & 0xfff;
601     if (offset == 0) {
602         sprintf(ptr, "pld\t[r%d]", rn);
603     } else {
604         sprintf(ptr, "pld\t[r%d, #%s%u]", rn, minus, offset);
605     }
606     return ptr;
607 }
608 
disasm_swi(uint32_t insn,char * ptr)609 char *Arm::disasm_swi(uint32_t insn, char *ptr)
610 {
611     uint8_t cond = (insn >> 28) & 0xf;
612     uint32_t sysnum = insn & 0x00ffffff;
613 
614     sprintf(ptr, "swi%s 0x%x", cond_to_str(cond), sysnum);
615     return ptr;
616 }
617 
disasm_swp(Opcode opcode,uint32_t insn,char * ptr)618 char *Arm::disasm_swp(Opcode opcode, uint32_t insn, char *ptr)
619 {
620     uint8_t cond = (insn >> 28) & 0xf;
621     uint8_t rn = (insn >> 16) & 0xf;
622     uint8_t rd = (insn >> 12) & 0xf;
623     uint8_t rm = insn & 0xf;
624 
625     const char *opname = opcode_names[opcode];
626     sprintf(ptr, "%s%s\tr%d, r%d, [r%d]", opname, cond_to_str(cond), rd, rm, rn);
627     return ptr;
628 }
629 
decode(uint32_t insn)630 Opcode Arm::decode(uint32_t insn) {
631     uint32_t bits27_26 = (insn >> 26) & 0x3;
632     switch (bits27_26) {
633         case 0x0:
634             return decode00(insn);
635         case 0x1:
636             return decode01(insn);
637         case 0x2:
638             return decode10(insn);
639         case 0x3:
640             return decode11(insn);
641     }
642     return OP_INVALID;
643 }
644 
decode00(uint32_t insn)645 Opcode Arm::decode00(uint32_t insn) {
646     uint8_t bit25 = (insn >> 25) & 0x1;
647     uint8_t bit4 = (insn >> 4) & 0x1;
648     if (bit25 == 0 && bit4 == 1) {
649         if ((insn & 0x0ffffff0) == 0x012fff10) {
650             // Bx instruction
651             return OP_BX;
652         }
653         if ((insn & 0x0ff000f0) == 0x01600010) {
654             // Clz instruction
655             return OP_CLZ;
656         }
657         if ((insn & 0xfff000f0) == 0xe1200070) {
658             // Bkpt instruction
659             return OP_BKPT;
660         }
661         uint32_t bits7_4 = (insn >> 4) & 0xf;
662         if (bits7_4 == 0x9) {
663             if ((insn & 0x0ff00ff0) == 0x01000090) {
664                 // Swp instruction
665                 uint8_t bit22 = (insn >> 22) & 0x1;
666                 if (bit22)
667                     return OP_SWPB;
668                 return OP_SWP;
669             }
670             // One of the multiply instructions
671             return decode_mul(insn);
672         }
673 
674         uint8_t bit7 = (insn >> 7) & 0x1;
675         if (bit7 == 1) {
676             // One of the load/store halfword/byte instructions
677             return decode_ldrh(insn);
678         }
679     }
680 
681     // One of the data processing instructions
682     return decode_alu(insn);
683 }
684 
decode01(uint32_t insn)685 Opcode Arm::decode01(uint32_t insn) {
686     uint8_t is_reg = (insn >> 25) & 0x1;
687     uint8_t bit4 = (insn >> 4) & 0x1;
688     if (is_reg == 1 && bit4 == 1)
689         return OP_UNDEFINED;
690     uint8_t is_load = (insn >> 20) & 0x1;
691     uint8_t is_byte = (insn >> 22) & 0x1;
692     if ((insn & 0xfd70f000) == 0xf550f000) {
693         // Pre-load
694         return OP_PLD;
695     }
696     if (is_load) {
697         if (is_byte) {
698             // Load byte
699             return OP_LDRB;
700         }
701         // Load word
702         return OP_LDR;
703     }
704     if (is_byte) {
705         // Store byte
706         return OP_STRB;
707     }
708     // Store word
709     return OP_STR;
710 }
711 
decode10(uint32_t insn)712 Opcode Arm::decode10(uint32_t insn) {
713     uint8_t bit25 = (insn >> 25) & 0x1;
714     if (bit25 == 0) {
715         // LDM/STM
716         uint8_t is_load = (insn >> 20) & 0x1;
717         if (is_load)
718             return OP_LDM;
719         return OP_STM;
720     }
721     // Branch or Branch with link
722     uint8_t is_link = (insn >> 24) & 1;
723     uint32_t offset = insn & 0xffffff;
724 
725     // Sign-extend the 24-bit offset
726     if ((offset >> 23) & 1)
727         offset |= 0xff000000;
728 
729     // Pre-compute the left-shift and the prefetch offset
730     offset <<= 2;
731     offset += 8;
732     if (is_link == 0)
733         return OP_B;
734     return OP_BL;
735 }
736 
decode11(uint32_t insn)737 Opcode Arm::decode11(uint32_t insn) {
738     uint8_t bit25 = (insn >> 25) & 0x1;
739     if (bit25 == 0) {
740         // LDC, SDC
741         uint8_t is_load = (insn >> 20) & 0x1;
742         if (is_load) {
743             // LDC
744             return OP_LDC;
745         }
746         // STC
747         return OP_STC;
748     }
749 
750     uint8_t bit24 = (insn >> 24) & 0x1;
751     if (bit24 == 0x1) {
752         // SWI
753         return OP_SWI;
754     }
755 
756     uint8_t bit4 = (insn >> 4) & 0x1;
757     uint8_t cpnum = (insn >> 8) & 0xf;
758 
759     if (cpnum == 15) {
760         // Special case for coprocessor 15
761         uint8_t opcode = (insn >> 21) & 0x7;
762         if (bit4 == 0 || opcode != 0) {
763             // This is an unexpected bit pattern.  Create an undefined
764             // instruction in case this is ever executed.
765             return OP_UNDEFINED;
766         }
767 
768         // MRC, MCR
769         uint8_t is_mrc = (insn >> 20) & 0x1;
770         if (is_mrc)
771             return OP_MRC;
772         return OP_MCR;
773     }
774 
775     if (bit4 == 0) {
776         // CDP
777         return OP_CDP;
778     }
779     // MRC, MCR
780     uint8_t is_mrc = (insn >> 20) & 0x1;
781     if (is_mrc)
782         return OP_MRC;
783     return OP_MCR;
784 }
785 
decode_mul(uint32_t insn)786 Opcode Arm::decode_mul(uint32_t insn) {
787     uint8_t bit24 = (insn >> 24) & 0x1;
788     if (bit24 != 0) {
789         // This is an unexpected bit pattern.  Create an undefined
790         // instruction in case this is ever executed.
791         return OP_UNDEFINED;
792     }
793     uint8_t bit23 = (insn >> 23) & 0x1;
794     uint8_t bit22_U = (insn >> 22) & 0x1;
795     uint8_t bit21_A = (insn >> 21) & 0x1;
796     if (bit23 == 0) {
797         // 32-bit multiply
798         if (bit22_U != 0) {
799             // This is an unexpected bit pattern.  Create an undefined
800             // instruction in case this is ever executed.
801             return OP_UNDEFINED;
802         }
803         if (bit21_A == 0)
804             return OP_MUL;
805         return OP_MLA;
806     }
807     // 64-bit multiply
808     if (bit22_U == 0) {
809         // Unsigned multiply long
810         if (bit21_A == 0)
811             return OP_UMULL;
812         return OP_UMLAL;
813     }
814     // Signed multiply long
815     if (bit21_A == 0)
816         return OP_SMULL;
817     return OP_SMLAL;
818 }
819 
decode_ldrh(uint32_t insn)820 Opcode Arm::decode_ldrh(uint32_t insn) {
821     uint8_t is_load = (insn >> 20) & 0x1;
822     uint8_t bits_65 = (insn >> 5) & 0x3;
823     if (is_load) {
824         if (bits_65 == 0x1) {
825             // Load unsigned halfword
826             return OP_LDRH;
827         } else if (bits_65 == 0x2) {
828             // Load signed byte
829             return OP_LDRSB;
830         }
831         // Signed halfword
832         if (bits_65 != 0x3) {
833             // This is an unexpected bit pattern.  Create an undefined
834             // instruction in case this is ever executed.
835             return OP_UNDEFINED;
836         }
837         // Load signed halfword
838         return OP_LDRSH;
839     }
840     // Store halfword
841     if (bits_65 != 0x1) {
842         // This is an unexpected bit pattern.  Create an undefined
843         // instruction in case this is ever executed.
844         return OP_UNDEFINED;
845     }
846     // Store halfword
847     return OP_STRH;
848 }
849 
decode_alu(uint32_t insn)850 Opcode Arm::decode_alu(uint32_t insn) {
851     uint8_t is_immed = (insn >> 25) & 0x1;
852     uint8_t opcode = (insn >> 21) & 0xf;
853     uint8_t bit_s = (insn >> 20) & 1;
854     uint8_t shift_is_reg = (insn >> 4) & 1;
855     uint8_t bit7 = (insn >> 7) & 1;
856     if (!is_immed && shift_is_reg && (bit7 != 0)) {
857         // This is an unexpected bit pattern.  Create an undefined
858         // instruction in case this is ever executed.
859         return OP_UNDEFINED;
860     }
861     switch (opcode) {
862         case 0x0:
863             return OP_AND;
864         case 0x1:
865             return OP_EOR;
866         case 0x2:
867             return OP_SUB;
868         case 0x3:
869             return OP_RSB;
870         case 0x4:
871             return OP_ADD;
872         case 0x5:
873             return OP_ADC;
874         case 0x6:
875             return OP_SBC;
876         case 0x7:
877             return OP_RSC;
878         case 0x8:
879             if (bit_s)
880                 return OP_TST;
881             return OP_MRS;
882         case 0x9:
883             if (bit_s)
884                 return OP_TEQ;
885             return OP_MSR;
886         case 0xa:
887             if (bit_s)
888                 return OP_CMP;
889             return OP_MRS;
890         case 0xb:
891             if (bit_s)
892                 return OP_CMN;
893             return OP_MSR;
894         case 0xc:
895             return OP_ORR;
896         case 0xd:
897             return OP_MOV;
898         case 0xe:
899             return OP_BIC;
900         case 0xf:
901             return OP_MVN;
902     }
903     // Unreachable
904     return OP_INVALID;
905 }
906