diff --git a/arch/AArch64/AArch64Mapping.c b/arch/AArch64/AArch64Mapping.c index 23da9f026..482f6dae3 100644 --- a/arch/AArch64/AArch64Mapping.c +++ b/arch/AArch64/AArch64Mapping.c @@ -379,6 +379,40 @@ static void AArch64_check_updates_flags(MCInst *MI) #endif // CAPSTONE_DIET } +/// Surfaces system-register accesses which alias an architectural +/// register Capstone models. MRS Xt, NZCV reads (and MSR NZCV, Xt +/// writes) the same flags every flag-setting instruction implicitly +/// defines, but the generated implicit register lists cannot express +/// it: MRS/MSR are single generic instructions whose system register +/// is an immediate operand, so LLVM's static Uses/Defs do not depend +/// on it. Add the aliased register to the implicit lists so +/// cs_regs_access() reports it like any other NZCV reader/writer. +/// The 128-bit pair forms MRRS/MSRR are deliberately excluded: NZCV +/// is not a valid 128-bit system register, so those encodings are +/// UNDEFINED rather than flag accesses. +static void AArch64_add_sysreg_alias_access(MCInst *MI) +{ +#ifndef CAPSTONE_DIET + if (!detail_is_set(MI)) + return; + const unsigned opcode = MCInst_getOpcode(MI); + if (opcode != AArch64_MRS && opcode != AArch64_MSR) + return; + cs_detail *detail = get_detail(MI); + for (int i = 0; i < detail->aarch64.op_count; ++i) { + const cs_aarch64_op *op = &detail->aarch64.operands[i]; + if (op->type != AARCH64_OP_SYSREG) + continue; + if (op->sysop.sub_type == AARCH64_OP_REG_MRS && + op->sysop.reg.sysreg == AARCH64_SYSREG_NZCV) + map_add_implicit_read(MI, AARCH64_REG_NZCV); + else if (op->sysop.sub_type == AARCH64_OP_REG_MSR && + op->sysop.reg.sysreg == AARCH64_SYSREG_NZCV) + map_add_implicit_write(MI, AARCH64_REG_NZCV); + } +#endif // CAPSTONE_DIET +} + static aarch64_shifter id_to_shifter(unsigned Opcode) { switch (Opcode) { @@ -934,6 +968,7 @@ void AArch64_printer(MCInst *MI, SStream *O, void * /* MCRegisterInfo* */ info) AArch64_check_post_index_am(MI, O); } AArch64_check_updates_flags(MI); + AArch64_add_sysreg_alias_access(MI); map_set_alias_id(MI, O, insn_alias_mnem_map, ARR_SIZE(insn_alias_mnem_map) - 1); int syntax_opt = MI->csh->syntax; diff --git a/tests/details/aarch64.yaml b/tests/details/aarch64.yaml index 48efa5442..20bf4b92b 100644 --- a/tests/details/aarch64.yaml +++ b/tests/details/aarch64.yaml @@ -1614,3 +1614,74 @@ test_cases: mem_index: x2 access: CS_AC_READ regs_read: [x1, x2] + - + # MRS/MSR of NZCV access the same flags every flag-setting + # instruction implicitly defines, so cs_regs_access() must report + # them. Other system registers (TPIDR_EL0 control case) are + # unaffected, and the 128-bit pair forms MRRS/MSRR must NOT gain + # an NZCV access: NZCV is not a valid 128-bit system register, so + # those encodings are UNDEFINED rather than flag accesses. + input: + bytes: [ 0x00, 0x42, 0x3b, 0xd5, 0x00, 0x42, 0x1b, 0xd5, 0x42, 0xd0, 0x3b, 0xd5, 0x00, 0x42, 0x7b, 0xd5, 0x00, 0x42, 0x5b, 0xd5 ] + arch: "CS_ARCH_AARCH64" + options: [ "CS_OPT_DETAIL" ] + address: 0x0 + expected: + insns: + - + asm_text: "mrs x0, NZCV" + details: + aarch64: + operands: + - + type: AARCH64_OP_REG + reg: x0 + access: CS_AC_WRITE + - + type: AARCH64_OP_SYSREG + sub_type: AARCH64_OP_REG_MRS + sys_raw_val: 0xda10 + cc: AArch64CC_Invalid + regs_read: [ nzcv ] + regs_write: [ x0 ] + - + asm_text: "msr NZCV, x0" + details: + aarch64: + operands: + - + type: AARCH64_OP_SYSREG + sub_type: AARCH64_OP_REG_MSR + sys_raw_val: 0xda10 + - + type: AARCH64_OP_REG + reg: x0 + access: CS_AC_READ + cc: AArch64CC_Invalid + regs_read: [ x0 ] + regs_write: [ nzcv ] + - + asm_text: "mrs x2, TPIDR_EL0" + details: + aarch64: + operands: + - + type: AARCH64_OP_REG + reg: x2 + access: CS_AC_WRITE + - + type: AARCH64_OP_SYSREG + sub_type: AARCH64_OP_REG_MRS + sys_raw_val: 0xde82 + cc: AArch64CC_Invalid + regs_write: [ x2 ] + - + asm_text: "mrrs x0, x1, NZCV" + details: + # Exact-match register lists: x0/x1 only, no NZCV read. + regs_write: [ x0, x1 ] + - + asm_text: "msrr NZCV, x0, x1" + details: + # Exact-match register lists: x0/x1 only, no NZCV write. + regs_read: [ x0, x1 ]