Repository: capstone-engine/capstone (C)

Issue:
Hardcoded registers in MIPS
There are some hardcoded registers for certain instructions:

```c
  /* 10387 */ "ddivu\t$zero, \0"
  /* 10401 */ "ddiv\t$zero, \0"
  /* 10414 */ "addiu\t$sp, \0"
....
  /* 10912 */ "jrc\t$ra\0"
  /* 10920 */ "jr\t$ra\0"
```

This breaks the real flag and no dollar sign flag.

This is the output from the next branch (last commit since this issue)

```
$ cstool -r -d mips64r2+nodollar 0111001e
 0  01 11 00 1e  ddiv   $zero, t0, s1
        ID: 31 (ddiv)
        op_count: 2
                operands[0].type: REG = t0
                operands[0].is_reglist: false
                operands[1].type: REG = s1
                operands[1].is_reglist: false
        Groups: HasStdEnc HasMips3 NotMips32r6 NotMips64r6 NotInMicroMips 
```

The correct output should be
```
$ cstool -r -d mips64r2+nodollar 0111001e
 0  01 11 00 1e  ddiv   zero, t0, s1
        ID: 31 (ddiv)
        op_count: 3
                operands[0].type: REG = zero
                operands[0].is_reglist: false
                operands[1].type: REG = t0
                operands[1].is_reglist: false
                operands[2].type: REG = s1
                operands[3].is_reglist: false
        Groups: HasStdEnc HasMips3 NotMips32r6 NotMips64r6 NotInMicroMips 
```


Discussion: The way to fix this is either messing with the `.td` files (annoying) or patching it out of the text. This is done with a similar case (different register alias) for ARM: https://github.com/capstone-engine/capstone/blob/5464c91dca8c37329acb400176be368a6978c6c7/arch/ARM/ARMMapping.c#L104
the issue is that is missing also some details, not just text.
Those have to be added as well in a fixup function.
It is also done for AArch64 and ARM IIRC.



Fix (patch):
diff --git a/arch/Mips/MipsDisassembler.c b/arch/Mips/MipsDisassembler.c
index 9bc4649942..83b23d1a3b 100644
--- a/arch/Mips/MipsDisassembler.c
+++ b/arch/Mips/MipsDisassembler.c
@@ -236,8 +236,6 @@ bool Mips_getFeatureBits(unsigned int mode, unsigned int feature)
 		}
 		return true;
 	}
-	case Mips_FeatureUseIndirectJumpsHazard:
-		return true;
 	default:
 		return false;
 	}
@@ -1459,6 +1457,7 @@ static DecodeStatus getInstruction(MCInst *Instr, uint64_t *Size, const uint8_t
 	bool IsNanoMips = Mips_getFeatureBits(mode, Mips_FeatureNanoMips);
 	bool IsMips32r6 = Mips_getFeatureBits(mode, Mips_FeatureMips32r6);
 	bool IsMips2 = Mips_getFeatureBits(mode, Mips_FeatureMips2);
+	bool IsMips16 = Mips_getFeatureBits(mode, Mips_FeatureMips16);
 	bool IsCnMips = Mips_getFeatureBits(mode, Mips_FeatureCnMips);
 	bool IsCnMipsP = Mips_getFeatureBits(mode, Mips_FeatureCnMipsP);
 	bool IsFP64 = Mips_getFeatureBits(mode, Mips_FeatureFP64Bit);
@@ -1498,6 +1497,14 @@ static DecodeStatus getInstruction(MCInst *Instr, uint64_t *Size, const uint8_t
 		Result = readInstruction16(Bytes, BytesLen, Address, Size,
 						&Insn, IsBigEndian);
 		if (Result != MCDisassembler_Fail) {
+			Result = decodeInstruction_2(DecoderTableNanoMips_Conflict_Space16,
+						     Instr, Insn, Address,
+						     NULL);
+			if (Result != MCDisassembler_Fail) {
+				*Size = 2;
+				return Result;
+			}
+
 			// Calling the auto-generated decoder function for NanoMips
 			// 16-bit instructions.
 			Result = decodeInstruction_2(DecoderTableNanoMips16,
@@ -1550,6 +1557,14 @@ static DecodeStatus getInstruction(MCInst *Instr, uint64_t *Size, const uint8_t
 
 		if (IsMips32r6) {
 			// Calling the auto-generated decoder function.
+			Result = decodeInstruction_4(DecoderTableMicroMipsR6_Ambiguous32,
+						     Instr, Insn, Address,
+						     NULL);
+			if (Result != MCDisassembler_Fail) {
+				*Size = 4;
+				return Result;
+			}
+
 			Result = decodeInstruction_4(DecoderTableMicroMipsR632,
 						     Instr, Insn, Address,
 						     NULL);
@@ -1567,6 +1582,13 @@ static DecodeStatus getInstruction(MCInst *Instr, uint64_t *Size, const uint8_t
 			return Result;
 		}
 
+		Result = decodeInstruction_4(DecoderTableMicroMipsDSP32, Instr,
+					     Insn, Address, NULL);
+		if (Result != MCDisassembler_Fail) {
+			*Size = 4;
+			return Result;
+		}
+
 		if (IsFP64) {
 			Result =
 				decodeInstruction_4(DecoderTableMicroMipsFP6432,
@@ -1586,6 +1608,28 @@ static DecodeStatus getInstruction(MCInst *Instr, uint64_t *Size, const uint8_t
 		return MCDisassembler_Fail;
 	}
 
+	if (IsMips16) {
+		Result = readInstruction32(Bytes, BytesLen, Address, Size,
+					   &Insn, IsBigEndian, IsMicroMips);
+		if (Result != MCDisassembler_Fail) {
+			Result = decodeInstruction_4(DecoderTable32, Instr, Insn, Address,
+						     NULL);
+			if (Result != MCDisassembler_Fail) {
+				*Size = 4;
+				return Result;
+			}
+		}
+
+		Result = readInstruction16(Bytes, BytesLen, Address, Size,
+					   &Insn, IsBigEndian);
+		if (Result == MCDisassembler_Fail) {
+			return MCDisassembler_Fail;
+		}
+
+		*Size = 2;
+		return decodeInstruction_2(DecoderTable16, Instr, Insn, Address, NULL);
+	}
+
 	// Attempt to read the instruction so that we can attempt to decode it. If
 	// the buffer is not 4 bytes long, let the higher level logic figure out
 	// what to do with a size of zero and MCDisassembler::Fail.
@@ -1619,6 +1663,16 @@ static DecodeStatus getInstruction(MCInst *Instr, uint64_t *Size, const uint8_t
 	}
 
 	if (IsMips32r6) {
+		Result = decodeInstruction_4(DecoderTableMips32r6_64r6_Ambiguous32, Instr,
+					     Insn, Address, NULL);
+		if (Result != MCDisassembler_Fail)
+			return Result;
+
+		Result = decodeInstruction_4(DecoderTableMips32r6_64r6_BranchZero32, Instr,
+					     Insn, Address, NULL);
+		if (Result != MCDisassembler_Fail)
+			return Result;
+
 		Result = decodeInstruction_4(DecoderTableMips32r6_64r632, Instr,
 					     Insn, Address, NULL);
 		if (Result != MCDisassembler_Fail)
@@ -1660,6 +1714,11 @@ static DecodeStatus getInstruction(MCInst *Instr, uint64_t *Size, const uint8_t
 			return Result;
 	}
 
+	Result = decodeInstruction_4(DecoderTableMipsDSP32, Instr, Insn,
+				     Address, NULL);
+	if (Result != MCDisassembler_Fail)
+		return Result;
+
 	// Calling the auto-generated decoder function.
 	Result = decodeInstruction_4(DecoderTableMips32, Instr, Insn, Address,
 				     NULL);
@@ -1673,7 +1732,15 @@ static DecodeStatus DecodeCPU16RegsRegisterClass(MCInst *Inst, unsigned RegNo,
 						 uint64_t Address,
 						 const void *Decoder)
 {
-	return MCDisassembler_Fail;
+	if (RegNo > 7)
+		return MCDisassembler_Fail;
+
+	if (RegNo < 2)
+		RegNo += 16;
+
+	unsigned Reg = getReg(Inst, Mips_GPR32RegClassID, RegNo);
+	MCOperand_CreateReg0(Inst, (Reg));
+	return MCDisassembler_Success;
 }
 
 static DecodeStatus DecodeGPR64RegisterClass(MCInst *Inst, unsigned RegNo,
diff --git a/arch/Mips/MipsGenDisassemblerTables.inc b/arch/Mips/MipsGenDisassemblerTables.inc
index abe630639e..d9b1148622 100644
--- a/arch/Mips/MipsGenDisassemblerTables.inc
+++ b/arch/Mips/MipsGenDisassemblerTables.inc
@@ -101,7 +101,7 @@ static const uint8_t DecoderTable16[] = {
 /* 299 */     MCD_OPC_CheckPredicate, 0, 57, 1, 0, // Skip to: 617
 /* 304 */     MCD_OPC_CheckField, 8, 3, 0, 50, 1, 0, // Skip to: 617
 /* 311 */     MCD_OPC_Decode, 150, 15, 10, // Opcode: JrcRx16
-/* 315 */     MCD_OPC_FilterValue, 7, 41, 1, 0, // Skip to: 617
+/* 315 */     MCD_OPC_FilterValue, 5, 41, 1, 0, // Skip to: 617
 /* 320 */     MCD_OPC_CheckPredicate, 0, 36, 1, 0, // Skip to: 617
 /* 325 */     MCD_OPC_CheckField, 8, 3, 0, 29, 1, 0, // Skip to: 617
 /* 332 */     MCD_OPC_Decode, 149, 15, 10, // Opcode: JrcRa16
@@ -190,7 +190,7 @@ static const uint8_t DecoderTable32[] = {
 /* 97 */      MCD_OPC_CheckField, 27, 5, 30, 167, 1, 0, // Skip to: 527
 /* 104 */     MCD_OPC_CheckField, 5, 3, 0, 160, 1, 0, // Skip to: 527
 /* 111 */     MCD_OPC_Decode, 240, 8, 16, // Opcode: BnezRxImmX16
-/* 115 */     MCD_OPC_FilterValue, 6, 106, 0, 0, // Skip to: 226
+/* 115 */     MCD_OPC_FilterValue, 12, 106, 0, 0, // Skip to: 226
 /* 120 */     MCD_OPC_ExtractField, 27, 5,  // Inst{31-27} ...
 /* 123 */     MCD_OPC_FilterValue, 30, 143, 1, 0, // Skip to: 527
 /* 128 */     MCD_OPC_ExtractField, 16, 5,  // Inst{20-16} ...
diff --git a/arch/Mips/MipsInstPrinter.c b/arch/Mips/MipsInstPrinter.c
index 3f587da1a8..3d3b69322c 100644
--- a/arch/Mips/MipsInstPrinter.c
+++ b/arch/Mips/MipsInstPrinter.c
@@ -121,6 +121,110 @@ static void printRegName(MCInst *MI, SStream *OS, MCRegister Reg)
 	SStream_concat0(OS, Mips_LLVM_getRegisterName(Reg, syntax_opt & CS_OPT_SYNTAX_NOREGNAME));
 }
 
+static void patch_cs_printer(MCInst *MI, SStream *O) {
+	// replace '# 16 bit inst' to empty.
+	SStream_replc(O, '#', 0);
+	SStream_trimls(O);
+
+	if (MI->csh->syntax & CS_OPT_SYNTAX_NO_DOLLAR) {
+		char *dollar = strchr(O->buffer, '$');
+		if (!dollar) {
+			return;
+		}
+		size_t dollar_len = strlen(dollar + 1);
+		// to include `\0`
+		memmove(dollar, dollar + 1, dollar_len + 1);
+	}
+}
+
+static void patch_cs_detail_operand_reg(cs_mips_op *op, unsigned reg, unsigned access) {
+	op->type = MIPS_OP_REG;
+	op->reg = reg;
+	op->is_reglist = false;
+	op->access = access;
+}
+
+static void patch_cs_details(MCInst *MI) {
+	if (!detail_is_set(MI))
+		return;
+
+	cs_mips_op *op0 = NULL, *op1 = NULL, *op2 = NULL;
+	unsigned opcode = MCInst_getOpcode(MI);
+	unsigned n_ops = MCInst_getNumOperands(MI);
+
+	switch(opcode) {
+	/* mips r2 to r5 only 64bit */
+	case Mips_DSDIV: /// ddiv $$zero, $rs, $rt
+		/* fall-thru */
+	case Mips_DUDIV: /// ddivu $$zero, $rs, $rt
+		if (n_ops != 2) {
+			return;
+		}
+		Mips_inc_op_count(MI);
+		op0 = Mips_get_detail_op(MI, -3);
+		op1 = Mips_get_detail_op(MI, -2);
+		op2 = Mips_get_detail_op(MI, -1);
+		// move all details by one and add $zero reg
+		*op2 = *op1;
+		*op1 = *op0;
+		patch_cs_detail_operand_reg(op0, MIPS_REG_ZERO_64, CS_AC_WRITE);
+		return;
+
+	/* mips r2 to r5 only */
+	case Mips_SDIV: /// div $$zero, $rs, $rt
+		/* fall-thru */
+	case Mips_UDIV: /// divu $$zero, $rs, $rt
+		/* fall-thru */
+	/* microMIPS only */
+	case Mips_SDIV_MM: /// div $$zero, $rs, $rt
+		/* fall-thru */
+	case Mips_UDIV_MM: /// divu $$zero, $rs, $rt
+		/* fall-thru */
+
+	/* MIPS16 only */
+	case Mips_DivRxRy16: /// div $$zero, $rx, $ry
+		/* fall-thru */
+	case Mips_DivuRxRy16: /// divu $$zero, $rx, $ry
+		if (n_ops != 2) {
+			return;
+		}
+		Mips_inc_op_count(MI);
+		op0 = Mips_get_detail_op(MI, -3);
+		op1 = Mips_get_detail_op(MI, -2);
+		op2 = Mips_get_detail_op(MI, -1);
+		// move all details by one and add $zero reg
+		*op2 = *op1;
+		*op1 = *op0;
+		patch_cs_detail_operand_reg(op0, MIPS_REG_ZERO, CS_AC_WRITE);
+		return;
+	case Mips_AddiuSpImm16: /// addiu $$sp, imm8
+		/* fall-thru */
+	case Mips_AddiuSpImmX16: /// addiu $$sp, imm8
+		if (n_ops != 1) {
+			return;
+		}
+		Mips_inc_op_count(MI);
+		op0 = Mips_get_detail_op(MI, -2);
+		op1 = Mips_get_detail_op(MI, -1);
+		// move all details by one and add $sp reg
+		*op1 = *op0;
+		patch_cs_detail_operand_reg(op0, MIPS_REG_SP, CS_AC_READ_WRITE);
+		return;
+	case Mips_JrcRa16: /// jrc $ra
+		/* fall-thru */
+	case Mips_JrRa16: /// jr $ra
+		if (n_ops > 0) {
+			return;
+		}
+		Mips_inc_op_count(MI);
+		op0 = Mips_get_detail_op(MI, -1);
+		patch_cs_detail_operand_reg(op0, MIPS_REG_RA, CS_AC_READ);
+		return;
+	default:
+		return;
+	}
+}
+
 void Mips_LLVM_printInst(MCInst *MI, uint64_t Address, SStream *O) {
 	bool useAliasDetails = map_use_alias_details(MI);
 	if (!useAliasDetails) {
@@ -137,6 +241,9 @@ void Mips_LLVM_printInst(MCInst *MI, uint64_t Address, SStream *O) {
 		printInstruction(MI, Address, O);
 	}
 
+	patch_cs_printer(MI, O);
+	patch_cs_details(MI);
+
 	if (!useAliasDetails) {
 		map_set_fill_detail_ops(MI, true);
 	}