Merge pull request #948 from ermshiperete/bug-xamarin-2394
[mono.git] / mono / arch / arm / arm-dis.c
1 /*
2  * Copyright (c) 2002 Sergey Chaban <serge@wildwestsoftware.com>
3  */
4
5
6 #include <stdarg.h>
7
8 #include "arm-dis.h"
9 #include "arm-codegen.h"
10
11
12 static ARMDis* gdisasm = NULL;
13
14 static int use_reg_alias = 1;
15
16 const static char* cond[] = {
17         "eq", "ne", "cs", "cc", "mi", "pl", "vs", "vc",
18         "hi", "ls", "ge", "lt", "gt", "le", "", "nv"
19 };
20
21 const static char* ops[] = {
22         "and", "eor", "sub", "rsb", "add", "adc", "sbc", "rsc",
23         "tst", "teq", "cmp", "cmn", "orr", "mov", "bic", "mvn"
24 };
25
26 const static char* shift_types[] = {"lsl", "lsr", "asr", "ror"};
27
28 const static char* mul_ops[] = {
29         "mul", "mla", "?", "?", "umull", "umlal", "smull", "smlal"
30 };
31
32 const static char* reg_alias[] = {
33         "a1", "a2", "a3", "a4",
34         "r4", "r5", "r6", "r7", "r8", "r9", "r10",
35         "fp", "ip", "sp", "lr", "pc"
36 };
37
38 const static char* msr_fld[] = {"f", "c", "x", "?", "s"};
39
40
41 /* private functions prototypes (to keep compiler happy) */
42 void chk_out(ARMDis* dis);
43 void dump_reg(ARMDis* dis, int reg);
44 void dump_creg(ARMDis* dis, int creg);
45 void dump_reglist(ARMDis* dis, int reg_list);
46 void init_gdisasm(void);
47
48 void dump_br(ARMDis* dis, ARMInstr i);
49 void dump_cdp(ARMDis* dis, ARMInstr i);
50 void dump_cdt(ARMDis* dis, ARMInstr i);
51 void dump_crt(ARMDis* dis, ARMInstr i);
52 void dump_dpi(ARMDis* dis, ARMInstr i);
53 void dump_hxfer(ARMDis* dis, ARMInstr i);
54 void dump_mrs(ARMDis* dis, ARMInstr i);
55 void dump_mrt(ARMDis* dis, ARMInstr i);
56 void dump_msr(ARMDis* dis, ARMInstr i);
57 void dump_mul(ARMDis* dis, ARMInstr i);
58 void dump_swi(ARMDis* dis, ARMInstr i);
59 void dump_swp(ARMDis* dis, ARMInstr i);
60 void dump_wxfer(ARMDis* dis, ARMInstr i);
61 void dump_clz(ARMDis* dis, ARMInstr i);
62
63
64 /*
65 void out(ARMDis* dis, const char* format, ...) {
66         va_list arglist;
67         va_start(arglist, format);
68         fprintf(dis->dis_out, format, arglist);
69         va_end(arglist);
70 }
71 */
72
73
74 void chk_out(ARMDis* dis) {
75         if (dis != NULL && dis->dis_out == NULL) dis->dis_out = stdout;
76 }
77
78
79 void armdis_set_output(ARMDis* dis, FILE* f) {
80         if (dis != NULL) {
81                 dis->dis_out = f;
82                 chk_out(dis);
83         }
84 }
85
86 FILE* armdis_get_output(ARMDis* dis) {
87         return (dis != NULL ? dis->dis_out : NULL);
88 }
89
90
91
92
93 void dump_reg(ARMDis* dis, int reg) {
94         reg &= 0xF;
95         if (!use_reg_alias || (reg > 3 && reg < 11)) {
96                 fprintf(dis->dis_out, "r%d", reg);
97         } else {
98                 fprintf(dis->dis_out, "%s", reg_alias[reg]);
99         }
100 }
101
102 void dump_creg(ARMDis* dis, int creg) {
103         if (dis != NULL) {
104                 creg &= 0xF;
105                 fprintf(dis->dis_out, "c%d", creg);
106         }
107 }
108
109 void dump_reglist(ARMDis* dis, int reg_list) {
110         int i = 0, j, n = 0;
111         int m1 = 1, m2, rn;
112         while (i < 16) {
113                 if ((reg_list & m1) != 0) {
114                         if (n != 0) fprintf(dis->dis_out, ", ");
115                         n++;
116                         dump_reg(dis, i);
117                         for (j = i+1, rn = 0, m2 = m1<<1; j < 16; ++j, m2<<=1) {
118                                 if ((reg_list & m2) != 0) ++rn;
119                                 else break;
120                         }
121                         i+=rn;
122                         if (rn > 1) {
123                                 fprintf(dis->dis_out, "-");
124                                 dump_reg(dis, i);
125                         } else if (rn == 1) {
126                                 fprintf(dis->dis_out, ", ");
127                                 dump_reg(dis, i);
128                         }
129                         m1<<=(rn+1);
130                         i++;
131                 } else {
132                         ++i;
133                         m1<<=1;
134                 }
135         }
136 }
137
138
139 void dump_br(ARMDis* dis, ARMInstr i) {
140         fprintf(dis->dis_out, "b%s%s\t%x\t; %p -> %#x",
141             (i.br.link == 1) ? "l" : "",
142             cond[i.br.cond], i.br.offset, dis->pi, (int)dis->pi + 4*2 + ((int)(i.br.offset << 8) >> 6));
143 }
144
145
146 void dump_dpi(ARMDis* dis, ARMInstr i) {
147         fprintf(dis->dis_out, "%s%s", ops[i.dpi.all.opcode], cond[i.dpi.all.cond]);
148
149         if ((i.dpi.all.opcode < ARMOP_TST || i.dpi.all.opcode > ARMOP_CMN) && (i.dpi.all.s != 0)) {
150                 fprintf(dis->dis_out, "s");
151         }
152
153         fprintf(dis->dis_out, "\t");
154
155         if ((i.dpi.all.opcode < ARMOP_TST) || (i.dpi.all.opcode > ARMOP_CMN)) {
156                 /* for comparison operations Rd is ignored */
157                 dump_reg(dis, i.dpi.all.rd);
158                 fprintf(dis->dis_out, ", ");
159         }
160
161         if ((i.dpi.all.opcode != ARMOP_MOV) && (i.dpi.all.opcode != ARMOP_MVN)) {
162                 /* for MOV/MVN Rn is ignored */
163                 dump_reg(dis, i.dpi.all.rn);
164                 fprintf(dis->dis_out, ", ");
165         }
166
167         if (i.dpi.all.type == 1) {
168                 /* immediate */
169                 if (i.dpi.op2_imm.rot != 0) {
170                         fprintf(dis->dis_out, "#%d, %d\t; 0x%x", i.dpi.op2_imm.imm, i.dpi.op2_imm.rot << 1,
171                                 ARM_SCALE(i.dpi.op2_imm.imm, (i.dpi.op2_imm.rot << 1)) );
172                 } else {
173                         fprintf(dis->dis_out, "#%d\t; 0x%x", i.dpi.op2_imm.imm, i.dpi.op2_imm.imm);
174                 }
175         } else {
176                 /* reg-reg */
177                 if (i.dpi.op2_reg.tag == 0) {
178                         /* op2 is reg shift by imm */
179                         dump_reg(dis, i.dpi.op2_reg_imm.r2.rm);
180                         if (i.dpi.op2_reg_imm.imm.shift != 0) {
181                                 fprintf(dis->dis_out, " %s #%d", shift_types[i.dpi.op2_reg_imm.r2.type], i.dpi.op2_reg_imm.imm.shift);
182                         }
183                 } else {
184                         /* op2 is reg shift by reg */
185                         dump_reg(dis, i.dpi.op2_reg_reg.r2.rm);
186                         fprintf(dis->dis_out, " %s ", shift_types[i.dpi.op2_reg_reg.r2.type]);
187                         dump_reg(dis, i.dpi.op2_reg_reg.reg.rs);
188                 }
189
190         }
191 }
192
193 void dump_wxfer(ARMDis* dis, ARMInstr i) {
194         fprintf(dis->dis_out, "%s%s%s%s\t",
195                 (i.wxfer.all.ls == 0) ? "str" : "ldr",
196                 cond[i.generic.cond],
197                 (i.wxfer.all.b == 0) ? "" : "b",
198                 (i.wxfer.all.ls != 0 && i.wxfer.all.wb != 0) ? "t" : "");
199         dump_reg(dis, i.wxfer.all.rd);
200         fprintf(dis->dis_out, ", [");
201         dump_reg(dis, i.wxfer.all.rn);
202         fprintf(dis->dis_out, "%s, ", (i.wxfer.all.p == 0) ? "]" : "");
203
204         if (i.wxfer.all.type == 0) { /* imm */
205                 fprintf(dis->dis_out, "#%s%d", (i.wxfer.all.u == 0) ? "-" : "", i.wxfer.all.op2_imm);
206         } else {
207                 dump_reg(dis, i.wxfer.op2_reg_imm.r2.rm);
208                 if (i.wxfer.op2_reg_imm.imm.shift != 0) {
209                         fprintf(dis->dis_out, " %s #%d", shift_types[i.wxfer.op2_reg_imm.r2.type], i.wxfer.op2_reg_imm.imm.shift);
210                 }
211         }
212
213         if (i.wxfer.all.p != 0) {
214                 /* close pre-index instr, also check for write-back */
215                 fprintf(dis->dis_out, "]%s", (i.wxfer.all.wb != 0) ? "!" : "");
216         }
217 }
218
219 void dump_hxfer(ARMDis* dis, ARMInstr i) {
220         fprintf(dis->dis_out, "%s%s%s%s\t",
221                 (i.hxfer.ls == 0) ? "str" : "ldr",
222                 cond[i.generic.cond],
223                 (i.hxfer.s != 0) ? "s" : "",
224                 (i.hxfer.h != 0) ? "h" : "b");
225         dump_reg(dis, i.hxfer.rd);
226         fprintf(dis->dis_out, ", [");
227         dump_reg(dis, i.hxfer.rn);
228         fprintf(dis->dis_out, "%s, ", (i.hxfer.p == 0) ? "]" : "");
229
230         if (i.hxfer.type != 0) { /* imm */
231                 fprintf(dis->dis_out, "#%s%d", (i.hxfer.u == 0) ? "-" : "", (i.hxfer.imm_hi << 4) | i.hxfer.rm);
232         } else {
233                 dump_reg(dis, i.hxfer.rm);
234         }
235
236         if (i.hxfer.p != 0) {
237                 /* close pre-index instr, also check for write-back */
238                 fprintf(dis->dis_out, "]%s", (i.hxfer.wb != 0) ? "!" : "");
239         }
240 }
241
242
243 void dump_mrt(ARMDis* dis, ARMInstr i) {
244         fprintf(dis->dis_out, "%s%s%s%s\t", (i.mrt.ls == 0) ? "stm" : "ldm", cond[i.mrt.cond],
245                 (i.mrt.u == 0) ? "d" : "i", (i.mrt.p == 0) ? "a" : "b");
246         dump_reg(dis, i.mrt.rn);
247         fprintf(dis->dis_out, "%s, {", (i.mrt.wb != 0) ? "!" : "");
248         dump_reglist(dis, i.mrt.reg_list);
249         fprintf(dis->dis_out, "}");
250 }
251
252
253 void dump_swp(ARMDis* dis, ARMInstr i) {
254         fprintf(dis->dis_out, "swp%s%s ", cond[i.swp.cond], (i.swp.b != 0) ? "b" : "");
255         dump_reg(dis, i.swp.rd);
256         fprintf(dis->dis_out, ", ");
257         dump_reg(dis, i.swp.rm);
258         fprintf(dis->dis_out, ", [");
259         dump_reg(dis, i.swp.rn);
260         fprintf(dis->dis_out, "]");
261 }
262
263
264 void dump_mul(ARMDis* dis, ARMInstr i) {
265         fprintf(dis->dis_out, "%s%s%s\t", mul_ops[i.mul.opcode], cond[i.mul.cond], (i.mul.s != 0) ? "s" : "");
266         switch (i.mul.opcode) {
267         case ARMOP_MUL:
268                 dump_reg(dis, i.mul.rd);
269                 fprintf(dis->dis_out, ", ");
270                 dump_reg(dis, i.mul.rm);
271                 fprintf(dis->dis_out, ", ");
272                 dump_reg(dis, i.mul.rs);
273                 break;
274         case ARMOP_MLA:
275                 dump_reg(dis, i.mul.rd);
276                 fprintf(dis->dis_out, ", ");
277                 dump_reg(dis, i.mul.rm);
278                 fprintf(dis->dis_out, ", ");
279                 dump_reg(dis, i.mul.rs);
280                 fprintf(dis->dis_out, ", ");
281                 dump_reg(dis, i.mul.rn);
282                 break;
283         case ARMOP_UMULL:
284         case ARMOP_UMLAL:
285         case ARMOP_SMULL:
286         case ARMOP_SMLAL:
287                 dump_reg(dis, i.mul.rd);
288                 fprintf(dis->dis_out, ", ");
289                 dump_reg(dis, i.mul.rn);
290                 fprintf(dis->dis_out, ", ");
291                 dump_reg(dis, i.mul.rm);
292                 fprintf(dis->dis_out, ", ");
293                 dump_reg(dis, i.mul.rs);
294                 break;
295         default:
296                 fprintf(dis->dis_out, "DCD 0x%x\t; <unknown>", i.raw);
297                 break;
298         }
299 }
300
301
302 void dump_cdp(ARMDis* dis, ARMInstr i) {
303         fprintf(dis->dis_out, "cdp%s\tp%d, %d, ", cond[i.generic.cond], i.cdp.cpn, i.cdp.op);
304         dump_creg(dis, i.cdp.crd);
305         fprintf(dis->dis_out, ", ");
306         dump_creg(dis, i.cdp.crn);
307         fprintf(dis->dis_out, ", ");
308         dump_creg(dis, i.cdp.crm);
309
310         if (i.cdp.op2 != 0) {
311                 fprintf(dis->dis_out, ", %d", i.cdp.op2);
312         }
313 }
314
315
316 void dump_cdt(ARMDis* dis, ARMInstr i) {
317         fprintf(dis->dis_out, "%s%s%s\tp%d, ", (i.cdt.ls == 0) ? "stc" : "ldc",
318                 cond[i.generic.cond], (i.cdt.n != 0) ? "l" : "", i.cdt.cpn);
319         dump_creg(dis, i.cdt.crd);
320         fprintf(dis->dis_out, ", ");
321         dump_reg(dis, i.cdt.rn);
322
323         if (i.cdt.p == 0) {
324                 fprintf(dis->dis_out, "]");
325         }
326
327         if (i.cdt.offs != 0) {
328                 fprintf(dis->dis_out, ", #%d", i.cdt.offs);
329         }
330
331         if (i.cdt.p != 0) {
332                 fprintf(dis->dis_out, "]%s", (i.cdt.wb != 0) ? "!" : "");
333         }
334 }
335
336
337 void dump_crt(ARMDis* dis, ARMInstr i) {
338         fprintf(dis->dis_out, "%s%s\tp%d, %d, ", (i.crt.ls == 0) ? "mrc" : "mcr",
339                 cond[i.generic.cond], i.crt.cpn, i.crt.op1);
340         dump_reg(dis, i.crt.rd);
341         fprintf(dis->dis_out, ", ");
342         dump_creg(dis, i.crt.crn);
343         fprintf(dis->dis_out, ", ");
344         dump_creg(dis, i.crt.crm);
345
346         if (i.crt.op2 != 0) {
347                 fprintf(dis->dis_out, ", %d", i.crt.op2);
348         }
349 }
350
351
352 void dump_msr(ARMDis* dis, ARMInstr i) {
353         fprintf(dis->dis_out, "msr%s\t%spsr_, ", cond[i.generic.cond],
354                 (i.msr.all.sel == 0) ? "s" : "c");
355         if (i.msr.all.type == 0) {
356                 /* reg */
357                 fprintf(dis->dis_out, "%s, ", msr_fld[i.msr.all.fld]);
358                 dump_reg(dis, i.msr.all.rm);
359         } else {
360                 /* imm */
361                 fprintf(dis->dis_out, "f, #%d", i.msr.op2_imm.imm << i.msr.op2_imm.rot);
362         }
363 }
364
365
366 void dump_mrs(ARMDis* dis, ARMInstr i) {
367         fprintf(dis->dis_out, "mrs%s\t", cond[i.generic.cond]);
368         dump_reg(dis, i.mrs.rd);
369         fprintf(dis->dis_out, ", %spsr", (i.mrs.sel == 0) ? "s" : "c");
370 }
371
372
373 void dump_swi(ARMDis* dis, ARMInstr i) {
374         fprintf(dis->dis_out, "swi%s\t%d", cond[i.generic.cond], i.swi.num);
375 }
376
377
378 void dump_clz(ARMDis* dis, ARMInstr i) {
379         fprintf(dis->dis_out, "clz\t");
380         dump_reg(dis, i.clz.rd);
381         fprintf(dis->dis_out, ", ");
382         dump_reg(dis, i.clz.rm);
383         fprintf(dis->dis_out, "\n");
384 }
385
386
387
388 void armdis_decode(ARMDis* dis, void* p, int size) {
389         int i;
390         arminstr_t* pi = (arminstr_t*)p;
391         ARMInstr instr;
392
393         if (dis == NULL) return;
394
395         chk_out(dis);
396
397         size/=sizeof(arminstr_t);
398
399         for (i=0; i<size; ++i) {
400                 fprintf(dis->dis_out, "%p:\t%08x\t", pi, *pi);
401                 dis->pi = pi;
402                 instr.raw = *pi++;
403
404                 if ((instr.raw & ARM_BR_MASK) == ARM_BR_TAG) {
405                         dump_br(dis, instr);
406                 } else if ((instr.raw & ARM_SWP_MASK) == ARM_SWP_TAG) {
407                         dump_swp(dis, instr);
408                 } else if ((instr.raw & ARM_MUL_MASK) == ARM_MUL_TAG) {
409                         dump_mul(dis, instr);
410                 } else if ((instr.raw & ARM_CLZ_MASK) == ARM_CLZ_TAG) {
411                         dump_clz(dis, instr);
412                 } else if ((instr.raw & ARM_WXFER_MASK) == ARM_WXFER_TAG) {
413                         dump_wxfer(dis, instr);
414                 } else if ((instr.raw & ARM_HXFER_MASK) == ARM_HXFER_TAG) {
415                         dump_hxfer(dis, instr);
416                 } else if ((instr.raw & ARM_DPI_MASK) == ARM_DPI_TAG) {
417                         dump_dpi(dis, instr);
418                 } else if ((instr.raw & ARM_MRT_MASK) == ARM_MRT_TAG) {
419                         dump_mrt(dis, instr);
420                 } else if ((instr.raw & ARM_CDP_MASK) == ARM_CDP_TAG) {
421                         dump_cdp(dis, instr);
422                 } else if ((instr.raw & ARM_CDT_MASK) == ARM_CDT_TAG) {
423                         dump_cdt(dis, instr);
424                 } else if ((instr.raw & ARM_CRT_MASK) == ARM_CRT_TAG) {
425                         dump_crt(dis, instr);
426                 } else if ((instr.raw & ARM_MSR_MASK) == ARM_MSR_TAG) {
427                         dump_msr(dis, instr);
428                 } else if ((instr.raw & ARM_MRS_MASK) == ARM_MRS_TAG) {
429                         dump_mrs(dis, instr);
430                 } else if ((instr.raw & ARM_SWI_MASK) == ARM_SWI_TAG) {
431                         dump_swi(dis, instr);
432                 } else {
433                         fprintf(dis->dis_out, "DCD 0x%x\t; <unknown>", instr.raw);
434                 }
435
436                 fprintf(dis->dis_out, "\n");
437         }
438 }
439
440
441 void armdis_open(ARMDis* dis, const char* dump_name) {
442         if (dis != NULL && dump_name != NULL) {
443                 armdis_set_output(dis, fopen(dump_name, "w"));
444         }
445 }
446
447
448 void armdis_close(ARMDis* dis) {
449         if (dis->dis_out != NULL && dis->dis_out != stdout && dis->dis_out != stderr) {
450                 fclose(dis->dis_out);
451                 dis->dis_out = NULL;
452         }
453 }
454
455
456 void armdis_dump(ARMDis* dis, const char* dump_name, void* p, int size) {
457         armdis_open(dis, dump_name);
458         armdis_decode(dis, p, size);
459         armdis_close(dis);
460 }
461
462
463 void armdis_init(ARMDis* dis) {
464         if (dis != NULL) {
465                 /* set to stdout */
466                 armdis_set_output(dis, NULL);
467         }
468 }
469
470
471
472
473 void init_gdisasm() {
474         if (gdisasm == NULL) {
475                 gdisasm = (ARMDis*)malloc(sizeof(ARMDis));
476                 armdis_init(gdisasm);
477         }
478 }
479
480 void _armdis_set_output(FILE* f) {
481         init_gdisasm();
482         armdis_set_output(gdisasm, f);
483 }
484
485 FILE* _armdis_get_output() {
486         init_gdisasm();
487         return armdis_get_output(gdisasm);
488 }
489
490 void _armdis_decode(void* p, int size) {
491         init_gdisasm();
492         armdis_decode(gdisasm, p, size);
493 }
494
495 void _armdis_open(const char* dump_name) {
496         init_gdisasm();
497         armdis_open(gdisasm, dump_name);
498 }
499
500 void _armdis_close() {
501         init_gdisasm();
502         armdis_close(gdisasm);
503 }
504
505 void _armdis_dump(const char* dump_name, void* p, int size) {
506         init_gdisasm();
507         armdis_dump(gdisasm, dump_name, p, size);
508 }
509