diff options
Diffstat (limited to 'arch/powerpc/disassembler.cpp')
| -rw-r--r-- | arch/powerpc/disassembler.cpp | 467 |
1 files changed, 165 insertions, 302 deletions
diff --git a/arch/powerpc/disassembler.cpp b/arch/powerpc/disassembler.cpp index 2a421971..df444fe8 100644 --- a/arch/powerpc/disassembler.cpp +++ b/arch/powerpc/disassembler.cpp @@ -1,358 +1,221 @@ -/****************************************************************************** -See disassembler.h for more information about how this fits into the PPC -architecture plugin picture. -******************************************************************************/ +#include <string> +#include <vector> -#include <string.h> // strcpy, etc. +#include "binaryninjaapi.h" #define MYLOG(...) while(0); -//#include <binaryninjaapi.h> -//#define MYLOG BinaryNinja::LogDebug +// #define MYLOG BinaryNinja::LogWarn +// #define MYLOG printf +using namespace std; +using namespace BinaryNinja; // for ::LogDebug, etc. + +#include "decode/decode.h" #include "disassembler.h" #include "util.h" -/* have to do this... while options can be toggled after initialization (thru - cs_option(), the modes cannot, and endianness is considered a mode) */ -thread_local csh handle_lil = 0; -thread_local csh handle_big = 0; - -int DoesQualifyForLocalDisassembly(const uint8_t *data, bool bigendian) +bool FillInstruction(Instruction* instruction, const uint8_t* data, size_t length, uint64_t address, uint32_t extraFlags = 0, uint32_t decodeFlags = DECODE_FLAGS_PPC64, BNEndianness endian=LittleEndian) { - uint32_t insword = *(uint32_t *)data; - int result = PPC_INS_INVALID; - uint32_t tmp = 0; - - if(bigendian == true) - { - insword = bswap32(insword); - } - - // 111111xxx00xxxxxxxxxx00001000000 <- fcmpo - tmp = insword & 0xFC6007FF; - if (tmp==0xFC000040) - result = PPC_INS_BN_FCMPO; - // 111100xxxxxxxxxxxxxxx00111010xxx <- xxpermr - if((insword & 0xFC0007F8) == 0xF00001D0) - result = PPC_INS_BN_XXPERMR; - - return result; -} - -void ppc_fcmpo(uint32_t insword, decomp_result *res) -{ - unsigned regtmp = 0; - - // 111111AAA00BBBBBCCCCC00001000000 "fcmpo crA,fB,fC" - regtmp = PPC_REG_CR0 + ((insword >> 23) & 7); - res->detail.ppc.operands[0].reg = (ppc_reg)(regtmp); - res->detail.ppc.operands[0].type = PPC_OP_REG; - - regtmp = PPC_REG_F0 + ((insword >> 16) & 31); - res->detail.ppc.operands[1].reg = (ppc_reg)(regtmp); - res->detail.ppc.operands[1].type = PPC_OP_REG; - - regtmp = PPC_REG_F0 + ((insword >> 11) & 31); - res->detail.ppc.operands[2].reg = (ppc_reg)(regtmp); - res->detail.ppc.operands[2].type = PPC_OP_REG; - - -#ifdef FORCE_TEST - SStream ss; - struct cs_struct* handle = 0; - struct MCInst tempmc = {0}; - char* first_space = 0; - - // SStream_Init(&ss); - ss.index = 0; - ss.buffer[0] = '\0'; - regtmp = PPC_REG_CR0 + ((insword >> 23) & 7); - tempmc.Operands[0].MachineOperandType = MCOperand::kRegister; - tempmc.Operands[0].Kind = 1; - tempmc.Operands[0].RegVal = regtmp; - regtmp = PPC_REG_F0 + ((insword >> 16) & 31); - tempmc.Operands[1].MachineOperandType = MCOperand::kRegister; - tempmc.Operands[1].Kind = 1; - tempmc.Operands[1].RegVal = regtmp; - regtmp = PPC_REG_F0 + ((insword >> 11) & 31); - tempmc.Operands[2].Kind = 1; - tempmc.Operands[2].MachineOperandType = MCOperand::kRegister; - tempmc.Operands[2].RegVal = regtmp; - - // temporarily set this so that print processing succeeds - res->insn.id = PPC_INS_FCMPU; - - if (handle_big != 0) + instruction->numBytes = length; + switch (instruction->numBytes) { - handle = (struct cs_struct*)handle_big; - } - else if (handle_lil != 0) + case 2: { - handle = (struct cs_struct*)handle_lil; - } - -#define PPC_FCMPUS 804 - - tempmc.csh = handle; - tempmc.Opcode = PPC_FCMPUS; - tempmc.flat_insn = &res->insn; - tempmc.flat_insn->detail = &res->detail; + uint16_t word16 = *(const uint16_t *) data; + // VLE is always big-endian + word16 = bswap16(word16); - if (handle != 0) - { - handle->printer(&tempmc, &ss, handle->printer_info); + return Decompose16(instruction, word16, address, decodeFlags | extraFlags); } - // replace the 'fcmpu' with 'fcmpo' - first_space = strchr(ss.buffer, ' '); - strncpy(res->insn.op_str, first_space + 1, sizeof(res->insn.op_str)); -#endif - - strncpy(res->insn.mnemonic, "fcmpo", sizeof(res->insn.mnemonic)); - - // reset this to the target value - res->insn.id = PPC_INS_BN_FCMPO; - res->detail.ppc.op_count = 3; -} - -void ppc_xxpermr(uint32_t insword, decomp_result *res) -{ - // 111100AAAAABBBBBCCCCC00011010BCA "xxpermr vsA,vsB,vsC" - int a = ((insword & 0x3E00000)>>21)|((insword & 0x1)<<5); - int b = ((insword & 0x1F0000)>>16)|((insword & 0x4)<<3); - int c = ((insword & 0xF800)>>11)|((insword & 0x2)<<4); - - res->detail.ppc.operands[0].reg = (ppc_reg)(PPC_REG_VS0 + a); - res->detail.ppc.operands[0].type = PPC_OP_REG; - res->detail.ppc.operands[1].reg = (ppc_reg)(PPC_REG_VS0 + b); - res->detail.ppc.operands[1].type = PPC_OP_REG; - res->detail.ppc.operands[2].reg = (ppc_reg)(PPC_REG_VS0 + c); - res->detail.ppc.operands[2].type = PPC_OP_REG; - - res->insn.id = PPC_INS_BN_XXPERMR; - res->detail.ppc.op_count = 3; - strncpy(res->insn.mnemonic, "xxpermr", sizeof(res->insn.mnemonic)); -} + case 4: + { + uint32_t word32 = *(const uint32_t *) data; -bool PerformLocalDisassembly(const uint8_t *data, uint64_t addr, size_t &len, decomp_result* res, bool bigendian) -{ - uint32_t local_op = 0; - uint32_t insword = *(uint32_t *)data; + // VLE is always big endian + if (((decodeFlags & DECODE_FLAGS_VLE) != 0) || endian == BigEndian) + word32 = bswap32(word32); - if(bigendian == true) - { - insword = bswap32(insword); + return Decompose32(instruction, word32, address, decodeFlags | extraFlags); } - local_op = DoesQualifyForLocalDisassembly(data, bigendian); - - switch(local_op) - { - case PPC_INS_BN_FCMPO: - ppc_fcmpo(insword, res); - break; - case PPC_INS_BN_XXPERMR: - ppc_xxpermr(insword, res); - break; default: + MYLOG("FillInstruction: unrecognized length %d", length); + LogWarn("FillInstruction: unrecognized length %d", length); return false; } - return true; } -extern "C" int -powerpc_init(int cs_mode_arg) +bool PushOperandTokens(string& result, const Operand* op) { - int rc = -1; + char buf[32]; + switch (op->cls) + { + case PPC_OP_REG_CRFD_IMPLY0: + case PPC_OP_REG_CRFS_IMPLY0: + if (op->reg == PPC_REG_CRF0) + return false; - MYLOG("powerpc_init()\n"); + result += PowerPCRegisterName(op->reg); - if(handle_lil || handle_big) { - MYLOG("ERROR: already initialized!\n"); - goto cleanup; - } + break; - /* initialize capstone handle */ - if(cs_open(CS_ARCH_PPC, (cs_mode)((int)CS_MODE_BIG_ENDIAN | cs_mode_arg), &handle_big) != CS_ERR_OK) { - MYLOG("ERROR: cs_open()\n"); - goto cleanup; - } + case PPC_OP_REG_RA: + case PPC_OP_REG_RB: + case PPC_OP_REG_RD: + case PPC_OP_REG_RS: + 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: + case PPC_OP_REG_CRFD: + case PPC_OP_REG_CRFS: + 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: + case PPC_OP_REG_VSX_RA: + case PPC_OP_REG_VSX_RA_DWORD0: + case PPC_OP_REG_VSX_RB: + case PPC_OP_REG_VSX_RB_DWORD0: + case PPC_OP_REG_VSX_RC: + case PPC_OP_REG_VSX_RC_DWORD0: + case PPC_OP_REG_VSX_RD: + case PPC_OP_REG_VSX_RD_DWORD0: + case PPC_OP_REG_VSX_RS: + case PPC_OP_REG_VSX_RS_DWORD0: + result += PowerPCRegisterName(op->reg); + break; - if(cs_open(CS_ARCH_PPC, (cs_mode)((int)CS_MODE_LITTLE_ENDIAN | cs_mode_arg), &handle_lil) != CS_ERR_OK) { - MYLOG("ERROR: cs_open()\n"); - goto cleanup; - } + case PPC_OP_UIMM: + snprintf(buf, sizeof(buf), "0x%" PRIx64, op->uimm); + result += buf; + break; - cs_option(handle_big, CS_OPT_DETAIL, CS_OPT_ON); - cs_option(handle_lil, CS_OPT_DETAIL, CS_OPT_ON); + case PPC_OP_SIMM: + if (op->simm < 0 && op->simm > -0x10000) + snprintf(buf, sizeof(buf), "-0x%llx", -op->simm); + else + snprintf(buf, sizeof(buf), "0x%llx", op->simm); + result += buf; + break; - rc = 0; - cleanup: - if(rc) { - powerpc_release(); - } + case PPC_OP_LABEL: + snprintf(buf, sizeof(buf), "0x%llx", op->label); + result += buf; + break; - return rc; -} + case PPC_OP_CRBIT_A: + case PPC_OP_CRBIT_B: + case PPC_OP_CRBIT_D: + result += GetCRBitName(op->crbit); + break; -extern "C" void -powerpc_release(void) -{ - if(handle_lil) { - cs_close(&handle_lil); - handle_lil = 0; - } + case PPC_OP_MEM_RA: + // eg: lwz r11, 8(r11) + // + // TODO: it would be nice to have the option to print these + // in hex; printed in decimal now for backwards compatibility + snprintf(buf, sizeof(buf), "%d", op->mem.offset); + result += buf; + + result += "("; + if (op->mem.reg == PPC_REG_GPR0) + result += "0"; + else + result += PowerPCRegisterName(op->mem.reg); + result += ")"; + break; - if(handle_big) { - cs_close(&handle_big); - handle_big = 0; + default: + //MYLOG("pushing a ???\n"); + result += "???"; } + + return true; } -extern "C" int -powerpc_decompose(const uint8_t *data, int size, uint64_t addr, bool lil_end, - struct decomp_result *res, bool is_64bit, int cs_mode_arg) +int disassemble(uint8_t *data, uint32_t addr, string& result) { int rc = -1; - res->status = STATUS_ERROR_UNSPEC; - - if(!handle_lil) { - powerpc_init(cs_mode_arg); - } - - //typedef struct cs_insn { - // unsigned int id; /* see capstone/ppc.h for PPC_INS_ADD, etc. */ - // uint64_t address; - // uint16_t size; - // uint8_t bytes[16]; - // char mnemonic[32]; /* string */ - // char op_str[160]; /* string */ - // cs_detail *detail; /* need CS_OP_DETAIL ON and CS_OP_SKIPDATA is OFF */ - //} cs_insn; - // where cs_detail is some details + architecture specific part - // typedef struct cs_detail { - // uint8_t regs_read[12]; - // uint8_t regs_read_count; - // uint8_t regs_write; - // uint8_t regs_write_count; - // uint8_t groups[8]; - // uint8_t groups_count; - // cs_ppc *ppc; - // } + char buf[32]; + size_t strlenMnem; + Instruction instruction; + const char* mnemonic = NULL; + size_t len = 4; + uint32_t decodeFlags = DECODE_FLAGS_PPC64; - // and finally ppc is: - // typedef struct cs_ppc { - // ppc_bc bc; /* branch code, see capstone/ppc.h for PPC_BC_LT, etc. */ - // ppc_bh bh; /* branch hint, see capstone/ppc.h for PPC_BH_PLUS, etc. */ - // bool update_cr0; - // uint8_t op_count; - // cs_ppc_op operands[8]; - // } cs_ppc; - - // and each operand is: - // typedef struct cs_ppc_op { - // ppc_op_type type; /* see capstone/ppc.h for PPC_OP_REG, etc. */ - // union { - // unsigned int reg; // register value for REG operand - // int32_t imm; // immediate value for IMM operand - // ppc_op_mem mem; // struct ppc_op_mem { uint base; int disp } - // ppc_op_crx crx; // struct ppc_op_crx { uint scale, uint reg } - // }; - // } cs_ppc_op; - - csh handle; - struct cs_struct *hand_tmp = 0; - cs_insn *insn = 0; /* instruction information - cs_disasm() will allocate array of cs_insn here */ - - /* which handle to use? - BIG end or LITTLE end? */ - handle = handle_big; - if(lil_end) handle = handle_lil; - res->handle = handle; - - hand_tmp = (struct cs_struct *)handle; - hand_tmp->mode = (cs_mode)((int)hand_tmp->mode | cs_mode_arg); - - /* call */ - size_t n = cs_disasm(handle, data, size, addr, 1, &insn); - if(n != 1) { - MYLOG("ERROR: cs_disasm() returned %" PRIdPTR " (cs_errno:%d)\n", n, cs_errno(handle)); - goto cleanup; + //MYLOG("%s()\n", __func__); + size_t instructionLength = GetInstructionLength(data, len, decodeFlags); + if (instructionLength == 0) + { + MYLOG("ERROR: not enough bytes for instruction\n"); + return false; } - /* set the status */ - res->status = STATUS_SUCCESS; - - /* copy the instruction struct, and detail sub struct to result */ - memcpy(&(res->insn), insn, sizeof(cs_insn)); - memcpy(&(res->detail), insn->detail, sizeof(cs_detail)); - - rc = 0; - cleanup: - if(insn) { - cs_free(insn, 1); - insn = 0; + len = instructionLength; + if (!FillInstruction(&instruction, data, instructionLength, addr)) + { + MYLOG("ERROR: FillInstruction()\n"); + return -1; } - return rc; -} -extern "C" int -powerpc_disassemble(struct decomp_result *res, char *buf, size_t len) -{ - /* ideally the "heavy" string disassemble result is derived from light data - in the decomposition result, but capstone doesn't make this distinction */ - int rc = -1; + /* mnemonic */ + mnemonic = GetMnemonic(&instruction); - if(len < strlen(res->insn.mnemonic)+strlen(res->insn.op_str) + 2) { - MYLOG("ERROR: insufficient room\n"); - goto cleanup; - } + result = mnemonic; + if (instruction.numOperands > 0) + result += " "; - strcpy(buf, res->insn.mnemonic); - strcat(buf, " "); - strcat(buf, res->insn.op_str); + OperandsList operand_list; + memset(&operand_list, 0, sizeof(operand_list)); - rc = 0; - cleanup: - return rc; -} + // The default is to just copy every operand, to simplify the + // alternate code path for special cases + operand_list.numOperands = instruction.numOperands; + for (int i = 0; i < instruction.numOperands; ++i) + { + operand_list.operands[i] = instruction.operands[i]; + } -static const char* const gqr_array[] = {"gqr0", "gqr1", "gqr2", "gqr3", "gqr4", "gqr5", "gqr6", "gqr7"}; + switch(instruction.id) + { + case PPC_ID_BCx: + FillBcxOperands(&operand_list, &instruction); + break; + case PPC_ID_BCCTRx: + FillBcctrxOperands(&operand_list, &instruction); + break; + case PPC_ID_BCLRx: + FillBclrxOperands(&operand_list, &instruction); + break; + case PPC_ID_VLE_SE_BC: + FillVle16BcOperands(&operand_list, &instruction); + break; + case PPC_ID_VLE_E_BCx: + FillVle32BcxOperands(&operand_list, &instruction); + break; + default: + // Already copied by default + ; + } -extern "C" const char * -powerpc_reg_to_str(uint32_t rid, int cs_mode_arg) -{ - if ((cs_mode_arg & CS_MODE_PS) != 0) + for (int i = 0; i < operand_list.numOperands; ++i) { - if ((rid >= PPC_REG_BN_GQR0) && (rid < PPC_REG_BN_ENDING)) + Operand* op = &(operand_list.operands[i]); + bool was_pushed = PushOperandTokens(result, op); + + if (was_pushed && i < operand_list.numOperands - 1) { - return gqr_array[rid - PPC_REG_BN_GQR0]; + //MYLOG("pushing a comma\n"); + result += ", "; } } - - if(!handle_lil) { - powerpc_init(cs_mode_arg); - } - - return cs_reg_name(handle_lil, rid); -} - -extern "C" const uint32_t -powerpc_crx_to_reg(uint32_t rid) -{ - if (rid >= PPC_REG_CR0EQ && rid <= PPC_REG_CR7EQ) - return (rid - PPC_REG_CR0EQ) * 4 + 2; - else if (rid >= PPC_REG_CR0GT && rid <= PPC_REG_CR7GT) - return (rid - PPC_REG_CR0GT) * 4 + 1; - else if (rid >= PPC_REG_CR0LT && rid <= PPC_REG_CR7LT) - return (rid - PPC_REG_CR0LT) * 4 + 0; - else if (rid >= PPC_REG_CR0UN && rid <= PPC_REG_CR7UN) - return (rid - PPC_REG_CR0UN) * 4 + 3; - else - return rid; + return 0; } |
