| //===-- MipsInstPrinter.cpp - Convert Mips MCInst to assembly syntax ------===// |
| // |
| // The LLVM Compiler Infrastructure |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // This class prints an Mips MCInst to a .s file. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| /* Capstone Disassembly Engine */ |
| /* By Nguyen Anh Quynh <aquynh@gmail.com>, 2013-2014 */ |
| |
| #ifdef CAPSTONE_HAS_MIPS |
| |
| #include "../../myinttypes.h" |
| #include <stdlib.h> |
| #include <stdio.h> // debug |
| #include <string.h> |
| |
| #include "MipsInstPrinter.h" |
| #include "../../MCInst.h" |
| #include "../../utils.h" |
| #include "../../SStream.h" |
| #include "../../MCRegisterInfo.h" |
| #include "MipsMapping.h" |
| |
| #include "MipsInstPrinter.h" |
| |
| static void printUnsignedImm(MCInst *MI, int opNum, SStream *O); |
| static char *printAliasInstr(MCInst *MI, SStream *O, void *info); |
| static char *printAlias(MCInst *MI, SStream *OS); |
| |
| // These enumeration declarations were originally in MipsInstrInfo.h but |
| // had to be moved here to avoid circular dependencies between |
| // LLVMMipsCodeGen and LLVMMipsAsmPrinter. |
| |
| // Mips Condition Codes |
| typedef enum Mips_CondCode { |
| // To be used with float branch True |
| Mips_FCOND_F, |
| Mips_FCOND_UN, |
| Mips_FCOND_OEQ, |
| Mips_FCOND_UEQ, |
| Mips_FCOND_OLT, |
| Mips_FCOND_ULT, |
| Mips_FCOND_OLE, |
| Mips_FCOND_ULE, |
| Mips_FCOND_SF, |
| Mips_FCOND_NGLE, |
| Mips_FCOND_SEQ, |
| Mips_FCOND_NGL, |
| Mips_FCOND_LT, |
| Mips_FCOND_NGE, |
| Mips_FCOND_LE, |
| Mips_FCOND_NGT, |
| |
| // To be used with float branch False |
| // This conditions have the same mnemonic as the |
| // above ones, but are used with a branch False; |
| Mips_FCOND_T, |
| Mips_FCOND_OR, |
| Mips_FCOND_UNE, |
| Mips_FCOND_ONE, |
| Mips_FCOND_UGE, |
| Mips_FCOND_OGE, |
| Mips_FCOND_UGT, |
| Mips_FCOND_OGT, |
| Mips_FCOND_ST, |
| Mips_FCOND_GLE, |
| Mips_FCOND_SNE, |
| Mips_FCOND_GL, |
| Mips_FCOND_NLT, |
| Mips_FCOND_GE, |
| Mips_FCOND_NLE, |
| Mips_FCOND_GT |
| } Mips_CondCode; |
| |
| #define GET_INSTRINFO_ENUM |
| #include "MipsGenInstrInfo.inc" |
| |
| static char *getRegisterName(unsigned RegNo); |
| static void printInstruction(MCInst *MI, SStream *O, MCRegisterInfo *MRI); |
| |
| static void set_mem_access(MCInst *MI, bool status) |
| { |
| MI->csh->doing_mem = status; |
| |
| if (MI->csh->detail != CS_OPT_ON) |
| return; |
| |
| if (status) { |
| MI->flat_insn->detail->mips.operands[MI->flat_insn->detail->mips.op_count].type = MIPS_OP_MEM; |
| MI->flat_insn->detail->mips.operands[MI->flat_insn->detail->mips.op_count].mem.base = MIPS_REG_INVALID; |
| MI->flat_insn->detail->mips.operands[MI->flat_insn->detail->mips.op_count].mem.disp = 0; |
| } else { |
| // done, create the next operand slot |
| MI->flat_insn->detail->mips.op_count++; |
| } |
| } |
| |
| static bool isReg(MCInst *MI, unsigned OpNo, unsigned R) |
| { |
| return (MCOperand_isReg(MCInst_getOperand(MI, OpNo)) && |
| MCOperand_getReg(MCInst_getOperand(MI, OpNo)) == R); |
| } |
| |
| static char* MipsFCCToString(Mips_CondCode CC) |
| { |
| switch (CC) { |
| default: return 0; // never reach |
| case Mips_FCOND_F: |
| case Mips_FCOND_T: return "f"; |
| case Mips_FCOND_UN: |
| case Mips_FCOND_OR: return "un"; |
| case Mips_FCOND_OEQ: |
| case Mips_FCOND_UNE: return "eq"; |
| case Mips_FCOND_UEQ: |
| case Mips_FCOND_ONE: return "ueq"; |
| case Mips_FCOND_OLT: |
| case Mips_FCOND_UGE: return "olt"; |
| case Mips_FCOND_ULT: |
| case Mips_FCOND_OGE: return "ult"; |
| case Mips_FCOND_OLE: |
| case Mips_FCOND_UGT: return "ole"; |
| case Mips_FCOND_ULE: |
| case Mips_FCOND_OGT: return "ule"; |
| case Mips_FCOND_SF: |
| case Mips_FCOND_ST: return "sf"; |
| case Mips_FCOND_NGLE: |
| case Mips_FCOND_GLE: return "ngle"; |
| case Mips_FCOND_SEQ: |
| case Mips_FCOND_SNE: return "seq"; |
| case Mips_FCOND_NGL: |
| case Mips_FCOND_GL: return "ngl"; |
| case Mips_FCOND_LT: |
| case Mips_FCOND_NLT: return "lt"; |
| case Mips_FCOND_NGE: |
| case Mips_FCOND_GE: return "nge"; |
| case Mips_FCOND_LE: |
| case Mips_FCOND_NLE: return "le"; |
| case Mips_FCOND_NGT: |
| case Mips_FCOND_GT: return "ngt"; |
| } |
| } |
| |
| static void printRegName(SStream *OS, unsigned RegNo) |
| { |
| SStream_concat(OS, "$%s", getRegisterName(RegNo)); |
| } |
| |
| void Mips_printInst(MCInst *MI, SStream *O, void *info) |
| { |
| char *mnem; |
| |
| switch (MCInst_getOpcode(MI)) { |
| default: break; |
| case Mips_Save16: |
| case Mips_SaveX16: |
| case Mips_Restore16: |
| case Mips_RestoreX16: |
| return; |
| } |
| |
| // Try to print any aliases first. |
| mnem = printAliasInstr(MI, O, info); |
| if (!mnem) { |
| mnem = printAlias(MI, O); |
| if (!mnem) { |
| printInstruction(MI, O, NULL); |
| } |
| } |
| |
| if (mnem) { |
| // fixup instruction id due to the change in alias instruction |
| MCInst_setOpcodePub(MI, Mips_map_insn(mnem)); |
| cs_mem_free(mnem); |
| } |
| } |
| |
| static void printOperand(MCInst *MI, unsigned OpNo, SStream *O) |
| { |
| MCOperand *Op; |
| |
| if (OpNo >= MI->size) |
| return; |
| |
| Op = MCInst_getOperand(MI, OpNo); |
| if (MCOperand_isReg(Op)) { |
| unsigned int reg = MCOperand_getReg(Op); |
| printRegName(O, reg); |
| reg = Mips_map_register(reg); |
| if (MI->csh->detail) { |
| if (MI->csh->doing_mem) { |
| MI->flat_insn->detail->mips.operands[MI->flat_insn->detail->mips.op_count].mem.base = reg; |
| } else { |
| MI->flat_insn->detail->mips.operands[MI->flat_insn->detail->mips.op_count].type = MIPS_OP_REG; |
| MI->flat_insn->detail->mips.operands[MI->flat_insn->detail->mips.op_count].reg = reg; |
| MI->flat_insn->detail->mips.op_count++; |
| } |
| } |
| } else if (MCOperand_isImm(Op)) { |
| int64_t imm = MCOperand_getImm(Op); |
| if (MI->csh->doing_mem) { |
| if (imm) { // only print Imm offset if it is not 0 |
| if (imm >= 0) { |
| if (imm > HEX_THRESHOLD) |
| SStream_concat(O, "0x%"PRIx64, imm); |
| else |
| SStream_concat(O, "%"PRIu64, imm); |
| } else { |
| if (imm < -HEX_THRESHOLD) |
| SStream_concat(O, "-0x%"PRIx64, -imm); |
| else |
| SStream_concat(O, "-%"PRIu64, -imm); |
| } |
| } |
| if (MI->csh->detail) |
| MI->flat_insn->detail->mips.operands[MI->flat_insn->detail->mips.op_count].mem.disp = imm; |
| } else { |
| if (imm >= 0) { |
| if (imm > HEX_THRESHOLD) |
| SStream_concat(O, "0x%"PRIx64, imm); |
| else |
| SStream_concat(O, "%"PRIu64, imm); |
| } else { |
| if (imm < -HEX_THRESHOLD) |
| SStream_concat(O, "-0x%"PRIx64, -imm); |
| else |
| SStream_concat(O, "-%"PRIu64, -imm); |
| } |
| |
| if (MI->csh->detail) { |
| MI->flat_insn->detail->mips.operands[MI->flat_insn->detail->mips.op_count].type = MIPS_OP_IMM; |
| MI->flat_insn->detail->mips.operands[MI->flat_insn->detail->mips.op_count].imm = imm; |
| MI->flat_insn->detail->mips.op_count++; |
| } |
| } |
| } |
| } |
| |
| static void printUnsignedImm(MCInst *MI, int opNum, SStream *O) |
| { |
| MCOperand *MO = MCInst_getOperand(MI, opNum); |
| if (MCOperand_isImm(MO)) { |
| int64_t imm = MCOperand_getImm(MO); |
| if (imm >= 0) { |
| if (imm > HEX_THRESHOLD) |
| SStream_concat(O, "0x%x", (unsigned short int)imm); |
| else |
| SStream_concat(O, "%u", (unsigned short int)imm); |
| } else { |
| if (imm < -HEX_THRESHOLD) |
| SStream_concat(O, "-0x%x", (short int)-imm); |
| else |
| SStream_concat(O, "-%u", (short int)-imm); |
| } |
| if (MI->csh->detail) { |
| MI->flat_insn->detail->mips.operands[MI->flat_insn->detail->mips.op_count].type = MIPS_OP_IMM; |
| MI->flat_insn->detail->mips.operands[MI->flat_insn->detail->mips.op_count].imm = (unsigned short int)imm; |
| MI->flat_insn->detail->mips.op_count++; |
| } |
| } else |
| printOperand(MI, opNum, O); |
| } |
| |
| static void printUnsignedImm8(MCInst *MI, int opNum, SStream *O) |
| { |
| MCOperand *MO = MCInst_getOperand(MI, opNum); |
| if (MCOperand_isImm(MO)) { |
| uint8_t imm = (uint8_t)MCOperand_getImm(MO); |
| if (imm > HEX_THRESHOLD) |
| SStream_concat(O, "0x%x", imm); |
| else |
| SStream_concat(O, "%u", imm); |
| if (MI->csh->detail) { |
| MI->flat_insn->detail->mips.operands[MI->flat_insn->detail->mips.op_count].type = MIPS_OP_IMM; |
| MI->flat_insn->detail->mips.operands[MI->flat_insn->detail->mips.op_count].imm = imm; |
| MI->flat_insn->detail->mips.op_count++; |
| } |
| } else |
| printOperand(MI, opNum, O); |
| } |
| |
| static void printMemOperand(MCInst *MI, int opNum, SStream *O) |
| { |
| // Load/Store memory operands -- imm($reg) |
| // If PIC target the target is loaded as the |
| // pattern lw $25,%call16($28) |
| set_mem_access(MI, true); |
| printOperand(MI, opNum + 1, O); |
| SStream_concat0(O, "("); |
| printOperand(MI, opNum, O); |
| SStream_concat0(O, ")"); |
| set_mem_access(MI, false); |
| } |
| |
| // TODO??? |
| static void printMemOperandEA(MCInst *MI, int opNum, SStream *O) |
| { |
| // when using stack locations for not load/store instructions |
| // print the same way as all normal 3 operand instructions. |
| printOperand(MI, opNum, O); |
| SStream_concat0(O, ", "); |
| printOperand(MI, opNum + 1, O); |
| return; |
| } |
| |
| static void printFCCOperand(MCInst *MI, int opNum, SStream *O) |
| { |
| MCOperand *MO = MCInst_getOperand(MI, opNum); |
| SStream_concat0(O, MipsFCCToString((Mips_CondCode)MCOperand_getImm(MO))); |
| } |
| |
| static char *printAlias1(char *Str, MCInst *MI, unsigned OpNo, SStream *OS) |
| { |
| SStream_concat(OS, "%s\t", Str); |
| printOperand(MI, OpNo, OS); |
| return cs_strdup(Str); |
| } |
| |
| static char *printAlias2(char *Str, MCInst *MI, |
| unsigned OpNo0, unsigned OpNo1, SStream *OS) |
| { |
| char *tmp; |
| |
| tmp = printAlias1(Str, MI, OpNo0, OS); |
| SStream_concat0(OS, ", "); |
| printOperand(MI, OpNo1, OS); |
| |
| return tmp; |
| } |
| |
| #define GET_REGINFO_ENUM |
| #include "MipsGenRegisterInfo.inc" |
| |
| static char *printAlias(MCInst *MI, SStream *OS) |
| { |
| switch (MCInst_getOpcode(MI)) { |
| case Mips_BEQ: |
| // beq $zero, $zero, $L2 => b $L2 |
| // beq $r0, $zero, $L2 => beqz $r0, $L2 |
| if (isReg(MI, 0, Mips_ZERO) && isReg(MI, 1, Mips_ZERO)) |
| return printAlias1("b", MI, 2, OS); |
| if (isReg(MI, 1, Mips_ZERO)) |
| return printAlias2("beqz", MI, 0, 2, OS); |
| return NULL; |
| case Mips_BEQL: |
| // beql $r0, $zero, $L2 => beqzl $r0, $L2 |
| if (isReg(MI, 0, Mips_ZERO) && isReg(MI, 1, Mips_ZERO)) |
| return printAlias2("beqzl", MI, 0, 2, OS); |
| return NULL; |
| case Mips_BEQ64: |
| // beq $r0, $zero, $L2 => beqz $r0, $L2 |
| if (isReg(MI, 1, Mips_ZERO_64)) |
| return printAlias2("beqz", MI, 0, 2, OS); |
| return NULL; |
| case Mips_BNE: |
| // bne $r0, $zero, $L2 => bnez $r0, $L2 |
| if (isReg(MI, 1, Mips_ZERO)) |
| return printAlias2("bnez", MI, 0, 2, OS); |
| return NULL; |
| case Mips_BNEL: |
| // bnel $r0, $zero, $L2 => bnezl $r0, $L2 |
| if (isReg(MI, 1, Mips_ZERO)) |
| return printAlias2("bnezl", MI, 0, 2, OS); |
| return NULL; |
| case Mips_BNE64: |
| // bne $r0, $zero, $L2 => bnez $r0, $L2 |
| if (isReg(MI, 1, Mips_ZERO_64)) |
| return printAlias2("bnez", MI, 0, 2, OS); |
| return NULL; |
| case Mips_BGEZAL: |
| // bgezal $zero, $L1 => bal $L1 |
| if (isReg(MI, 0, Mips_ZERO)) |
| return printAlias1("bal", MI, 1, OS); |
| return NULL; |
| case Mips_BC1T: |
| // bc1t $fcc0, $L1 => bc1t $L1 |
| if (isReg(MI, 0, Mips_FCC0)) |
| return printAlias1("bc1t", MI, 1, OS); |
| return NULL; |
| case Mips_BC1F: |
| // bc1f $fcc0, $L1 => bc1f $L1 |
| if (isReg(MI, 0, Mips_FCC0)) |
| return printAlias1("bc1f", MI, 1, OS); |
| return NULL; |
| case Mips_JALR: |
| // jalr $ra, $r1 => jalr $r1 |
| if (isReg(MI, 0, Mips_RA)) |
| return printAlias1("jalr", MI, 1, OS); |
| return NULL; |
| case Mips_JALR64: |
| // jalr $ra, $r1 => jalr $r1 |
| if (isReg(MI, 0, Mips_RA_64)) |
| return printAlias1("jalr", MI, 1, OS); |
| return NULL; |
| case Mips_NOR: |
| case Mips_NOR_MM: |
| // nor $r0, $r1, $zero => not $r0, $r1 |
| if (isReg(MI, 2, Mips_ZERO)) |
| return printAlias2("not", MI, 0, 1, OS); |
| return NULL; |
| case Mips_NOR64: |
| // nor $r0, $r1, $zero => not $r0, $r1 |
| if (isReg(MI, 2, Mips_ZERO_64)) |
| return printAlias2("not", MI, 0, 1, OS); |
| return NULL; |
| case Mips_OR: |
| // or $r0, $r1, $zero => move $r0, $r1 |
| if (isReg(MI, 2, Mips_ZERO)) |
| return printAlias2("move", MI, 0, 1, OS); |
| return NULL; |
| default: return NULL; |
| } |
| } |
| |
| #define PRINT_ALIAS_INSTR |
| #include "MipsGenAsmWriter.inc" |
| |
| #endif |