/src/capstonev5/arch/Mips/MipsInstPrinter.c
Line | Count | Source (jump to first uncovered line) |
1 | | //===-- MipsInstPrinter.cpp - Convert Mips MCInst to assembly syntax ------===// |
2 | | // |
3 | | // The LLVM Compiler Infrastructure |
4 | | // |
5 | | // This file is distributed under the University of Illinois Open Source |
6 | | // License. See LICENSE.TXT for details. |
7 | | // |
8 | | //===----------------------------------------------------------------------===// |
9 | | // |
10 | | // This class prints an Mips MCInst to a .s file. |
11 | | // |
12 | | //===----------------------------------------------------------------------===// |
13 | | |
14 | | /* Capstone Disassembly Engine */ |
15 | | /* By Nguyen Anh Quynh <aquynh@gmail.com>, 2013-2015 */ |
16 | | |
17 | | #ifdef CAPSTONE_HAS_MIPS |
18 | | |
19 | | #include <capstone/platform.h> |
20 | | #include <stdlib.h> |
21 | | #include <stdio.h> // debug |
22 | | #include <string.h> |
23 | | |
24 | | #include "MipsInstPrinter.h" |
25 | | #include "../../MCInst.h" |
26 | | #include "../../utils.h" |
27 | | #include "../../SStream.h" |
28 | | #include "../../MCRegisterInfo.h" |
29 | | #include "MipsMapping.h" |
30 | | |
31 | | #include "MipsInstPrinter.h" |
32 | | |
33 | | static void printUnsignedImm(MCInst *MI, int opNum, SStream *O); |
34 | | static char *printAliasInstr(MCInst *MI, SStream *O, void *info); |
35 | | static char *printAlias(MCInst *MI, SStream *OS); |
36 | | |
37 | | // These enumeration declarations were originally in MipsInstrInfo.h but |
38 | | // had to be moved here to avoid circular dependencies between |
39 | | // LLVMMipsCodeGen and LLVMMipsAsmPrinter. |
40 | | |
41 | | // Mips Condition Codes |
42 | | typedef enum Mips_CondCode { |
43 | | // To be used with float branch True |
44 | | Mips_FCOND_F, |
45 | | Mips_FCOND_UN, |
46 | | Mips_FCOND_OEQ, |
47 | | Mips_FCOND_UEQ, |
48 | | Mips_FCOND_OLT, |
49 | | Mips_FCOND_ULT, |
50 | | Mips_FCOND_OLE, |
51 | | Mips_FCOND_ULE, |
52 | | Mips_FCOND_SF, |
53 | | Mips_FCOND_NGLE, |
54 | | Mips_FCOND_SEQ, |
55 | | Mips_FCOND_NGL, |
56 | | Mips_FCOND_LT, |
57 | | Mips_FCOND_NGE, |
58 | | Mips_FCOND_LE, |
59 | | Mips_FCOND_NGT, |
60 | | |
61 | | // To be used with float branch False |
62 | | // This conditions have the same mnemonic as the |
63 | | // above ones, but are used with a branch False; |
64 | | Mips_FCOND_T, |
65 | | Mips_FCOND_OR, |
66 | | Mips_FCOND_UNE, |
67 | | Mips_FCOND_ONE, |
68 | | Mips_FCOND_UGE, |
69 | | Mips_FCOND_OGE, |
70 | | Mips_FCOND_UGT, |
71 | | Mips_FCOND_OGT, |
72 | | Mips_FCOND_ST, |
73 | | Mips_FCOND_GLE, |
74 | | Mips_FCOND_SNE, |
75 | | Mips_FCOND_GL, |
76 | | Mips_FCOND_NLT, |
77 | | Mips_FCOND_GE, |
78 | | Mips_FCOND_NLE, |
79 | | Mips_FCOND_GT |
80 | | } Mips_CondCode; |
81 | | |
82 | | #define GET_INSTRINFO_ENUM |
83 | | #include "MipsGenInstrInfo.inc" |
84 | | |
85 | | static const char *getRegisterName(unsigned RegNo); |
86 | | static void printInstruction(MCInst *MI, SStream *O, const MCRegisterInfo *MRI); |
87 | | |
88 | | static void set_mem_access(MCInst *MI, bool status) |
89 | 76.6k | { |
90 | 76.6k | MI->csh->doing_mem = status; |
91 | | |
92 | 76.6k | if (MI->csh->detail != CS_OPT_ON) |
93 | 0 | return; |
94 | | |
95 | 76.6k | if (status) { |
96 | 38.3k | MI->flat_insn->detail->mips.operands[MI->flat_insn->detail->mips.op_count].type = MIPS_OP_MEM; |
97 | 38.3k | MI->flat_insn->detail->mips.operands[MI->flat_insn->detail->mips.op_count].mem.base = MIPS_REG_INVALID; |
98 | 38.3k | MI->flat_insn->detail->mips.operands[MI->flat_insn->detail->mips.op_count].mem.disp = 0; |
99 | 38.3k | } else { |
100 | | // done, create the next operand slot |
101 | 38.3k | MI->flat_insn->detail->mips.op_count++; |
102 | 38.3k | } |
103 | 76.6k | } |
104 | | |
105 | | static bool isReg(MCInst *MI, unsigned OpNo, unsigned R) |
106 | 11.5k | { |
107 | 11.5k | return (MCOperand_isReg(MCInst_getOperand(MI, OpNo)) && |
108 | 11.5k | MCOperand_getReg(MCInst_getOperand(MI, OpNo)) == R); |
109 | 11.5k | } |
110 | | |
111 | | static const char* MipsFCCToString(Mips_CondCode CC) |
112 | 0 | { |
113 | 0 | switch (CC) { |
114 | 0 | default: return 0; // never reach |
115 | 0 | case Mips_FCOND_F: |
116 | 0 | case Mips_FCOND_T: return "f"; |
117 | 0 | case Mips_FCOND_UN: |
118 | 0 | case Mips_FCOND_OR: return "un"; |
119 | 0 | case Mips_FCOND_OEQ: |
120 | 0 | case Mips_FCOND_UNE: return "eq"; |
121 | 0 | case Mips_FCOND_UEQ: |
122 | 0 | case Mips_FCOND_ONE: return "ueq"; |
123 | 0 | case Mips_FCOND_OLT: |
124 | 0 | case Mips_FCOND_UGE: return "olt"; |
125 | 0 | case Mips_FCOND_ULT: |
126 | 0 | case Mips_FCOND_OGE: return "ult"; |
127 | 0 | case Mips_FCOND_OLE: |
128 | 0 | case Mips_FCOND_UGT: return "ole"; |
129 | 0 | case Mips_FCOND_ULE: |
130 | 0 | case Mips_FCOND_OGT: return "ule"; |
131 | 0 | case Mips_FCOND_SF: |
132 | 0 | case Mips_FCOND_ST: return "sf"; |
133 | 0 | case Mips_FCOND_NGLE: |
134 | 0 | case Mips_FCOND_GLE: return "ngle"; |
135 | 0 | case Mips_FCOND_SEQ: |
136 | 0 | case Mips_FCOND_SNE: return "seq"; |
137 | 0 | case Mips_FCOND_NGL: |
138 | 0 | case Mips_FCOND_GL: return "ngl"; |
139 | 0 | case Mips_FCOND_LT: |
140 | 0 | case Mips_FCOND_NLT: return "lt"; |
141 | 0 | case Mips_FCOND_NGE: |
142 | 0 | case Mips_FCOND_GE: return "nge"; |
143 | 0 | case Mips_FCOND_LE: |
144 | 0 | case Mips_FCOND_NLE: return "le"; |
145 | 0 | case Mips_FCOND_NGT: |
146 | 0 | case Mips_FCOND_GT: return "ngt"; |
147 | 0 | } |
148 | 0 | } |
149 | | |
150 | | static void printRegName(SStream *OS, unsigned RegNo) |
151 | 248k | { |
152 | 248k | SStream_concat(OS, "$%s", getRegisterName(RegNo)); |
153 | 248k | } |
154 | | |
155 | | void Mips_printInst(MCInst *MI, SStream *O, void *info) |
156 | 149k | { |
157 | 149k | char *mnem; |
158 | | |
159 | 149k | switch (MCInst_getOpcode(MI)) { |
160 | 149k | default: break; |
161 | 149k | case Mips_Save16: |
162 | 0 | case Mips_SaveX16: |
163 | 0 | case Mips_Restore16: |
164 | 0 | case Mips_RestoreX16: |
165 | 0 | return; |
166 | 149k | } |
167 | | |
168 | | // Try to print any aliases first. |
169 | 149k | mnem = printAliasInstr(MI, O, info); |
170 | 149k | if (!mnem) { |
171 | 138k | mnem = printAlias(MI, O); |
172 | 138k | if (!mnem) { |
173 | 136k | printInstruction(MI, O, NULL); |
174 | 136k | } |
175 | 138k | } |
176 | | |
177 | 149k | if (mnem) { |
178 | | // fixup instruction id due to the change in alias instruction |
179 | 13.0k | MCInst_setOpcodePub(MI, Mips_map_insn(mnem)); |
180 | 13.0k | cs_mem_free(mnem); |
181 | 13.0k | } |
182 | 149k | } |
183 | | |
184 | | static void printOperand(MCInst *MI, unsigned OpNo, SStream *O) |
185 | 339k | { |
186 | 339k | MCOperand *Op; |
187 | | |
188 | 339k | if (OpNo >= MI->size) |
189 | 0 | return; |
190 | | |
191 | 339k | Op = MCInst_getOperand(MI, OpNo); |
192 | 339k | if (MCOperand_isReg(Op)) { |
193 | 241k | unsigned int reg = MCOperand_getReg(Op); |
194 | 241k | printRegName(O, reg); |
195 | 241k | reg = Mips_map_register(reg); |
196 | 241k | if (MI->csh->detail) { |
197 | 241k | if (MI->csh->doing_mem) { |
198 | 38.3k | MI->flat_insn->detail->mips.operands[MI->flat_insn->detail->mips.op_count].mem.base = reg; |
199 | 203k | } else { |
200 | 203k | MI->flat_insn->detail->mips.operands[MI->flat_insn->detail->mips.op_count].type = MIPS_OP_REG; |
201 | 203k | MI->flat_insn->detail->mips.operands[MI->flat_insn->detail->mips.op_count].reg = reg; |
202 | 203k | MI->flat_insn->detail->mips.op_count++; |
203 | 203k | } |
204 | 241k | } |
205 | 241k | } else if (MCOperand_isImm(Op)) { |
206 | 97.4k | int64_t imm = MCOperand_getImm(Op); |
207 | 97.4k | if (MI->csh->doing_mem) { |
208 | 38.3k | if (imm) { // only print Imm offset if it is not 0 |
209 | 37.1k | printInt64(O, imm); |
210 | 37.1k | } |
211 | 38.3k | if (MI->csh->detail) |
212 | 38.3k | MI->flat_insn->detail->mips.operands[MI->flat_insn->detail->mips.op_count].mem.disp = imm; |
213 | 59.1k | } else { |
214 | 59.1k | printInt64(O, imm); |
215 | | |
216 | 59.1k | if (MI->csh->detail) { |
217 | 59.1k | MI->flat_insn->detail->mips.operands[MI->flat_insn->detail->mips.op_count].type = MIPS_OP_IMM; |
218 | 59.1k | MI->flat_insn->detail->mips.operands[MI->flat_insn->detail->mips.op_count].imm = imm; |
219 | 59.1k | MI->flat_insn->detail->mips.op_count++; |
220 | 59.1k | } |
221 | 59.1k | } |
222 | 97.4k | } |
223 | 339k | } |
224 | | |
225 | | static void printUnsignedImm(MCInst *MI, int opNum, SStream *O) |
226 | 21.4k | { |
227 | 21.4k | MCOperand *MO = MCInst_getOperand(MI, opNum); |
228 | 21.4k | if (MCOperand_isImm(MO)) { |
229 | 21.4k | int64_t imm = MCOperand_getImm(MO); |
230 | 21.4k | printInt64(O, imm); |
231 | | |
232 | 21.4k | if (MI->csh->detail) { |
233 | 21.4k | MI->flat_insn->detail->mips.operands[MI->flat_insn->detail->mips.op_count].type = MIPS_OP_IMM; |
234 | 21.4k | MI->flat_insn->detail->mips.operands[MI->flat_insn->detail->mips.op_count].imm = (unsigned short int)imm; |
235 | 21.4k | MI->flat_insn->detail->mips.op_count++; |
236 | 21.4k | } |
237 | 21.4k | } else |
238 | 0 | printOperand(MI, opNum, O); |
239 | 21.4k | } |
240 | | |
241 | | static void printUnsignedImm8(MCInst *MI, int opNum, SStream *O) |
242 | 3.85k | { |
243 | 3.85k | MCOperand *MO = MCInst_getOperand(MI, opNum); |
244 | 3.85k | if (MCOperand_isImm(MO)) { |
245 | 3.85k | uint8_t imm = (uint8_t)MCOperand_getImm(MO); |
246 | 3.85k | if (imm > HEX_THRESHOLD) |
247 | 928 | SStream_concat(O, "0x%x", imm); |
248 | 2.92k | else |
249 | 2.92k | SStream_concat(O, "%u", imm); |
250 | 3.85k | if (MI->csh->detail) { |
251 | 3.85k | MI->flat_insn->detail->mips.operands[MI->flat_insn->detail->mips.op_count].type = MIPS_OP_IMM; |
252 | 3.85k | MI->flat_insn->detail->mips.operands[MI->flat_insn->detail->mips.op_count].imm = imm; |
253 | 3.85k | MI->flat_insn->detail->mips.op_count++; |
254 | 3.85k | } |
255 | 3.85k | } else |
256 | 0 | printOperand(MI, opNum, O); |
257 | 3.85k | } |
258 | | |
259 | | static void printMemOperand(MCInst *MI, int opNum, SStream *O) |
260 | 38.3k | { |
261 | | // Load/Store memory operands -- imm($reg) |
262 | | // If PIC target the target is loaded as the |
263 | | // pattern lw $25,%call16($28) |
264 | | |
265 | | // opNum can be invalid if instruction had reglist as operand. |
266 | | // MemOperand is always last operand of instruction (base + offset). |
267 | 38.3k | switch (MCInst_getOpcode(MI)) { |
268 | 36.5k | default: |
269 | 36.5k | break; |
270 | 36.5k | case Mips_SWM32_MM: |
271 | 500 | case Mips_LWM32_MM: |
272 | 1.23k | case Mips_SWM16_MM: |
273 | 1.78k | case Mips_LWM16_MM: |
274 | 1.78k | opNum = MCInst_getNumOperands(MI) - 2; |
275 | 1.78k | break; |
276 | 38.3k | } |
277 | | |
278 | 38.3k | set_mem_access(MI, true); |
279 | 38.3k | printOperand(MI, opNum + 1, O); |
280 | 38.3k | SStream_concat0(O, "("); |
281 | 38.3k | printOperand(MI, opNum, O); |
282 | 38.3k | SStream_concat0(O, ")"); |
283 | 38.3k | set_mem_access(MI, false); |
284 | 38.3k | } |
285 | | |
286 | | // TODO??? |
287 | | static void printMemOperandEA(MCInst *MI, int opNum, SStream *O) |
288 | 0 | { |
289 | | // when using stack locations for not load/store instructions |
290 | | // print the same way as all normal 3 operand instructions. |
291 | 0 | printOperand(MI, opNum, O); |
292 | 0 | SStream_concat0(O, ", "); |
293 | 0 | printOperand(MI, opNum + 1, O); |
294 | 0 | return; |
295 | 0 | } |
296 | | |
297 | | static void printFCCOperand(MCInst *MI, int opNum, SStream *O) |
298 | 0 | { |
299 | 0 | MCOperand *MO = MCInst_getOperand(MI, opNum); |
300 | 0 | SStream_concat0(O, MipsFCCToString((Mips_CondCode)MCOperand_getImm(MO))); |
301 | 0 | } |
302 | | |
303 | | static void printRegisterPair(MCInst *MI, int opNum, SStream *O) |
304 | 499 | { |
305 | 499 | printRegName(O, MCOperand_getReg(MCInst_getOperand(MI, opNum))); |
306 | 499 | } |
307 | | |
308 | | static char *printAlias1(const char *Str, MCInst *MI, unsigned OpNo, SStream *OS) |
309 | 1.84k | { |
310 | 1.84k | SStream_concat(OS, "%s\t", Str); |
311 | 1.84k | printOperand(MI, OpNo, OS); |
312 | 1.84k | return cs_strdup(Str); |
313 | 1.84k | } |
314 | | |
315 | | static char *printAlias2(const char *Str, MCInst *MI, |
316 | | unsigned OpNo0, unsigned OpNo1, SStream *OS) |
317 | 972 | { |
318 | 972 | char *tmp; |
319 | | |
320 | 972 | tmp = printAlias1(Str, MI, OpNo0, OS); |
321 | 972 | SStream_concat0(OS, ", "); |
322 | 972 | printOperand(MI, OpNo1, OS); |
323 | | |
324 | 972 | return tmp; |
325 | 972 | } |
326 | | |
327 | | #define GET_REGINFO_ENUM |
328 | | #include "MipsGenRegisterInfo.inc" |
329 | | |
330 | | static char *printAlias(MCInst *MI, SStream *OS) |
331 | 138k | { |
332 | 138k | switch (MCInst_getOpcode(MI)) { |
333 | 3.25k | case Mips_BEQ: |
334 | 3.85k | case Mips_BEQ_MM: |
335 | | // beq $zero, $zero, $L2 => b $L2 |
336 | | // beq $r0, $zero, $L2 => beqz $r0, $L2 |
337 | 3.85k | if (isReg(MI, 0, Mips_ZERO) && isReg(MI, 1, Mips_ZERO)) |
338 | 706 | return printAlias1("b", MI, 2, OS); |
339 | 3.14k | if (isReg(MI, 1, Mips_ZERO)) |
340 | 591 | return printAlias2("beqz", MI, 0, 2, OS); |
341 | 2.55k | return NULL; |
342 | 0 | case Mips_BEQ64: |
343 | | // beq $r0, $zero, $L2 => beqz $r0, $L2 |
344 | 0 | if (isReg(MI, 1, Mips_ZERO_64)) |
345 | 0 | return printAlias2("beqz", MI, 0, 2, OS); |
346 | 0 | return NULL; |
347 | 1.37k | case Mips_BNE: |
348 | | // bne $r0, $zero, $L2 => bnez $r0, $L2 |
349 | 1.37k | if (isReg(MI, 1, Mips_ZERO)) |
350 | 156 | return printAlias2("bnez", MI, 0, 2, OS); |
351 | 1.22k | return NULL; |
352 | 0 | case Mips_BNE64: |
353 | | // bne $r0, $zero, $L2 => bnez $r0, $L2 |
354 | 0 | if (isReg(MI, 1, Mips_ZERO_64)) |
355 | 0 | return printAlias2("bnez", MI, 0, 2, OS); |
356 | 0 | return NULL; |
357 | 448 | case Mips_BGEZAL: |
358 | | // bgezal $zero, $L1 => bal $L1 |
359 | 448 | if (isReg(MI, 0, Mips_ZERO)) |
360 | 121 | return printAlias1("bal", MI, 1, OS); |
361 | 327 | return NULL; |
362 | 297 | case Mips_BC1T: |
363 | | // bc1t $fcc0, $L1 => bc1t $L1 |
364 | 297 | if (isReg(MI, 0, Mips_FCC0)) |
365 | 0 | return printAlias1("bc1t", MI, 1, OS); |
366 | 297 | return NULL; |
367 | 244 | case Mips_BC1F: |
368 | | // bc1f $fcc0, $L1 => bc1f $L1 |
369 | 244 | if (isReg(MI, 0, Mips_FCC0)) |
370 | 0 | return printAlias1("bc1f", MI, 1, OS); |
371 | 244 | return NULL; |
372 | 130 | case Mips_JALR: |
373 | | // jalr $ra, $r1 => jalr $r1 |
374 | 130 | if (isReg(MI, 0, Mips_RA)) |
375 | 46 | return printAlias1("jalr", MI, 1, OS); |
376 | 84 | return NULL; |
377 | 0 | case Mips_JALR64: |
378 | | // jalr $ra, $r1 => jalr $r1 |
379 | 0 | if (isReg(MI, 0, Mips_RA_64)) |
380 | 0 | return printAlias1("jalr", MI, 1, OS); |
381 | 0 | return NULL; |
382 | 48 | case Mips_NOR: |
383 | 310 | case Mips_NOR_MM: |
384 | | // nor $r0, $r1, $zero => not $r0, $r1 |
385 | 310 | if (isReg(MI, 2, Mips_ZERO)) |
386 | 166 | return printAlias2("not", MI, 0, 1, OS); |
387 | 144 | return NULL; |
388 | 0 | case Mips_NOR64: |
389 | | // nor $r0, $r1, $zero => not $r0, $r1 |
390 | 0 | if (isReg(MI, 2, Mips_ZERO_64)) |
391 | 0 | return printAlias2("not", MI, 0, 1, OS); |
392 | 0 | return NULL; |
393 | 438 | case Mips_OR: |
394 | | // or $r0, $r1, $zero => move $r0, $r1 |
395 | 438 | if (isReg(MI, 2, Mips_ZERO)) |
396 | 59 | return printAlias2("move", MI, 0, 1, OS); |
397 | 379 | return NULL; |
398 | 131k | default: return NULL; |
399 | 138k | } |
400 | 138k | } |
401 | | |
402 | | static void printRegisterList(MCInst *MI, int opNum, SStream *O) |
403 | 2.31k | { |
404 | 2.31k | int i, e, reg; |
405 | | |
406 | | // - 2 because register List is always first operand of instruction and it is |
407 | | // always followed by memory operand (base + offset). |
408 | 8.65k | for (i = opNum, e = MCInst_getNumOperands(MI) - 2; i != e; ++i) { |
409 | 6.33k | if (i != opNum) |
410 | 4.01k | SStream_concat0(O, ", "); |
411 | 6.33k | reg = MCOperand_getReg(MCInst_getOperand(MI, i)); |
412 | 6.33k | printRegName(O, reg); |
413 | 6.33k | if (MI->csh->detail) { |
414 | 6.33k | MI->flat_insn->detail->mips.operands[MI->flat_insn->detail->mips.op_count].type = MIPS_OP_REG; |
415 | 6.33k | MI->flat_insn->detail->mips.operands[MI->flat_insn->detail->mips.op_count].reg = reg; |
416 | 6.33k | MI->flat_insn->detail->mips.op_count++; |
417 | 6.33k | } |
418 | 6.33k | } |
419 | 2.31k | } |
420 | | |
421 | | #define PRINT_ALIAS_INSTR |
422 | | #include "MipsGenAsmWriter.inc" |
423 | | |
424 | | #endif |