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
set_mem_access(MCInst * MI,bool status)88 static void set_mem_access(MCInst *MI, bool status)
89 {
90 MI->csh->doing_mem = status;
91
92 if (MI->csh->detail != CS_OPT_ON)
93 return;
94
95 if (status) {
96 MI->flat_insn->detail->mips.operands[MI->flat_insn->detail->mips.op_count].type = MIPS_OP_MEM;
97 MI->flat_insn->detail->mips.operands[MI->flat_insn->detail->mips.op_count].mem.base = MIPS_REG_INVALID;
98 MI->flat_insn->detail->mips.operands[MI->flat_insn->detail->mips.op_count].mem.disp = 0;
99 } else {
100 // done, create the next operand slot
101 MI->flat_insn->detail->mips.op_count++;
102 }
103 }
104
isReg(MCInst * MI,unsigned OpNo,unsigned R)105 static bool isReg(MCInst *MI, unsigned OpNo, unsigned R)
106 {
107 return (MCOperand_isReg(MCInst_getOperand(MI, OpNo)) &&
108 MCOperand_getReg(MCInst_getOperand(MI, OpNo)) == R);
109 }
110
MipsFCCToString(Mips_CondCode CC)111 static const char* MipsFCCToString(Mips_CondCode CC)
112 {
113 switch (CC) {
114 default: return 0; // never reach
115 case Mips_FCOND_F:
116 case Mips_FCOND_T: return "f";
117 case Mips_FCOND_UN:
118 case Mips_FCOND_OR: return "un";
119 case Mips_FCOND_OEQ:
120 case Mips_FCOND_UNE: return "eq";
121 case Mips_FCOND_UEQ:
122 case Mips_FCOND_ONE: return "ueq";
123 case Mips_FCOND_OLT:
124 case Mips_FCOND_UGE: return "olt";
125 case Mips_FCOND_ULT:
126 case Mips_FCOND_OGE: return "ult";
127 case Mips_FCOND_OLE:
128 case Mips_FCOND_UGT: return "ole";
129 case Mips_FCOND_ULE:
130 case Mips_FCOND_OGT: return "ule";
131 case Mips_FCOND_SF:
132 case Mips_FCOND_ST: return "sf";
133 case Mips_FCOND_NGLE:
134 case Mips_FCOND_GLE: return "ngle";
135 case Mips_FCOND_SEQ:
136 case Mips_FCOND_SNE: return "seq";
137 case Mips_FCOND_NGL:
138 case Mips_FCOND_GL: return "ngl";
139 case Mips_FCOND_LT:
140 case Mips_FCOND_NLT: return "lt";
141 case Mips_FCOND_NGE:
142 case Mips_FCOND_GE: return "nge";
143 case Mips_FCOND_LE:
144 case Mips_FCOND_NLE: return "le";
145 case Mips_FCOND_NGT:
146 case Mips_FCOND_GT: return "ngt";
147 }
148 }
149
printRegName(SStream * OS,unsigned RegNo)150 static void printRegName(SStream *OS, unsigned RegNo)
151 {
152 SStream_concat(OS, "$%s", getRegisterName(RegNo));
153 }
154
Mips_printInst(MCInst * MI,SStream * O,void * info)155 void Mips_printInst(MCInst *MI, SStream *O, void *info)
156 {
157 char *mnem;
158
159 switch (MCInst_getOpcode(MI)) {
160 default: break;
161 case Mips_Save16:
162 case Mips_SaveX16:
163 case Mips_Restore16:
164 case Mips_RestoreX16:
165 return;
166 }
167
168 // Try to print any aliases first.
169 mnem = printAliasInstr(MI, O, info);
170 if (!mnem) {
171 mnem = printAlias(MI, O);
172 if (!mnem) {
173 printInstruction(MI, O, NULL);
174 }
175 }
176
177 if (mnem) {
178 // fixup instruction id due to the change in alias instruction
179 MCInst_setOpcodePub(MI, Mips_map_insn(mnem));
180 cs_mem_free(mnem);
181 }
182 }
183
printOperand(MCInst * MI,unsigned OpNo,SStream * O)184 static void printOperand(MCInst *MI, unsigned OpNo, SStream *O)
185 {
186 MCOperand *Op;
187
188 if (OpNo >= MI->size)
189 return;
190
191 Op = MCInst_getOperand(MI, OpNo);
192 if (MCOperand_isReg(Op)) {
193 unsigned int reg = MCOperand_getReg(Op);
194 printRegName(O, reg);
195 reg = Mips_map_register(reg);
196 if (MI->csh->detail) {
197 if (MI->csh->doing_mem) {
198 MI->flat_insn->detail->mips.operands[MI->flat_insn->detail->mips.op_count].mem.base = reg;
199 } else {
200 MI->flat_insn->detail->mips.operands[MI->flat_insn->detail->mips.op_count].type = MIPS_OP_REG;
201 MI->flat_insn->detail->mips.operands[MI->flat_insn->detail->mips.op_count].reg = reg;
202 MI->flat_insn->detail->mips.op_count++;
203 }
204 }
205 } else if (MCOperand_isImm(Op)) {
206 int64_t imm = MCOperand_getImm(Op);
207 if (MI->csh->doing_mem) {
208 if (imm) { // only print Imm offset if it is not 0
209 printInt64(O, imm);
210 }
211 if (MI->csh->detail)
212 MI->flat_insn->detail->mips.operands[MI->flat_insn->detail->mips.op_count].mem.disp = imm;
213 } else {
214 printInt64(O, imm);
215
216 if (MI->csh->detail) {
217 MI->flat_insn->detail->mips.operands[MI->flat_insn->detail->mips.op_count].type = MIPS_OP_IMM;
218 MI->flat_insn->detail->mips.operands[MI->flat_insn->detail->mips.op_count].imm = imm;
219 MI->flat_insn->detail->mips.op_count++;
220 }
221 }
222 }
223 }
224
printUnsignedImm(MCInst * MI,int opNum,SStream * O)225 static void printUnsignedImm(MCInst *MI, int opNum, SStream *O)
226 {
227 MCOperand *MO = MCInst_getOperand(MI, opNum);
228 if (MCOperand_isImm(MO)) {
229 int64_t imm = MCOperand_getImm(MO);
230 printInt64(O, imm);
231
232 if (MI->csh->detail) {
233 MI->flat_insn->detail->mips.operands[MI->flat_insn->detail->mips.op_count].type = MIPS_OP_IMM;
234 MI->flat_insn->detail->mips.operands[MI->flat_insn->detail->mips.op_count].imm = (unsigned short int)imm;
235 MI->flat_insn->detail->mips.op_count++;
236 }
237 } else
238 printOperand(MI, opNum, O);
239 }
240
printUnsignedImm8(MCInst * MI,int opNum,SStream * O)241 static void printUnsignedImm8(MCInst *MI, int opNum, SStream *O)
242 {
243 MCOperand *MO = MCInst_getOperand(MI, opNum);
244 if (MCOperand_isImm(MO)) {
245 uint8_t imm = (uint8_t)MCOperand_getImm(MO);
246 if (imm > HEX_THRESHOLD)
247 SStream_concat(O, "0x%x", imm);
248 else
249 SStream_concat(O, "%u", imm);
250 if (MI->csh->detail) {
251 MI->flat_insn->detail->mips.operands[MI->flat_insn->detail->mips.op_count].type = MIPS_OP_IMM;
252 MI->flat_insn->detail->mips.operands[MI->flat_insn->detail->mips.op_count].imm = imm;
253 MI->flat_insn->detail->mips.op_count++;
254 }
255 } else
256 printOperand(MI, opNum, O);
257 }
258
printMemOperand(MCInst * MI,int opNum,SStream * O)259 static void printMemOperand(MCInst *MI, int opNum, SStream *O)
260 {
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 switch (MCInst_getOpcode(MI)) {
268 default:
269 break;
270 case Mips_SWM32_MM:
271 case Mips_LWM32_MM:
272 case Mips_SWM16_MM:
273 case Mips_LWM16_MM:
274 opNum = MCInst_getNumOperands(MI) - 2;
275 break;
276 }
277
278 set_mem_access(MI, true);
279 printOperand(MI, opNum + 1, O);
280 SStream_concat0(O, "(");
281 printOperand(MI, opNum, O);
282 SStream_concat0(O, ")");
283 set_mem_access(MI, false);
284 }
285
286 // TODO???
printMemOperandEA(MCInst * MI,int opNum,SStream * O)287 static void printMemOperandEA(MCInst *MI, int opNum, SStream *O)
288 {
289 // when using stack locations for not load/store instructions
290 // print the same way as all normal 3 operand instructions.
291 printOperand(MI, opNum, O);
292 SStream_concat0(O, ", ");
293 printOperand(MI, opNum + 1, O);
294 return;
295 }
296
printFCCOperand(MCInst * MI,int opNum,SStream * O)297 static void printFCCOperand(MCInst *MI, int opNum, SStream *O)
298 {
299 MCOperand *MO = MCInst_getOperand(MI, opNum);
300 SStream_concat0(O, MipsFCCToString((Mips_CondCode)MCOperand_getImm(MO)));
301 }
302
printRegisterPair(MCInst * MI,int opNum,SStream * O)303 static void printRegisterPair(MCInst *MI, int opNum, SStream *O)
304 {
305 printRegName(O, MCOperand_getReg(MCInst_getOperand(MI, opNum)));
306 }
307
printAlias1(const char * Str,MCInst * MI,unsigned OpNo,SStream * OS)308 static char *printAlias1(const char *Str, MCInst *MI, unsigned OpNo, SStream *OS)
309 {
310 SStream_concat(OS, "%s\t", Str);
311 printOperand(MI, OpNo, OS);
312 return cs_strdup(Str);
313 }
314
printAlias2(const char * Str,MCInst * MI,unsigned OpNo0,unsigned OpNo1,SStream * OS)315 static char *printAlias2(const char *Str, MCInst *MI,
316 unsigned OpNo0, unsigned OpNo1, SStream *OS)
317 {
318 char *tmp;
319
320 tmp = printAlias1(Str, MI, OpNo0, OS);
321 SStream_concat0(OS, ", ");
322 printOperand(MI, OpNo1, OS);
323
324 return tmp;
325 }
326
327 #define GET_REGINFO_ENUM
328 #include "MipsGenRegisterInfo.inc"
329
printAlias(MCInst * MI,SStream * OS)330 static char *printAlias(MCInst *MI, SStream *OS)
331 {
332 switch (MCInst_getOpcode(MI)) {
333 case Mips_BEQ:
334 case Mips_BEQ_MM:
335 // beq $zero, $zero, $L2 => b $L2
336 // beq $r0, $zero, $L2 => beqz $r0, $L2
337 if (isReg(MI, 0, Mips_ZERO) && isReg(MI, 1, Mips_ZERO))
338 return printAlias1("b", MI, 2, OS);
339 if (isReg(MI, 1, Mips_ZERO))
340 return printAlias2("beqz", MI, 0, 2, OS);
341 return NULL;
342 case Mips_BEQ64:
343 // beq $r0, $zero, $L2 => beqz $r0, $L2
344 if (isReg(MI, 1, Mips_ZERO_64))
345 return printAlias2("beqz", MI, 0, 2, OS);
346 return NULL;
347 case Mips_BNE:
348 // bne $r0, $zero, $L2 => bnez $r0, $L2
349 if (isReg(MI, 1, Mips_ZERO))
350 return printAlias2("bnez", MI, 0, 2, OS);
351 return NULL;
352 case Mips_BNE64:
353 // bne $r0, $zero, $L2 => bnez $r0, $L2
354 if (isReg(MI, 1, Mips_ZERO_64))
355 return printAlias2("bnez", MI, 0, 2, OS);
356 return NULL;
357 case Mips_BGEZAL:
358 // bgezal $zero, $L1 => bal $L1
359 if (isReg(MI, 0, Mips_ZERO))
360 return printAlias1("bal", MI, 1, OS);
361 return NULL;
362 case Mips_BC1T:
363 // bc1t $fcc0, $L1 => bc1t $L1
364 if (isReg(MI, 0, Mips_FCC0))
365 return printAlias1("bc1t", MI, 1, OS);
366 return NULL;
367 case Mips_BC1F:
368 // bc1f $fcc0, $L1 => bc1f $L1
369 if (isReg(MI, 0, Mips_FCC0))
370 return printAlias1("bc1f", MI, 1, OS);
371 return NULL;
372 case Mips_JALR:
373 // jalr $ra, $r1 => jalr $r1
374 if (isReg(MI, 0, Mips_RA))
375 return printAlias1("jalr", MI, 1, OS);
376 return NULL;
377 case Mips_JALR64:
378 // jalr $ra, $r1 => jalr $r1
379 if (isReg(MI, 0, Mips_RA_64))
380 return printAlias1("jalr", MI, 1, OS);
381 return NULL;
382 case Mips_NOR:
383 case Mips_NOR_MM:
384 // nor $r0, $r1, $zero => not $r0, $r1
385 if (isReg(MI, 2, Mips_ZERO))
386 return printAlias2("not", MI, 0, 1, OS);
387 return NULL;
388 case Mips_NOR64:
389 // nor $r0, $r1, $zero => not $r0, $r1
390 if (isReg(MI, 2, Mips_ZERO_64))
391 return printAlias2("not", MI, 0, 1, OS);
392 return NULL;
393 case Mips_OR:
394 // or $r0, $r1, $zero => move $r0, $r1
395 if (isReg(MI, 2, Mips_ZERO))
396 return printAlias2("move", MI, 0, 1, OS);
397 return NULL;
398 default: return NULL;
399 }
400 }
401
printRegisterList(MCInst * MI,int opNum,SStream * O)402 static void printRegisterList(MCInst *MI, int opNum, SStream *O)
403 {
404 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 for (i = opNum, e = MCInst_getNumOperands(MI) - 2; i != e; ++i) {
409 if (i != opNum)
410 SStream_concat0(O, ", ");
411 reg = MCOperand_getReg(MCInst_getOperand(MI, i));
412 printRegName(O, reg);
413 if (MI->csh->detail) {
414 MI->flat_insn->detail->mips.operands[MI->flat_insn->detail->mips.op_count].type = MIPS_OP_REG;
415 MI->flat_insn->detail->mips.operands[MI->flat_insn->detail->mips.op_count].reg = reg;
416 MI->flat_insn->detail->mips.op_count++;
417 }
418 }
419 }
420
421 #define PRINT_ALIAS_INSTR
422 #include "MipsGenAsmWriter.inc"
423
424 #endif
425