MIPS: Fix MIPS16 decoding, wrong flags and ghost registers (#2665)

* Disable Mips_FeatureUseIndirectJumpsHazard

Mips_FeatureUseIndirectJumpsHazard is only used for jalr when used in
mips32 configs.

* DDIV test

* Fix details on instructions that contains ghost registers.

* Add missing MIPS tables & Fix mips16 and JrcRa16/AddiuSpImmX16 decoding.

Mips16 decoding of JrcRa16 & AddiuSpImmX16 was expecting the wrong bits.

Implement DecodeCPU16RegsRegisterClass for Mips16

Adds missing mips decoding tables:
- DecoderTableNanoMips_Conflict_Space16
- DecoderTableMicroMipsR6_Ambiguous32
- DecoderTableMicroMipsDSP32
- DecoderTable16
- DecoderTable32
- DecoderTableMips32r6_64r6_Ambiguous32
- DecoderTableMips32r6_64r6_BranchZero32
- DecoderTableMipsDSP32

* Patch details only when details are request.

* Fix wrong tests

* Address comments

* Add mips DSP test

* microMips32r3 DSP

* Test Conflict_Space16

* Test Conflict_Space16
This commit is contained in:
Giovanni 2025-04-04 13:40:01 +08:00 committed by GitHub
parent 98a393e34d
commit 81a6ba0389
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 332 additions and 10 deletions

View File

@ -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,

View File

@ -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} ...

View File

@ -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);
}

View File

@ -213,6 +213,8 @@ static const TestOptionMapEntry test_option_map[] = {
.opt = { .type = CS_OPT_SYNTAX, .val = CS_OPT_SYNTAX_CS_REG_ALIAS } },
{ .str = "CS_OPT_SYNTAX_PERCENT",
.opt = { .type = CS_OPT_SYNTAX, .val = CS_OPT_SYNTAX_PERCENT } },
{ .str = "CS_OPT_SYNTAX_NO_DOLLAR",
.opt = { .type = CS_OPT_SYNTAX, .val = CS_OPT_SYNTAX_NO_DOLLAR } },
};
static const cs_enum_id_map cs_enum_map[] = {

View File

@ -207,7 +207,7 @@ test_cases:
expected:
insns:
-
asm_text: "beqzalc $2, 1340"
asm_text: "bnezalc $2, 1340"
-
input:
@ -547,7 +547,7 @@ test_cases:
expected:
insns:
-
asm_text: "bovc $zero, $zero, 12"
asm_text: "bnvc $zero, $zero, 12"
-
input:

View File

@ -507,7 +507,7 @@ test_cases:
expected:
insns:
-
asm_text: "bovc $zero, $zero, 12"
asm_text: "bnvc $zero, $zero, 12"
-
input:
@ -517,7 +517,7 @@ test_cases:
expected:
insns:
-
asm_text: "beqzalc $2, 1340"
asm_text: "bnezalc $2, 1340"
-
input:

View File

@ -422,7 +422,7 @@ test_cases:
mnemonic: "lw"
op_str: "$v0, 0($sp)"
details:
groups: [ HasStdEnc, NotInMicroMips, NotNanoMips ]
groups: [ NotInMips16Mode, HasDSP ]
-
asm_text: "ori $at, $at, 0x3456"
mnemonic: "ori"

View File

@ -5880,3 +5880,149 @@ test_cases:
asm_text: "svc #0x8"
details:
groups: [ call, int ]
-
input:
name: "jalr on mips32r2"
bytes: [ 0x09, 0xf8, 0x20, 0x03 ]
arch: "CS_ARCH_MIPS"
options: [ CS_MODE_MIPS32R2, CS_MODE_LITTLE_ENDIAN ]
address: 0x0
expected:
insns:
-
asm_text: "jalr $t9"
-
input:
name: "ddiv on mips64r2"
bytes: [ 0x01, 0x11, 0x00, 0x1e, 0x01, 0x65, 0x00, 0x1a ]
arch: "CS_ARCH_MIPS"
options: [ CS_MODE_MIPS64R2, CS_MODE_BIG_ENDIAN, CS_OPT_SYNTAX_NO_DOLLAR, CS_OPT_DETAIL ]
address: 0x0
expected:
insns:
- asm_text: "ddiv zero, t0, s1"
details:
mips:
operands:
- type: MIPS_OP_REG
reg: zero
- type: MIPS_OP_REG
reg: t0
- type: MIPS_OP_REG
reg: s1
- asm_text: "div zero, t3, a1"
details:
mips:
operands:
- type: MIPS_OP_REG
reg: zero
- type: MIPS_OP_REG
reg: t3
- type: MIPS_OP_REG
reg: a1
- input:
name: "jr/jrc/ ra & div/divu zero on mips16"
bytes: [ 0xe8, 0x20, 0xe8, 0xa0, 0xeb, 0x5a, 0xeb, 0x5b, 0x63, 0x1e, 0xf0, 0x64, 0x63, 0x05 ]
arch: "CS_ARCH_MIPS"
options: [ CS_MODE_MIPS16, CS_MODE_BIG_ENDIAN, CS_OPT_SYNTAX_NO_DOLLAR, CS_OPT_DETAIL ]
address: 0x0
expected:
insns:
- asm_text: "jr ra"
details:
mips:
operands:
- type: MIPS_OP_REG
reg: ra
- asm_text: "jrc ra"
details:
mips:
operands:
- type: MIPS_OP_REG
reg: ra
- asm_text: "div zero, v1, v0"
details:
mips:
operands:
- type: MIPS_OP_REG
reg: zero
- type: MIPS_OP_REG
reg: v1
- type: MIPS_OP_REG
reg: v0
- asm_text: "divu zero, v1, v0"
details:
mips:
operands:
- type: MIPS_OP_REG
reg: zero
- type: MIPS_OP_REG
reg: v1
- type: MIPS_OP_REG
reg: v0
- asm_text: "addiu sp, 0x1e"
details:
mips:
operands:
- type: MIPS_OP_REG
reg: sp
- type: MIPS_OP_IMM
imm: 0x1e
- asm_text: "addiu sp, 0x2065"
details:
mips:
operands:
- type: MIPS_OP_REG
reg: sp
- type: MIPS_OP_IMM
imm: 0x2065
- input:
name: "Test mips32 DSP"
bytes: [ 0x7e,0x32,0x83,0x11,0x7e,0x53,0x8d,0x11,0x7e,0x74,0x95,0x51,0x7e,0x95,0x9b,0xd1 ]
arch: "CS_ARCH_MIPS"
options: [ CS_MODE_MIPS32, CS_MODE_BIG_ENDIAN ]
address: 0x0
expected:
insns:
- asm_text: "precrq.qb.ph $s0, $s1, $s2"
- asm_text: "precrq.ph.w $s1, $s2, $s3"
- asm_text: "precrq_rs.ph.w $s2, $s3, $s4"
- asm_text: "precrqu_s.qb.ph $s3, $s4, $s5"
- input:
name: "Test microMips32r3 DSP"
bytes: [ 0x00,0xa4,0x1c,0x0d,0x00,0xa4,0x1b,0x05,0x00,0x65,0x42,0xbc,0x00,0x64,0x92,0xbc ]
arch: "CS_ARCH_MIPS"
options: [ CS_MODE_MICRO32R3, CS_MODE_BIG_ENDIAN ]
address: 0x0
expected:
insns:
- asm_text: "addq_s.ph $v1, $a0, $a1"
- asm_text: "addq_s.w $v1, $a0, $a1"
- asm_text: "dpaq_s.w.ph $ac1, $a1, $v1"
- asm_text: "dpaq_sa.l.w $ac2, $a0, $v1"
- input:
name: "Test nanomips BNEC[16] (not available in NMS) - Conflict_Space16"
bytes: [ 0xd8, 0xf6 ]
arch: "CS_ARCH_MIPS"
options: [ CS_MODE_NANOMIPS, CS_MODE_BIG_ENDIAN ]
address: 0x0
expected:
insns:
- asm_text: "bnec $a3, $s1, 14"
- input:
name: "Test mips32r6 - Conflict_Space16"
bytes: [ 0x58, 0x63, 0x00, 0x00, 0x5C, 0x63, 0x00, 0x00 ]
arch: "CS_ARCH_MIPS"
options: [ CS_MODE_MIPS32R6, CS_MODE_BIG_ENDIAN ]
address: 0x0
expected:
insns:
- asm_text: "bgezc $v1, 4"
- asm_text: "bltzc $v1, 8"