From e8206bc27e1633c7e4c99907f43bf67d2f200cd5 Mon Sep 17 00:00:00 2001 From: Galen Williamson Date: Sun, 29 Jun 2025 11:23:58 -0400 Subject: [powerpc] Full merge of PowerPC disassembler PRs, including: * Remove dependency on capstone for PowerPC disassembly #6292 * Add support for PowerPC VLE instruction set #6740 * Add support for paired-single instructions #6821 * Various post-merge fixes and tweaks to disassembly and lifting * Removal of dependence on capstone for assembler's scoring mechanism (capstone currently disabled, but not removed from codebase yet) --- arch/powerpc/capstone_compare_test.c | 1875 ++++++++++++++++++++++++++++++++++ 1 file changed, 1875 insertions(+) create mode 100644 arch/powerpc/capstone_compare_test.c (limited to 'arch/powerpc/capstone_compare_test.c') diff --git a/arch/powerpc/capstone_compare_test.c b/arch/powerpc/capstone_compare_test.c new file mode 100644 index 00000000..0c64c0eb --- /dev/null +++ b/arch/powerpc/capstone_compare_test.c @@ -0,0 +1,1875 @@ +#include +#include +#include + +#include "capstone/capstone.h" + +#include "decode/decode.h" + +bool crbit_equal(ppc_reg cs_reg, uint32_t crbit) +{ + if (crbit >= 32) + { + printf("invalid new crbit %d\n", crbit); + return false; + } + + if (cs_reg < PPC_REG_CR0EQ || cs_reg > PPC_REG_CR7UN) + { + printf("invalid cs crbit cs reg %d\n", cs_reg); + return false; + } + + uint32_t crn = 0; + uint32_t bit = 0; + if (PPC_REG_CR0EQ <= cs_reg && cs_reg <= PPC_REG_CR7EQ) + { + crn = cs_reg - PPC_REG_CR0EQ; + bit = 2; + } + else if (PPC_REG_CR0GT <= cs_reg && cs_reg <= PPC_REG_CR7GT) + { + crn = cs_reg - PPC_REG_CR0GT; + bit = 1; + } + else if (PPC_REG_CR0LT <= cs_reg && cs_reg <= PPC_REG_CR7LT) + { + crn = cs_reg - PPC_REG_CR0LT; + bit = 0; + } else { + // PPC_REG_CR0UN <= cs_reg && cs_reg <= PPC_REG_CR7UN + crn = cs_reg - PPC_REG_CR0UN; + bit = 3; + } + + uint32_t cs_equivalent = 4*crn + bit; + return crbit == cs_equivalent; +} + +bool ops_equal(csh handle, const cs_ppc_op* capstone_op, const Operand* new_op) +{ + switch (new_op->cls) + { + case PPC_OP_NONE: return capstone_op->type == PPC_OP_INVALID; + + case PPC_OP_UIMM: + { + // handle RA|0 + if (capstone_op->type == PPC_OP_REG) + { + if (capstone_op->reg == PPC_REG_INVALID) + { + if (new_op->uimm != 0) + { + printf("new operand is UIMM %ld != 0, when capstone is PPC_REG_INVALID\n", + new_op->uimm); + + return false; + } + + return true; + } + + printf("new operand is UIMM %ld, capstone is REG %d != PPC_REG_INVALID\n", + new_op->uimm, capstone_op->reg); + return false; + } + + if (capstone_op->type != PPC_OP_IMM) + { + printf("new operand is UIMM, but capstone is %d\n", capstone_op->type); + return false; + } + + int64_t capstone_imm = capstone_op->imm; + int64_t new_imm = new_op->uimm; + + if (capstone_imm != new_imm) + { + printf("new operand UIMM is %#lx, capstone imm is %#lx\n", + new_imm, capstone_imm); + return false; + } + + return true; + } + + case PPC_OP_SIMM: + { + if (capstone_op->type != PPC_OP_IMM) + { + printf("new operand is SIMM, but capstone is %d\n", capstone_op->type); + return false; + } + + int64_t capstone_imm = capstone_op->imm; + int64_t new_imm = new_op->simm; + + if (capstone_imm != new_imm) + { + printf("new operand SIMM is %#lx, capstone imm is %#lx\n", + new_imm, capstone_imm); + return false; + } + + return true; + } + + case PPC_OP_REG_RA: + case PPC_OP_REG_RB: + case PPC_OP_REG_RC: + case PPC_OP_REG_RD: + case PPC_OP_REG_RS: + { + if (capstone_op->type != PPC_OP_REG) + { + printf("new operand is register, but capstone is %d\n", capstone_op->type); + return false; + } + + int capstone_reg = capstone_op->reg - PPC_REG_R0; + int new_reg = new_op->reg - NUPPC_REG_GPR0; + + if (capstone_reg != new_reg) + { + printf("new operand register %d, capstone register %d\n", + new_reg, capstone_reg); + + return false; + } + + return true; + } + + case PPC_OP_REG_FRA: + case PPC_OP_REG_FRB: + case PPC_OP_REG_FRC: + case PPC_OP_REG_FRD: + case PPC_OP_REG_FRS: + { + if (capstone_op->type != PPC_OP_REG) + { + printf("new operand is register, but capstone is %d\n", capstone_op->type); + return false; + } + + int capstone_reg = capstone_op->reg - PPC_REG_F0; + int new_reg = new_op->reg - NUPPC_REG_FR0; + + if (capstone_reg != new_reg) + { + printf("new operand register %d, capstone register %d\n", + new_reg, capstone_reg); + + return false; + } + + return true; + } + + case PPC_OP_REG_CRFD: + case PPC_OP_REG_CRFD_IMPLY0: + case PPC_OP_REG_CRFS: + { + if (capstone_op->type != PPC_OP_REG) + { + printf("new operand is CR, but capstone is %d\n", capstone_op->type); + return false; + } + + int capstone_reg = capstone_op->reg - PPC_REG_CR0; + int new_reg = new_op->reg - NUPPC_REG_CRF0; + + if (capstone_reg != new_reg) + { + printf("new operand CR %d, capstone CR register %d\n", + new_reg, capstone_reg); + + return false; + } + + return true; + } + + case PPC_OP_CRBIT_A: + case PPC_OP_CRBIT_B: + case PPC_OP_CRBIT_D: + { + if (capstone_op->type != PPC_OP_REG) + { + printf("new operand is CRbit, but capstone is %d\n", capstone_op->type); + return false; + } + + const char* capstone_name = cs_reg_name(handle, capstone_op->reg); + + if (!crbit_equal(capstone_op->reg, new_op->crbit)) + { + printf("new operand CR %d, capstone CR register %d (%s)\n", + new_op->crbit, capstone_op->reg, capstone_name); + + return false; + } + + return true; + } + + case PPC_OP_REG_AV_VA: + case PPC_OP_REG_AV_VB: + case PPC_OP_REG_AV_VC: + case PPC_OP_REG_AV_VD: + case PPC_OP_REG_AV_VS: + { + if (capstone_op->type != PPC_OP_REG) + { + printf("new operand is altivec register, but capstone is %d\n", capstone_op->type); + return false; + } + + int capstone_reg = capstone_op->reg - PPC_REG_V0; + int new_reg = new_op->reg - NUPPC_REG_AV_VR0; + + if (capstone_reg != new_reg) + { + printf("new operand altivec register %d, capstone altivec register %d\n", + new_reg, capstone_reg); + + return false; + } + + return true; + } + + case PPC_OP_REG_VSX_RA: + case PPC_OP_REG_VSX_RB: + case PPC_OP_REG_VSX_RC: + case PPC_OP_REG_VSX_RD: + case PPC_OP_REG_VSX_RS: + { + if (capstone_op->type != PPC_OP_REG) + { + printf("new operand is vsx (full) register, but capstone is %d\n", capstone_op->type); + return false; + } + + const char* capstone_name = cs_reg_name(handle, capstone_op->reg); + int new_reg = new_op->reg - NUPPC_REG_VSX_VR0; + int capstone_reg; + if (PPC_REG_VS0 <= capstone_op->reg && capstone_op->reg <= PPC_REG_VS63) + capstone_reg = capstone_op->reg - PPC_REG_VS0; + else if (PPC_REG_V0 <= capstone_op->reg && capstone_op->reg <= PPC_REG_V31) + capstone_reg = capstone_op->reg - PPC_REG_V0 + 32; + else + { + printf("new operand vsx (full) register %d, capstone register %d (%s) isn't any kind of vector\n", new_reg, capstone_op->reg, capstone_name); + + return false; + } + + if (capstone_reg != new_reg) + { + printf("new operand vsx (full) register %d, capstone vsx register %d (%s)\n", + new_reg, capstone_reg, capstone_name); + + return false; + } + + return true; + } + + case PPC_OP_REG_VSX_RA_DWORD0: + case PPC_OP_REG_VSX_RB_DWORD0: + case PPC_OP_REG_VSX_RC_DWORD0: + case PPC_OP_REG_VSX_RD_DWORD0: + case PPC_OP_REG_VSX_RS_DWORD0: + { + if (capstone_op->type != PPC_OP_REG) + { + printf("new operand is vsx (dword0) register, but capstone is %d\n", capstone_op->type); + return false; + } + + const char* capstone_name = cs_reg_name(handle, capstone_op->reg); + int new_reg = new_op->reg - NUPPC_REG_VSX_VR0; + int capstone_reg; + if (PPC_REG_VS0 <= capstone_op->reg && capstone_op->reg <= PPC_REG_VS63) + capstone_reg = capstone_op->reg - PPC_REG_VS0; + else if (PPC_REG_V0 <= capstone_op->reg && capstone_op->reg <= PPC_REG_V31) + capstone_reg = capstone_op->reg - PPC_REG_V0 + 32; + else if (PPC_REG_F0 <= capstone_op->reg && capstone_op->reg <= PPC_REG_F31) + capstone_reg = capstone_op->reg - PPC_REG_F0; + else + { + printf("new operand vsx (dword0) register %d, capstone register %d (%s) isn't any kind of vector\n", new_reg, capstone_op->reg, capstone_name); + + return false; + } + + + if (capstone_reg != new_reg) + { + printf("new operand vsx (dword0) register %d, capstone vsx register %d (%s)\n", + new_reg, capstone_reg, capstone_name); + + return false; + } + + return true; + } + + case PPC_OP_MEM_RA: + { + if (capstone_op->type != PPC_OP_MEM) + { + printf("new operand is memory, but capstone is %d\n", capstone_op->type); + return false; + } + + // memory are of the form RA|0 + int new_reg = new_op->mem.reg - NUPPC_REG_GPR0; + if (new_reg == 0) + { + if (capstone_op->mem.base != PPC_REG_INVALID) + { + printf("new operand mem reg is r0, capstone mem reg is %d != invalid\n", + capstone_op->mem.base); + return false; + } + } + else + { + int capstone_reg = capstone_op->mem.base - PPC_REG_R0; + if (capstone_reg != new_reg) + { + printf("new operand mem reg is %d, capstone mem reg is %d\n", + new_reg, capstone_reg); + return false; + } + } + + int capstone_offset = capstone_op->mem.disp; + int new_offset = new_op->mem.offset; + + if (capstone_offset != new_offset) + { + printf("new operand mem offset is %d, capstone mem offset is %d\n", + new_offset, capstone_offset); + + return false; + } + + return true; + } + + default: + printf("unhandled class %d\n", new_op->cls); + return false; + } + +} + +uint32_t bc_to_bi(ppc_bc bc) +{ + switch (bc) + { + case PPC_BC_LT: return 0; + case PPC_BC_GT: return 1; + case PPC_BC_EQ: return 2; + case PPC_BC_SO: return 3; + default: return 0xffffffff; + } +} + +uint32_t crx_to_bi(const ppc_op_crx* crx) +{ + uint32_t crn = (crx->reg - PPC_REG_CR0); + uint32_t extra = 0; + switch (crx->cond) + { + case PPC_BC_LT: extra = 0; break; + case PPC_BC_GT: extra = 1; break; + case PPC_BC_EQ: extra = 2; break; + case PPC_BC_SO: extra = 3; break; + + default: + printf("bcx capstone op 0 cond is weird %d\n", + crx->cond); + return 0xffffffff; + } + + return crx->scale*crn + extra; +} + +// compare bcx