From d1c404bed8fff5bcc07a8d3b1bce742f47356b5e Mon Sep 17 00:00:00 2001 From: Anton Kochkov Date: Sat, 4 May 2024 18:48:21 +0800 Subject: [PATCH] hppa: use capstone disassembler --- librz/arch/isa/hppa/hppa.h | 18 ++ librz/arch/isa/hppa/hppa.inc | 82 ++++++++ librz/arch/meson.build | 2 + librz/arch/p/analysis/analysis_hppa_cs.c | 227 +++++++++++++++++++++++ librz/arch/p/arch_hppa_cs.c | 9 + librz/arch/p/asm/asm_hppa_cs.c | 84 +++++++++ 6 files changed, 422 insertions(+) create mode 100644 librz/arch/isa/hppa/hppa.h create mode 100644 librz/arch/isa/hppa/hppa.inc create mode 100644 librz/arch/p/analysis/analysis_hppa_cs.c create mode 100644 librz/arch/p/arch_hppa_cs.c create mode 100644 librz/arch/p/asm/asm_hppa_cs.c diff --git a/librz/arch/isa/hppa/hppa.h b/librz/arch/isa/hppa/hppa.h new file mode 100644 index 00000000000..ae5b5edca82 --- /dev/null +++ b/librz/arch/isa/hppa/hppa.h @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: 2024 Anton Kochkov +// SPDX-License-Identifier: LGPL-3.0-only + +#include +#include + +#ifndef RZ_HPPA_H +#define RZ_HPPA_H + +typedef struct { + csh h; + cs_mode mode; + cs_insn *insn; + ut32 count; + ut32 word; +} RzAsmHPPAContext; + +#endif // RZ_HPPA_H diff --git a/librz/arch/isa/hppa/hppa.inc b/librz/arch/isa/hppa/hppa.inc new file mode 100644 index 00000000000..d1f3d0e49c1 --- /dev/null +++ b/librz/arch/isa/hppa/hppa.inc @@ -0,0 +1,82 @@ +// SPDX-FileCopyrightText: 2023 billow +// SPDX-License-Identifier: LGPL-3.0-only + +#include +#include +#include "hppa.h" + +static inline cs_mode hppa_cpu_to_cs_mode(const char *cpu_type) { + if (RZ_STR_ISNOTEMPTY(cpu_type)) { + if (!strcmp(cpu_type, "hppa1.1")) { + return CS_MODE_HPPA_11; + } + if (!strcmp(cpu_type, "hppa2.0")) { + return CS_MODE_HPPA_20; + } + if (!strcmp(cpu_type, "hppa2.0w")) { + return CS_MODE_HPPA_20W; + } + } + return CS_MODE_HPPA_11; +} + +static inline bool hppa_setup_cs_handle(RzAsmHPPAContext *ctx, const char *cpu, const char *features) { + const cs_mode mode = hppa_cpu_to_cs_mode(cpu) | CS_MODE_BIG_ENDIAN; + if (mode != ctx->mode) { + cs_close(&ctx->h); + ctx->h = 0; + ctx->mode = mode; + } + + if (ctx->h != 0) { + return true; + } + cs_err err = cs_open(CS_ARCH_HPPA, mode, &ctx->h); + if (err) { + RZ_LOG_ERROR("Failed on cs_open() with error returned: %u\n", err); + return false; + } + err = cs_option(ctx->h, CS_OPT_DETAIL, + RZ_STR_ISNOTEMPTY(features) || features == NULL ? CS_OPT_ON : CS_OPT_OFF); + if (err) { + RZ_LOG_ERROR("Failed on cs_open() with error returned: %u\n", err); + return false; + } + return true; +} + +static inline ut8 hppa_op_count(cs_insn *insn) { + return insn->detail->hppa.op_count; +} + +static inline cs_hppa_op *hppa_op_get(cs_insn *insn, int idx) { + if (idx >= hppa_op_count(insn)) { + RZ_LOG_WARN("Failed to get operand%d [%d]: \"%s %s\"\n", + idx, hppa_op_count(insn), insn->mnemonic, insn->op_str); + rz_warn_if_reached(); + return NULL; + } + return &insn->detail->hppa.operands[idx]; +} + +static inline const char *hppa_op_as_reg(RzAsmHPPAContext *ctx, int idx) { + const cs_hppa_op *op = hppa_op_get(ctx->insn, idx); + if (op->type != HPPA_OP_REG) { + RZ_LOG_WARN("Failed to get operand%d [%d]: \"%s %s\" [reg]\n", + idx, hppa_op_count(ctx->insn), ctx->insn->mnemonic, ctx->insn->op_str); + rz_warn_if_reached(); + return NULL; + } + return cs_reg_name(ctx->h, op->reg); +} + +static inline st64 hppa_op_as_imm(RzAsmHPPAContext *ctx, int idx) { + const cs_hppa_op *op = hppa_op_get(ctx->insn, idx); + if (op->type != HPPA_OP_IMM) { + RZ_LOG_WARN("Failed to get operand%d [%d]: \"%s %s\" [imm]\n", + idx, hppa_op_count(ctx->insn), ctx->insn->mnemonic, ctx->insn->op_str); + rz_warn_if_reached(); + return 0; + } + return op->imm; +} diff --git a/librz/arch/meson.build b/librz/arch/meson.build index 8569c3aa2a9..241f1c54cb9 100644 --- a/librz/arch/meson.build +++ b/librz/arch/meson.build @@ -20,6 +20,7 @@ arch_plugins_list = [ 'gb', 'h8300', 'hexagon', + 'hppa_cs', 'i4004', 'i8080', 'java', @@ -76,6 +77,7 @@ arch_plugin_sources = [ 'p/arch_ebc.c', 'p/arch_gb.c', 'p/arch_h8300.c', + 'p/arch_hppa_cs.c', 'p/arch_hexagon.c', 'p/arch_i4004.c', 'p/arch_i8080.c', diff --git a/librz/arch/p/analysis/analysis_hppa_cs.c b/librz/arch/p/analysis/analysis_hppa_cs.c new file mode 100644 index 00000000000..04ab89f87ca --- /dev/null +++ b/librz/arch/p/analysis/analysis_hppa_cs.c @@ -0,0 +1,227 @@ +// SPDX-FileCopyrightText: 2024 Anton Kochkov +// SPDX-License-Identifier: LGPL-3.0-only + +#include +#include +#include +#include + +#include + +static char *hppa_reg_profile(RzAnalysis *_) { + const char *p = + "=PC pc\n" + "=SP gr30\n" + "=A0 gr26\n" + "=A1 gr25\n" + "=A2 gr24\n" + "=A3 gr23\n" + "gpr gr0 .32 0 0\n" + "gpr gr1 .32 4 0\n" + "gpr gr2 .32 8 0\n" + "gpr gr3 .32 12 0\n" + "gpr gr4 .32 16 0\n" + "gpr gr5 .32 20 0\n" + "gpr gr6 .32 24 0\n" + "gpr gr7 .32 28 0\n" + "gpr gr8 .32 32 0\n" + "gpr gr9 .32 36 0\n" + "gpr gr10 .32 40 0\n" + "gpr gr11 .32 44 0\n" + "gpr gr12 .32 48 0\n" + "gpr gr13 .32 52 0\n" + "gpr gr14 .32 56 0\n" + "gpr gr15 .32 60 0\n" + "gpr gr16 .32 64 0\n" + "gpr gr17 .32 68 0\n" + "gpr gr18 .32 72 0\n" + "gpr gr19 .32 76 0\n" + "gpr gr20 .32 80 0\n" + "gpr gr21 .32 84 0\n" + "gpr gr22 .32 88 0\n" + "gpr gr23 .32 92 0\n" + "gpr gr24 .32 96 0\n" + "gpr gr25 .32 100 0\n" + "gpr gr26 .32 104 0\n" + "gpr gr27 .32 108 0\n" + "gpr gr28 .32 112 0\n" + "gpr gr29 .32 116 0\n" + "gpr gr30 .32 120 0\n" + "gpr gr31 .32 124 0\n" + "ctr sr0 .32 128 0\n" + "ctr sr1 .32 132 0\n" + "ctr sr2 .32 136 0\n" + "ctr sr3 .32 140 0\n" + "ctr sr4 .32 144 0\n" + "ctr sr5 .32 148 0\n" + "ctr sr6 .32 152 0\n" + "ctr sr7 .32 156 0\n" + "flg psw .32 160 0\n"; + return strdup(p); +} + +static void hppa_opex(RzAsmHPPAContext *ctx, RzStrBuf *sb) { + PJ *pj = pj_new(); + if (!pj) { + return; + } + pj_o(pj); + pj_ka(pj, "operands"); + cs_hppa *hpc = &ctx->insn->detail->hppa; + for (st32 i = 0; i < hpc->op_count; i++) { + cs_hppa_op *op = hpc->operands + i; + pj_o(pj); + switch (op->type) { + case HPPA_OP_INVALID: { + pj_ks(pj, "type", "invalid"); + break; + } + case HPPA_OP_REG: { + pj_ks(pj, "type", "reg"); + pj_ks(pj, "value", cs_reg_name(ctx->h, op->reg)); + break; + } + case HPPA_OP_IMM: { + pj_ks(pj, "type", "imm"); + pj_ki(pj, "value", op->imm); + break; + } + case HPPA_OP_MEM: { + pj_ks(pj, "type", "mem"); + pj_ks(pj, "base", cs_reg_name(ctx->h, op->mem.base)); + pj_ks(pj, "space", cs_reg_name(ctx->h, op->mem.space)); + break; + } + // FIXME: Handle other types as well + default: + break; + } + pj_end(pj); + } + pj_end(pj); + pj_end(pj); + + rz_strbuf_init(sb); + rz_strbuf_append(sb, pj_string(pj)); + pj_free(pj); +} + +static void hppa_op_set_type(RzAsmHPPAContext *ctx, RzAnalysisOp *op) { + switch (ctx->insn->id) { + default: { + op->type = RZ_ANALYSIS_OP_TYPE_UNK; + break; + } + case HPPA_INS_BL: { + op->type = RZ_ANALYSIS_OP_TYPE_IRCALL; + op->jump = hppa_op_as_imm(ctx, 0); + break; + } + case HPPA_INS_BLR: { + op->type = RZ_ANALYSIS_OP_TYPE_IRJMP; + op->reg = hppa_op_as_reg(ctx, 0); + break; + } + } +} + +static int +hppa_op(RzAnalysis *a, RzAnalysisOp *op, ut64 addr, const ut8 *data, int len, RzAnalysisOpMask mask) { + if (!(a && op && data && len > 0)) { + return 0; + } + if (!a->big_endian) { + return -1; + } + + RzAsmHPPAContext *ctx = a->plugin_data; + if (!hppa_setup_cs_handle(ctx, a->cpu, NULL)) { + return -1; + } + + op->size = 2; + + ctx->insn = NULL; + ctx->count = cs_disasm(ctx->h, (const ut8 *)data, len, addr, 1, &ctx->insn); + if (ctx->count <= 0 || !ctx->insn) { + op->type = RZ_ANALYSIS_OP_TYPE_ILL; + if (mask & RZ_ANALYSIS_OP_MASK_DISASM) { + op->mnemonic = strdup("invalid"); + } + goto beach; + } + + if (mask & RZ_ANALYSIS_OP_MASK_DISASM) { + op->mnemonic = rz_str_newf("%s%s%s", + ctx->insn->mnemonic, ctx->insn->op_str[0] ? " " : "", ctx->insn->op_str); + } + op->size = ctx->insn->size; + op->id = (int)ctx->insn->id; + op->addr = ctx->insn->address; + hppa_op_set_type(ctx, op); + if (mask & RZ_ANALYSIS_OP_MASK_OPEX) { + hppa_opex(ctx, &op->opex); + } + +beach: + cs_free(ctx->insn, ctx->count); + return op->size; +} + +static int hppa_archinfo(RzAnalysis *a, RzAnalysisInfoType query) { + switch (query) { + case RZ_ANALYSIS_ARCHINFO_MIN_OP_SIZE: + return 2; + case RZ_ANALYSIS_ARCHINFO_MAX_OP_SIZE: + return 4; + case RZ_ANALYSIS_ARCHINFO_TEXT_ALIGN: + case RZ_ANALYSIS_ARCHINFO_DATA_ALIGN: + case RZ_ANALYSIS_ARCHINFO_CAN_USE_POINTERS: + default: + return -1; + } +} + +static bool hppa_init(void **u) { + if (!u) { + return false; + } + RzAsmHPPAContext *ctx = RZ_NEW0(RzAsmHPPAContext); + if (!ctx) { + return false; + } + *u = ctx; + return true; +} + +static bool hppa_fini(void *u) { + if (!u) { + return true; + } + RzAsmHPPAContext *ctx = u; + cs_close(&ctx->h); + free(u); + return true; +} + +RzAnalysisPlugin rz_analysis_plugin_hppa_cs = { + .name = "hppa", + .desc = "Capstone HP PA-RISC analysis plugin", + .author = "xvilka", + .license = "LGPL3", + .arch = "hppa", + .bits = 32 | 64, + .get_reg_profile = hppa_reg_profile, + .archinfo = hppa_archinfo, + .op = hppa_op, + .init = hppa_init, + .fini = hppa_fini, +}; + +#ifndef RZ_PLUGIN_INCORE +RZ_API RzLibStruct rizin_plugin = { + .type = RZ_LIB_TYPE_ANALYSIS, + .data = &rz_analysis_plugin_hppa_cs, + .version = RZ_VERSION +}; +#endif diff --git a/librz/arch/p/arch_hppa_cs.c b/librz/arch/p/arch_hppa_cs.c new file mode 100644 index 00000000000..58ec66cbc8e --- /dev/null +++ b/librz/arch/p/arch_hppa_cs.c @@ -0,0 +1,9 @@ +// SPDX-FileCopyrightText: 2024 RizinOrg +// SPDX-License-Identifier: LGPL-3.0-only + +#include + +#include "analysis/analysis_hppa_cs.c" +#include "asm/asm_hppa_cs.c" + +RZ_ARCH_PLUGIN_DEFINE_DEPRECATED(hppa_cs); diff --git a/librz/arch/p/asm/asm_hppa_cs.c b/librz/arch/p/asm/asm_hppa_cs.c new file mode 100644 index 00000000000..faae9001134 --- /dev/null +++ b/librz/arch/p/asm/asm_hppa_cs.c @@ -0,0 +1,84 @@ +// SPDX-FileCopyrightText: 2024 Anton Kochkov +// SPDX-License-Identifier: LGPL-3.0-only + +#include +#include +#include + +static int disassemble(RzAsm *a, RzAsmOp *op, const ut8 *buf, int len) { + if (!buf || !op || !a->plugin_data) { + return -1; + } + + RzAsmHPPAContext *ctx = a->plugin_data; + if (!hppa_setup_cs_handle(ctx, a->cpu, a->features)) { + return -1; + } + + ctx->insn = NULL; + ctx->count = cs_disasm(ctx->h, buf, len, a->pc, 1, &ctx->insn); + if (ctx->count <= 0) { + goto beach; + } + + op->size = ctx->insn->size; + rz_asm_op_setf_asm(op, "%s%s%s", + ctx->insn->mnemonic, RZ_STR_ISNOTEMPTY(ctx->insn->op_str) ? " " : "", ctx->insn->op_str); + +beach: + cs_free(ctx->insn, ctx->count); + ctx->insn = NULL; + ctx->count = 0; + return op->size; +} + +static bool init(void **u) { + if (!u) { + return false; + } + // u = RzAsm.plugin_data + RzAsmHPPAContext *ctx = NULL; + if (*u) { + rz_mem_memzero(*u, sizeof(RzAsmHPPAContext)); + ctx = *u; + } else { + ctx = RZ_NEW0(RzAsmHPPAContext); + if (!ctx) { + return false; + } + *u = ctx; + } + return true; +} + +static bool fini(void *u) { + if (!u) { + return true; + } + RzAsmHPPAContext *ctx = u; + cs_close(&ctx->h); + free(u); + return true; +} + + +RzAsmPlugin rz_asm_plugin_hppa_cs = { + .name = "hppa", + .arch = "hppa", + .author = "xvilka", + .license = "LGPL3", + .bits = 32 | 64, + .endian = RZ_SYS_ENDIAN_BIG, + .desc = "Capstone HP PA-RISC disassembler", + .disassemble = &disassemble, + .init = &init, + .fini = &fini, +}; + +#ifndef RZ_PLUGIN_INCORE +RZ_API RzLibStruct rizin_plugin = { + .type = RZ_LIB_TYPE_ASM, + .data = &rz_asm_plugin_hppa_cs, + .version = RZ_VERSION +}; +#endif