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-2014 */
16 
17 #ifdef CAPSTONE_HAS_MIPS
18 
19 #include <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 char *getRegisterName(unsigned RegNo);
86 static void printInstruction(MCInst *MI, SStream *O, 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 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 				if (imm >= 0) {
210 					if (imm > HEX_THRESHOLD)
211 						SStream_concat(O, "0x%"PRIx64, imm);
212 					else
213 						SStream_concat(O, "%"PRIu64, imm);
214 				} else {
215 					if (imm < -HEX_THRESHOLD)
216 						SStream_concat(O, "-0x%"PRIx64, -imm);
217 					else
218 						SStream_concat(O, "-%"PRIu64, -imm);
219 				}
220 			}
221 			if (MI->csh->detail)
222 				MI->flat_insn->detail->mips.operands[MI->flat_insn->detail->mips.op_count].mem.disp = imm;
223 		} else {
224 			if (imm >= 0) {
225 				if (imm > HEX_THRESHOLD)
226 					SStream_concat(O, "0x%"PRIx64, imm);
227 				else
228 					SStream_concat(O, "%"PRIu64, imm);
229 			} else {
230 				if (imm < -HEX_THRESHOLD)
231 					SStream_concat(O, "-0x%"PRIx64, -imm);
232 				else
233 					SStream_concat(O, "-%"PRIu64, -imm);
234 			}
235 
236 			if (MI->csh->detail) {
237 				MI->flat_insn->detail->mips.operands[MI->flat_insn->detail->mips.op_count].type = MIPS_OP_IMM;
238 				MI->flat_insn->detail->mips.operands[MI->flat_insn->detail->mips.op_count].imm = imm;
239 				MI->flat_insn->detail->mips.op_count++;
240 			}
241 		}
242 	}
243 }
244 
printUnsignedImm(MCInst * MI,int opNum,SStream * O)245 static void printUnsignedImm(MCInst *MI, int opNum, SStream *O)
246 {
247 	MCOperand *MO = MCInst_getOperand(MI, opNum);
248 	if (MCOperand_isImm(MO)) {
249 		int64_t imm = MCOperand_getImm(MO);
250 		if (imm >= 0) {
251 			if (imm > HEX_THRESHOLD)
252 				SStream_concat(O, "0x%x", (unsigned short int)imm);
253 			else
254 				SStream_concat(O, "%u", (unsigned short int)imm);
255 		} else {
256 			if (imm < -HEX_THRESHOLD)
257 				SStream_concat(O, "-0x%x", (short int)-imm);
258 			else
259 				SStream_concat(O, "-%u", (short int)-imm);
260 		}
261 		if (MI->csh->detail) {
262 			MI->flat_insn->detail->mips.operands[MI->flat_insn->detail->mips.op_count].type = MIPS_OP_IMM;
263 			MI->flat_insn->detail->mips.operands[MI->flat_insn->detail->mips.op_count].imm = (unsigned short int)imm;
264 			MI->flat_insn->detail->mips.op_count++;
265 		}
266 	} else
267 		printOperand(MI, opNum, O);
268 }
269 
printUnsignedImm8(MCInst * MI,int opNum,SStream * O)270 static void printUnsignedImm8(MCInst *MI, int opNum, SStream *O)
271 {
272 	MCOperand *MO = MCInst_getOperand(MI, opNum);
273 	if (MCOperand_isImm(MO)) {
274 		uint8_t imm = (uint8_t)MCOperand_getImm(MO);
275 		if (imm > HEX_THRESHOLD)
276 			SStream_concat(O, "0x%x", imm);
277 		else
278 			SStream_concat(O, "%u", imm);
279 		if (MI->csh->detail) {
280 			MI->flat_insn->detail->mips.operands[MI->flat_insn->detail->mips.op_count].type = MIPS_OP_IMM;
281 			MI->flat_insn->detail->mips.operands[MI->flat_insn->detail->mips.op_count].imm = imm;
282 			MI->flat_insn->detail->mips.op_count++;
283 		}
284 	} else
285 		printOperand(MI, opNum, O);
286 }
287 
printMemOperand(MCInst * MI,int opNum,SStream * O)288 static void printMemOperand(MCInst *MI, int opNum, SStream *O)
289 {
290 	// Load/Store memory operands -- imm($reg)
291 	// If PIC target the target is loaded as the
292 	// pattern lw $25,%call16($28)
293 	set_mem_access(MI, true);
294 	printOperand(MI, opNum + 1, O);
295 	SStream_concat0(O, "(");
296 	printOperand(MI, opNum, O);
297 	SStream_concat0(O, ")");
298 	set_mem_access(MI, false);
299 }
300 
301 // TODO???
printMemOperandEA(MCInst * MI,int opNum,SStream * O)302 static void printMemOperandEA(MCInst *MI, int opNum, SStream *O)
303 {
304 	// when using stack locations for not load/store instructions
305 	// print the same way as all normal 3 operand instructions.
306 	printOperand(MI, opNum, O);
307 	SStream_concat0(O, ", ");
308 	printOperand(MI, opNum + 1, O);
309 	return;
310 }
311 
printFCCOperand(MCInst * MI,int opNum,SStream * O)312 static void printFCCOperand(MCInst *MI, int opNum, SStream *O)
313 {
314 	MCOperand *MO = MCInst_getOperand(MI, opNum);
315 	SStream_concat0(O, MipsFCCToString((Mips_CondCode)MCOperand_getImm(MO)));
316 }
317 
printAlias1(char * Str,MCInst * MI,unsigned OpNo,SStream * OS)318 static char *printAlias1(char *Str, MCInst *MI, unsigned OpNo, SStream *OS)
319 {
320 	SStream_concat(OS, "%s\t", Str);
321 	printOperand(MI, OpNo, OS);
322 	return cs_strdup(Str);
323 }
324 
printAlias2(char * Str,MCInst * MI,unsigned OpNo0,unsigned OpNo1,SStream * OS)325 static char *printAlias2(char *Str, MCInst *MI,
326 		unsigned OpNo0, unsigned OpNo1, SStream *OS)
327 {
328 	char *tmp;
329 
330 	tmp = printAlias1(Str, MI, OpNo0, OS);
331 	SStream_concat0(OS, ", ");
332 	printOperand(MI, OpNo1, OS);
333 
334 	return tmp;
335 }
336 
337 #define GET_REGINFO_ENUM
338 #include "MipsGenRegisterInfo.inc"
339 
printAlias(MCInst * MI,SStream * OS)340 static char *printAlias(MCInst *MI, SStream *OS)
341 {
342 	switch (MCInst_getOpcode(MI)) {
343 		case Mips_BEQ:
344 			// beq $zero, $zero, $L2 => b $L2
345 			// beq $r0, $zero, $L2 => beqz $r0, $L2
346 			if (isReg(MI, 0, Mips_ZERO) && isReg(MI, 1, Mips_ZERO))
347 				return printAlias1("b", MI, 2, OS);
348 			if (isReg(MI, 1, Mips_ZERO))
349 				return printAlias2("beqz", MI, 0, 2, OS);
350 			return NULL;
351 		case Mips_BEQL:
352 			// beql $r0, $zero, $L2 => beqzl $r0, $L2
353 			if (isReg(MI, 0, Mips_ZERO) && isReg(MI, 1, Mips_ZERO))
354 				return printAlias2("beqzl", MI, 0, 2, OS);
355 			return NULL;
356 		case Mips_BEQ64:
357 			// beq $r0, $zero, $L2 => beqz $r0, $L2
358 			if (isReg(MI, 1, Mips_ZERO_64))
359 				return printAlias2("beqz", MI, 0, 2, OS);
360 			return NULL;
361 		case Mips_BNE:
362 			// bne $r0, $zero, $L2 => bnez $r0, $L2
363 			if (isReg(MI, 1, Mips_ZERO))
364 				return printAlias2("bnez", MI, 0, 2, OS);
365 			return NULL;
366 		case Mips_BNEL:
367 			// bnel $r0, $zero, $L2 => bnezl $r0, $L2
368 			if (isReg(MI, 1, Mips_ZERO))
369 				return printAlias2("bnezl", MI, 0, 2, OS);
370 			return NULL;
371 		case Mips_BNE64:
372 			// bne $r0, $zero, $L2 => bnez $r0, $L2
373 			if (isReg(MI, 1, Mips_ZERO_64))
374 				return printAlias2("bnez", MI, 0, 2, OS);
375 			return NULL;
376 		case Mips_BGEZAL:
377 			// bgezal $zero, $L1 => bal $L1
378 			if (isReg(MI, 0, Mips_ZERO))
379 				return printAlias1("bal", MI, 1, OS);
380 			return NULL;
381 		case Mips_BC1T:
382 			// bc1t $fcc0, $L1 => bc1t $L1
383 			if (isReg(MI, 0, Mips_FCC0))
384 				return printAlias1("bc1t", MI, 1, OS);
385 			return NULL;
386 		case Mips_BC1F:
387 			// bc1f $fcc0, $L1 => bc1f $L1
388 			if (isReg(MI, 0, Mips_FCC0))
389 				return printAlias1("bc1f", MI, 1, OS);
390 			return NULL;
391 		case Mips_JALR:
392 			// jalr $ra, $r1 => jalr $r1
393 			if (isReg(MI, 0, Mips_RA))
394 				return printAlias1("jalr", MI, 1, OS);
395 			return NULL;
396 		case Mips_JALR64:
397 			// jalr $ra, $r1 => jalr $r1
398 			if (isReg(MI, 0, Mips_RA_64))
399 				return printAlias1("jalr", MI, 1, OS);
400 			return NULL;
401 		case Mips_NOR:
402 		case Mips_NOR_MM:
403 			// nor $r0, $r1, $zero => not $r0, $r1
404 			if (isReg(MI, 2, Mips_ZERO))
405 				return printAlias2("not", MI, 0, 1, OS);
406 			return NULL;
407 		case Mips_NOR64:
408 			// nor $r0, $r1, $zero => not $r0, $r1
409 			if (isReg(MI, 2, Mips_ZERO_64))
410 				return printAlias2("not", MI, 0, 1, OS);
411 			return NULL;
412 		case Mips_OR:
413 			// or $r0, $r1, $zero => move $r0, $r1
414 			if (isReg(MI, 2, Mips_ZERO))
415 				return printAlias2("move", MI, 0, 1, OS);
416 			return NULL;
417 		default: return NULL;
418 	}
419 }
420 
421 #define PRINT_ALIAS_INSTR
422 #include "MipsGenAsmWriter.inc"
423 
424 #endif
425