diff --git a/Mapping.c b/Mapping.c index bdee97e441..5ca76100ad 100644 --- a/Mapping.c +++ b/Mapping.c @@ -414,7 +414,8 @@ bool map_use_alias_details(const MCInst *MI) { assert(MI); return (MI->csh->detail_opt & CS_OPT_ON) && - !(MI->csh->detail_opt & CS_OPT_DETAIL_REAL); + !(MI->csh->detail_opt & CS_OPT_DETAIL_REAL) && + !(MI->csh->detail_opt & CS_OPT_DETAIL_ALIAS_REAL); } /// Sets the setDetailOps flag to @p Val. diff --git a/arch/RISCV/RISCVInstPrinter.c b/arch/RISCV/RISCVInstPrinter.c index b2309903b7..3d47a8dc37 100644 --- a/arch/RISCV/RISCVInstPrinter.c +++ b/arch/RISCV/RISCVInstPrinter.c @@ -374,6 +374,7 @@ void RISCV_LLVM_printInstruction(MCInst *MI, SStream *O, MI->MRI = (MCRegisterInfo *)info; MCInst_setIsAlias(MI, false); + MI->flat_insn->uncompressed_id = 0; bool usesAliasDetails = map_use_alias_details(MI); MI->flat_insn->usesAliasDetails = usesAliasDetails; @@ -392,6 +393,10 @@ void RISCV_LLVM_printInstruction(MCInst *MI, SStream *O, Uncompressed.csh = MI->csh; Uncompressed.flat_insn = MI->flat_insn; is_uncompressed = true; + const insn_map *umap = lookup_insn_map(MI->csh, + MCInst_getOpcode(&Uncompressed)); + if (umap) + MI->flat_insn->uncompressed_id = umap->mapid; } // print the exact instruction text and done @@ -399,33 +404,45 @@ void RISCV_LLVM_printInstruction(MCInst *MI, SStream *O, (MI->csh->syntax & CS_OPT_SYNTAX_NO_ALIAS_TEXT) || (is_uncompressed && MI->csh->syntax & CS_OPT_SYNTAX_NO_ALIAS_TEXT_COMPRESSED); + + // if alias suppression is on, print the original instruction no matter what if (print_exact_text) { printInstruction(MI, MI->address, O); } else { // side-effectful check for alias instructions that prints to the SStream if true if (printAliasInstr(McInstr, MI->address, O)) { MCInst_setIsAlias(MI, true); - // do we still want the exact details even if the text is alias ? - if (!usesAliasDetails && detail_is_set(MI)) { - // disable actual printing - SStream_Close(O); - // discard the alias operands - memset(MI->flat_insn->detail->riscv.operands, 0, - sizeof(MI->flat_insn->detail->riscv - .operands)); - MI->flat_insn->detail->riscv.op_count = 0; - // re-disassemble again with no printing in order to obtain the full details - // including the whole operands array - printInstruction(MI, MI->address, O); - // re-open the stream to restore the usual state - SStream_Open(O); - } - } else // the instruction is not an alias + } else { printInstruction(McInstr, MI->address, O); + } + } + + bool real = MI->csh->detail_opt & CS_OPT_DETAIL_REAL; + // CS_OPT_DETAIL_REAL takes priority: if enabled, then real details for both aliases and compressed instructions + // CS_OPT_DETAIL_ALIAS_REAL applies when CS_OPT_DETAIL_REAL is absent: real details for aliases only + bool replaceWithRealDetails = real ? (MCInst_isAlias(MI) || is_uncompressed) : MCInst_isAlias(MI); + + // do we still want the exact details (regardless of if the text is alias or not) ? + if (replaceWithRealDetails && !usesAliasDetails && detail_is_set(MI)) { + // disable actual printing + SStream_Close(O); + // discard the alias operands + memset(MI->flat_insn->detail->riscv.operands, 0, + sizeof(MI->flat_insn->detail->riscv.operands)); + MI->flat_insn->detail->riscv.op_count = 0; + // re-disassemble again with no printing in order to obtain the full details + // including the whole operands array + printInstruction(McInstr, MI->address, O); + // re-open the stream to restore the usual state + SStream_Open(O); } RISCV_add_groups(MI); + + // HACKS (TODO: INVESTIGATE later) RISCV_add_missing_write_access(MI); RISCV_compact_operands(MI); + // END HACKS + RISCV_set_alias_id(MI, O); } diff --git a/arch/RISCV/RISCVMapping.c b/arch/RISCV/RISCVMapping.c index a52217d63b..6c667a7a29 100644 --- a/arch/RISCV/RISCVMapping.c +++ b/arch/RISCV/RISCVMapping.c @@ -21,6 +21,7 @@ #include "RISCVGenRegisterInfo.inc" #include "RISCVInstPrinter.h" +#include "RISCVBaseInfo.h" const char *RISCV_reg_name(csh handle, unsigned int reg) { @@ -53,10 +54,23 @@ void RISCV_add_cs_detail_0(MCInst *MI, riscv_op_group opgroup, unsigned OpNum) { if (!detail_is_set(MI)) return; - // are not "true" arguments and has no Capstone equivalent + // rounding mode: store in detail, not as a regular operand if (opgroup == RISCV_OP_GROUP_FRMArg || - opgroup == RISCV_OP_GROUP_FRMArgLegacy) + opgroup == RISCV_OP_GROUP_FRMArgLegacy) { + unsigned frm = (unsigned)MCInst_getOperand(MI, OpNum)->ImmVal; + riscv_rounding_mode rm; + switch (frm) { + case RISCVFPRndMode_RNE: rm = RISCV_RM_RNE; break; + case RISCVFPRndMode_RTZ: rm = RISCV_RM_RTZ; break; + case RISCVFPRndMode_RDN: rm = RISCV_RM_RDN; break; + case RISCVFPRndMode_RUP: rm = RISCV_RM_RUP; break; + case RISCVFPRndMode_RMM: rm = RISCV_RM_RMM; break; + case RISCVFPRndMode_DYN: rm = RISCV_RM_DYN; break; + default: rm = RISCV_RM_INVALID; break; + } + RISCV_get_detail(MI)->rounding_mode = rm; return; + } if (opgroup == RISCV_OP_GROUP_FPImmOperand) { unsigned Imm = (unsigned)MCInst_getOperand(MI, OpNum)->ImmVal; diff --git a/cstool/cstool.c b/cstool/cstool.c index d507453f18..d0499457ff 100644 --- a/cstool/cstool.c +++ b/cstool/cstool.c @@ -643,7 +643,7 @@ static void usage(char *prog) int i, j; printf("Cstool for Capstone Disassembler Engine v%u.%u.%u\n\n", CS_VERSION_MAJOR, CS_VERSION_MINOR, CS_VERSION_EXTRA); - printf("Syntax: %s [-d|-a|-r|-s|-u|-v] [start-address-in-hex-format]\n", + printf("Syntax: %s [-d|-a|-r|-R|-s|-u|-v] [start-address-in-hex-format]\n", prog); printf("\nThe following options are supported:\n"); @@ -676,6 +676,7 @@ static void usage(char *prog) printf("\nExtra options:\n"); printf(" -d show detailed information of the instructions\n"); printf(" -r show detailed information of the real instructions (even for alias)\n"); + printf(" -R show detailed information of the real instructions for aliases only (not for compressed)\n"); printf(" -a Print Capstone register alias (if any). Otherwise LLVM register names are emitted.\n"); printf(" -s decode in SKIPDATA mode\n"); printf(" -u show immediates as unsigned\n"); @@ -691,6 +692,10 @@ static void print_details(csh handle, cs_arch arch, cs_mode md, cs_insn *ins) printf("with %s operand set\n", ins->usesAliasDetails ? "ALIAS" : "REAL"); } + if (ins->uncompressed_id) { + printf("\tUncompressed: %" PRIu64 " (%s)\n", ins->uncompressed_id, + cs_insn_name(handle, ins->uncompressed_id)); + } switch (arch) { case CS_ARCH_X86: @@ -839,9 +844,10 @@ int main(int argc, char **argv) bool skipdata = false; bool custom_reg_alias = false; bool set_real_detail = false; + bool set_alias_real_detail = false; int args_left; - while ((c = getopt(argc, argv, "rasudhvf")) != -1) { + while ((c = getopt(argc, argv, "rRasudhvf")) != -1) { switch (c) { case 'a': custom_reg_alias = true; @@ -849,6 +855,9 @@ int main(int argc, char **argv) case 'r': set_real_detail = true; break; + case 'R': + set_alias_real_detail = true; + break; case 's': skipdata = true; break; @@ -1063,6 +1072,11 @@ int main(int argc, char **argv) (CS_OPT_DETAIL_REAL | CS_OPT_ON)); } + if (set_alias_real_detail) { + cs_option(handle, CS_OPT_DETAIL, + (CS_OPT_DETAIL_ALIAS_REAL | CS_OPT_ON)); + } + count = cs_disasm(handle, assembly, size, address, 0, &insn); if (count > 0) { diff --git a/cstool/cstool_riscv.c b/cstool/cstool_riscv.c index 9fe1ee2a72..6702fe8c59 100644 --- a/cstool/cstool_riscv.c +++ b/cstool/cstool_riscv.c @@ -68,5 +68,14 @@ void print_insn_detail_riscv(csh handle, cs_insn *ins) } } + if (riscv->rounding_mode != RISCV_RM_INVALID) { + static const char *const rm_str[] = { + [RISCV_RM_RNE] = "rne", [RISCV_RM_RTZ] = "rtz", + [RISCV_RM_RDN] = "rdn", [RISCV_RM_RUP] = "rup", + [RISCV_RM_RMM] = "rmm", [RISCV_RM_DYN] = "dyn", + }; + printf("\trounding_mode: %s\n", rm_str[riscv->rounding_mode]); + } + printf("\n"); } diff --git a/include/capstone/capstone.h b/include/capstone/capstone.h index ca061afc31..ad28f83cea 100644 --- a/include/capstone/capstone.h +++ b/include/capstone/capstone.h @@ -372,6 +372,9 @@ typedef enum cs_opt_value { CS_OPT_DETAIL_REAL = 1 << 1, ///< If enabled, always sets the real instruction detail. Even if the instruction is an alias. + CS_OPT_DETAIL_ALIAS_REAL = + 1 + << 2, ///< Like CS_OPT_DETAIL_REAL but only for alias instructions. Compressed instructions that are uncompressed keep alias details. } cs_opt_value; /// An option @@ -551,6 +554,12 @@ typedef struct cs_insn { /// -- Only supported by auto-sync archs -- uint64_t alias_id; + /// If this instruction is a compressed instruction (RISCV only), + /// this member is set with the ID of its non-compressed equivalent. + /// Otherwise 0 (i.e. _INS_INVALID). + /// -- Only supported by RISCV -- + uint64_t uncompressed_id; + /// Address (EIP) of this instruction /// This information is available even when CS_OPT_DETAIL = CS_OPT_OFF uint64_t address; diff --git a/include/capstone/riscv.h b/include/capstone/riscv.h index 83148303b8..17694aeac7 100644 --- a/include/capstone/riscv.h +++ b/include/capstone/riscv.h @@ -24,6 +24,17 @@ extern "C" { #pragma warning(disable : 4201) #endif +//> Floating-point rounding mode for RISC-V FP instructions +typedef enum riscv_rounding_mode { + RISCV_RM_INVALID = 0, ///< not applicable (no rounding mode) + RISCV_RM_RNE, ///< round to nearest, ties to even + RISCV_RM_RTZ, ///< round towards zero + RISCV_RM_RDN, ///< round down (towards -infinity) + RISCV_RM_RUP, ///< round up (towards +infinity) + RISCV_RM_RMM, ///< round to nearest, ties to max magnitude + RISCV_RM_DYN, ///< dynamic rounding mode (use frm CSR) +} riscv_rounding_mode; + //> Operand type for instruction's operands typedef enum riscv_op_type { RISCV_OP_INVALID = CS_OP_INVALID, ///< = CS_OP_INVALID (Uninitialized). @@ -65,6 +76,7 @@ typedef struct cs_riscv { // or 0 when instruction has no operand. uint8_t op_count; cs_riscv_op operands[NUM_RISCV_OPS]; // operands for this instruction. + riscv_rounding_mode rounding_mode; // FP rounding mode, or RISCV_RM_INVALID } cs_riscv; //> RISCV registers diff --git a/suite/cstest/include/test_mapping.h b/suite/cstest/include/test_mapping.h index 993b86bda2..3dd67d9b95 100644 --- a/suite/cstest/include/test_mapping.h +++ b/suite/cstest/include/test_mapping.h @@ -244,6 +244,9 @@ static const TestOptionMapEntry test_option_map[] = { { .str = "CS_OPT_DETAIL_REAL", .opt = { .type = CS_OPT_DETAIL, .val = CS_OPT_DETAIL_REAL | CS_OPT_ON } }, + { .str = "CS_OPT_DETAIL_ALIAS_REAL", + .opt = { .type = CS_OPT_DETAIL, + .val = CS_OPT_DETAIL_ALIAS_REAL | CS_OPT_ON } }, { .str = "CS_OPT_SKIPDATA", .opt = { .type = CS_OPT_SKIPDATA, .val = CS_OPT_ON } }, { .str = "CS_OPT_UNSIGNED", diff --git a/tests/details/riscv.yaml b/tests/details/riscv.yaml index fbfdbf43aa..6340e1e4e7 100644 --- a/tests/details/riscv.yaml +++ b/tests/details/riscv.yaml @@ -6224,4 +6224,82 @@ test_cases: expected: insns: - asm_text: "addi a0, a1, 0" - - asm_text: "c.li a0, 0x15" + + # CS_OPT_DETAIL_ALIAS_REAL: alias (mv) gets real details; compressed (c.add) is NOT an alias + # so is_alias stays false and its details are the uncompressed form's operands unchanged. + - input: + bytes: [0x2e, 0x95, 0x13, 0x85, 0x05, 0x00] + arch: "CS_ARCH_RISCV" + options: [CS_MODE_RISCV32, CS_MODE_RISCV_C, CS_OPT_DETAIL_ALIAS_REAL] + address: 0x0 + expected: + insns: + - asm_text: "add a0, a0, a1" + is_alias: -1 + details: + riscv: + operands: + - type: RISCV_OP_REG + reg: a0 + access: CS_AC_WRITE + - type: RISCV_OP_REG + reg: a0 + access: CS_AC_READ + - type: RISCV_OP_REG + reg: a1 + access: CS_AC_READ + - asm_text: "mv a0, a1" + is_alias: 1 + uses_alias_details: -1 + details: + riscv: + operands: + - type: RISCV_OP_REG + reg: a0 + access: CS_AC_WRITE + - type: RISCV_OP_REG + reg: a1 + access: CS_AC_READ + - type: RISCV_OP_IMM + imm: 0x0 + access: CS_AC_READ + + # Both CS_OPT_DETAIL_REAL and CS_OPT_DETAIL_ALIAS_REAL: CS_OPT_DETAIL_REAL takes priority. + # Compressed (c.add) now also gets real details via re-disassembly (is_uncompressed path). + # Alias (mv) is unchanged — real details in both cases. + - input: + bytes: [0x2e, 0x95, 0x13, 0x85, 0x05, 0x00] + arch: "CS_ARCH_RISCV" + options: [CS_MODE_RISCV32, CS_MODE_RISCV_C, CS_OPT_DETAIL_REAL, CS_OPT_DETAIL_ALIAS_REAL] + address: 0x0 + expected: + insns: + - asm_text: "add a0, a0, a1" + is_alias: -1 + details: + riscv: + operands: + - type: RISCV_OP_REG + reg: a0 + access: CS_AC_WRITE + - type: RISCV_OP_REG + reg: a0 + access: CS_AC_READ + - type: RISCV_OP_REG + reg: a1 + access: CS_AC_READ + - asm_text: "mv a0, a1" + is_alias: 1 + uses_alias_details: -1 + details: + riscv: + operands: + - type: RISCV_OP_REG + reg: a0 + access: CS_AC_WRITE + - type: RISCV_OP_REG + reg: a1 + access: CS_AC_READ + - type: RISCV_OP_IMM + imm: 0x0 + access: CS_AC_READ diff --git a/tests/issues/issues.yaml b/tests/issues/issues.yaml index 5c873e6d14..0968b4709e 100644 --- a/tests/issues/issues.yaml +++ b/tests/issues/issues.yaml @@ -6334,6 +6334,8 @@ test_cases: details: riscv: operands: + - type: "RISCV_OP_REG" + reg: "sp" - type: "RISCV_OP_REG" reg: "sp" - type: "RISCV_OP_IMM" @@ -6354,6 +6356,8 @@ test_cases: details: riscv: operands: + - type: "RISCV_OP_REG" + reg: "sp" - type: "RISCV_OP_REG" reg: "sp" - type: "RISCV_OP_IMM"