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 33static void printUnsignedImm(MCInst *MI, int opNum, SStream *O); 34static char *printAliasInstr(MCInst *MI, SStream *O, void *info); 35static 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 42typedef 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 85static char *getRegisterName(unsigned RegNo); 86static void printInstruction(MCInst *MI, SStream *O, MCRegisterInfo *MRI); 87 88static 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 105static 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 111static 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 150static void printRegName(SStream *OS, unsigned RegNo) 151{ 152 SStream_concat(OS, "$%s", getRegisterName(RegNo)); 153} 154 155void 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 184static 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 245static 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 270static 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 288static 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??? 302static 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 312static 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 318static 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 325static 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 340static 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