diff --git a/librz/arch/isa/hppa/hppa.h b/librz/arch/isa/hppa/hppa.h new file mode 100644 index 00000000000..bd235250ed4 --- /dev/null +++ b/librz/arch/isa/hppa/hppa.h @@ -0,0 +1,19 @@ +// 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; + RzPVector /**/ *token_patterns; +} 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..c03a163bdda --- /dev/null +++ b/librz/arch/isa/hppa/hppa.inc @@ -0,0 +1,116 @@ +// SPDX-FileCopyrightText: 2023 billow +// SPDX-FileCopyrightText: 2024 Anton Kochkov +// 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, bool big_endian) { + const cs_mode mode = hppa_cpu_to_cs_mode(cpu) | (big_endian ? CS_MODE_BIG_ENDIAN : CS_MODE_LITTLE_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 const char *hppa_op_as_mem(RzAsmHPPAContext *ctx, int idx) { + const cs_hppa_op *op = hppa_op_get(ctx->insn, idx); + if (op->type != HPPA_OP_MEM) { + 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; +} + +static inline st64 hppa_op_as_disp(RzAsmHPPAContext *ctx, int idx) { + const cs_hppa_op *op = hppa_op_get(ctx->insn, idx); + if (op->type != HPPA_OP_DISP) { + 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; +} + +static inline st64 hppa_op_as_target(RzAsmHPPAContext *ctx, int idx) { + const cs_hppa_op *op = hppa_op_get(ctx->insn, idx); + if (op->type != HPPA_OP_TARGET) { + 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 298e5e96b6d..93e9e8ce4d8 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..f307a118dbc --- /dev/null +++ b/librz/arch/p/analysis/analysis_hppa_cs.c @@ -0,0 +1,493 @@ +// SPDX-FileCopyrightText: 2024 Anton Kochkov +// SPDX-License-Identifier: LGPL-3.0-only + +#include +#include +#include +#include + +#include + +static char *hppa_reg_profile(RzAnalysis *analysis) { + if (analysis->bits == 64) { + const char *p = + "=PC pc\n" + "=SP r30\n" + "=A0 r26\n" + "=A1 r25\n" + "=A2 r24\n" + "=A3 r23\n" + "gpr r0 .64 0 0\n" + "gpr r1 .64 8 0\n" + "gpr r2 .64 16 0\n" + "gpr r3 .64 24 0\n" + "gpr r4 .64 32 0\n" + "gpr r5 .64 40 0\n" + "gpr r6 .64 48 0\n" + "gpr r7 .64 56 0\n" + "gpr r8 .64 64 0\n" + "gpr r9 .64 72 0\n" + "gpr r10 .64 80 0\n" + "gpr r11 .64 88 0\n" + "gpr r12 .64 96 0\n" + "gpr r13 .64 104 0\n" + "gpr r14 .64 112 0\n" + "gpr r15 .64 120 0\n" + "gpr r16 .64 128 0\n" + "gpr r17 .64 136 0\n" + "gpr r18 .64 144 0\n" + "gpr r19 .64 152 0\n" + "gpr r20 .64 160 0\n" + "gpr r21 .64 168 0\n" + "gpr r22 .64 176 0\n" + "gpr r23 .64 184 0\n" + "gpr r24 .64 192 0\n" + "gpr r25 .64 200 0\n" + "gpr r26 .64 208 0\n" + "gpr r27 .64 216 0\n" + "gpr r28 .64 224 0\n" + "gpr r29 .64 232 0\n" + "gpr r30 .64 240 0\n" + "gpr r31 .64 248 0\n" + "ctr sr0 .64 256 0\n" + "ctr sr1 .64 264 0\n" + "ctr sr2 .64 272 0\n" + "ctr sr3 .64 280 0\n" + "ctr sr4 .64 288 0\n" + "ctr sr5 .64 296 0\n" + "ctr sr6 .64 304 0\n" + "ctr sr7 .64 312 0\n" + "flg psw .64 320 0\n"; + return strdup(p); + } else { + const char *p = + "=PC pc\n" + "=SP r30\n" + "=A0 r26\n" + "=A1 r25\n" + "=A2 r24\n" + "=A3 r23\n" + "gpr r0 .32 0 0\n" + "gpr r1 .32 4 0\n" + "gpr r2 .32 8 0\n" + "gpr r3 .32 12 0\n" + "gpr r4 .32 16 0\n" + "gpr r5 .32 20 0\n" + "gpr r6 .32 24 0\n" + "gpr r7 .32 28 0\n" + "gpr r8 .32 32 0\n" + "gpr r9 .32 36 0\n" + "gpr r10 .32 40 0\n" + "gpr r11 .32 44 0\n" + "gpr r12 .32 48 0\n" + "gpr r13 .32 52 0\n" + "gpr r14 .32 56 0\n" + "gpr r15 .32 60 0\n" + "gpr r16 .32 64 0\n" + "gpr r17 .32 68 0\n" + "gpr r18 .32 72 0\n" + "gpr r19 .32 76 0\n" + "gpr r20 .32 80 0\n" + "gpr r21 .32 84 0\n" + "gpr r22 .32 88 0\n" + "gpr r23 .32 92 0\n" + "gpr r24 .32 96 0\n" + "gpr r25 .32 100 0\n" + "gpr r26 .32 104 0\n" + "gpr r27 .32 108 0\n" + "gpr r28 .32 112 0\n" + "gpr r29 .32 116 0\n" + "gpr r30 .32 120 0\n" + "gpr r31 .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 inline void hppa_fillval(RzReg *rz_reg, csh handle, RzAnalysisValue *av, cs_hppa_op *hop) { + switch (hop->type) { + case HPPA_OP_INVALID: + default: + av->type = RZ_ANALYSIS_VAL_UNK; + break; + case HPPA_OP_IMM: + case HPPA_OP_DISP: + case HPPA_OP_TARGET: + av->type = RZ_ANALYSIS_VAL_IMM; + av->imm = hop->imm; + break; + case HPPA_OP_REG: + case HPPA_OP_IDX_REG: + av->type = RZ_ANALYSIS_VAL_REG; + av->reg = rz_reg_get(rz_reg, cs_reg_name(handle, hop->reg), RZ_REG_TYPE_ANY); + break; + case HPPA_OP_MEM: + av->type = RZ_ANALYSIS_VAL_MEM; + // FIXME: Handle also the space + av->reg = rz_reg_get(rz_reg, cs_reg_name(handle, hop->mem.base), RZ_REG_TYPE_ANY); + av->delta = 0; + break; + } +} + +static void hppa_fillvals(RzAsmHPPAContext *ctx, RzAnalysis *a, RzAnalysisOp *op) { + uint8_t srci = 0; + cs_hppa *hc = &ctx->insn->detail->hppa; + for (uint8_t i = 0; i < hc->op_count; ++i) { + cs_hppa_op *hop = &hc->operands[i]; + RzAnalysisValue *av = rz_analysis_value_new(); + hppa_fillval(a->reg, ctx->h, av, hop); + if (hop->access & CS_AC_READ) { + av->access |= RZ_ANALYSIS_ACC_R; + op->src[srci++] = av; + } + if (hop->access & CS_AC_WRITE) { + av->access |= RZ_ANALYSIS_ACC_W; + if (srci > 0 && av == op->src[srci - 1]) { + av = rz_mem_dup(av, sizeof(RzAnalysisValue)); + } + op->dst = av; + } + } +} + +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_IDX_REG: { + pj_ks(pj, "type", "idx_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_DISP: { + pj_ks(pj, "type", "disp"); + pj_ki(pj, "value", op->imm); + break; + } + case HPPA_OP_TARGET: { + pj_ks(pj, "type", "target"); + 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)); + if (op->mem.space != HPPA_REG_INVALID) { + pj_ks(pj, "space", cs_reg_name(ctx->h, op->mem.space)); + } else { + pj_ks(pj, "space", "unavailable"); + } + 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_NOP: + op->type = RZ_ANALYSIS_OP_TYPE_NOP; + break; + case HPPA_INS_ADD: + case HPPA_INS_ADDI: + case HPPA_INS_ADDIO: + case HPPA_INS_ADDIT: + case HPPA_INS_ADDITO: + case HPPA_INS_ADDIL: + case HPPA_INS_ADDC: + case HPPA_INS_ADDCO: + case HPPA_INS_ADDL: + case HPPA_INS_ADDO: + op->type = RZ_ANALYSIS_OP_TYPE_ADD; + break; + case HPPA_INS_ADDB: + case HPPA_INS_ADDIB: + op->type = RZ_ANALYSIS_OP_TYPE_JMP; + op->jump = ctx->insn->address + hppa_op_as_target(ctx, 2); + break; + case HPPA_INS_ADDBT: + case HPPA_INS_ADDBF: + case HPPA_INS_ADDIBT: + case HPPA_INS_ADDIBF: + op->type = RZ_ANALYSIS_OP_TYPE_CJMP; + op->jump = ctx->insn->address + hppa_op_as_target(ctx, 2); + op->fail = ctx->insn->address + ctx->insn->size; + break; + case HPPA_INS_AND: + case HPPA_INS_ANDCM: + op->type = RZ_ANALYSIS_OP_TYPE_AND; + break; + case HPPA_INS_BB: + op->type = RZ_ANALYSIS_OP_TYPE_CJMP; + op->jump = ctx->insn->address + hppa_op_as_target(ctx, 2); + op->fail = ctx->insn->address + ctx->insn->size; + break; + case HPPA_INS_BE: + op->type = RZ_ANALYSIS_OP_TYPE_IRCALL; + op->reg = hppa_op_as_mem(ctx, 1); + break; + case HPPA_INS_BL: + op->type = RZ_ANALYSIS_OP_TYPE_IRCALL; + op->jump = ctx->insn->address + hppa_op_as_target(ctx, 0); + break; + case HPPA_INS_BLE: + op->type = RZ_ANALYSIS_OP_TYPE_IRCALL; + op->reg = hppa_op_as_mem(ctx, 1); + break; + case HPPA_INS_BLR: + op->type = RZ_ANALYSIS_OP_TYPE_IRJMP; + op->reg = hppa_op_as_reg(ctx, 0); + break; + case HPPA_INS_BV: + op->type = RZ_ANALYSIS_OP_TYPE_IRCALL; + // FIXME: Should be result of the *two* registers actually + op->reg = hppa_op_as_mem(ctx, 1); + break; + case HPPA_INS_BVB: + op->type = RZ_ANALYSIS_OP_TYPE_CJMP; + op->jump = ctx->insn->address + hppa_op_as_target(ctx, 1); + break; + case HPPA_INS_GATE: + op->type = RZ_ANALYSIS_OP_TYPE_JMP; + op->jump = hppa_op_as_target(ctx, 0); + break; + case HPPA_INS_LDB: + case HPPA_INS_LDBS: + case HPPA_INS_LDCD: + case HPPA_INS_LDCW: + case HPPA_INS_LDCWS: + case HPPA_INS_LDD: + case HPPA_INS_LDDA: + case HPPA_INS_LDH: + case HPPA_INS_LDHS: + case HPPA_INS_LDI: + case HPPA_INS_LDW: + case HPPA_INS_LDWA: + case HPPA_INS_LDWAS: + case HPPA_INS_LDWM: + case HPPA_INS_LDWS: + op->type = RZ_ANALYSIS_OP_TYPE_LOAD; + const cs_hppa_op *op1 = hppa_op_get(ctx->insn, 1); + if (op1->type == HPPA_OP_REG && op1->reg == HPPA_REG_GR30 /* SP */) { + op->stackop = RZ_ANALYSIS_STACK_GET; + op->stackptr = 0; + } + op->ptr = (st64)hppa_op_as_disp(ctx, 0); + break; + case HPPA_INS_LDSID: + case HPPA_INS_LDHX: + case HPPA_INS_LDBX: + case HPPA_INS_LDCWX: + case HPPA_INS_LDWAX: + case HPPA_INS_LDWX: + op->type = RZ_ANALYSIS_OP_TYPE_LOAD; + break; + case HPPA_INS_LDIL: + op->type = RZ_ANALYSIS_OP_TYPE_MOV; + break; + case HPPA_INS_LCI: + case HPPA_INS_LDO: + op->type = RZ_ANALYSIS_OP_TYPE_LEA; + break; + case HPPA_INS_MOVB: + case HPPA_INS_MOVIB: + op->type = RZ_ANALYSIS_OP_TYPE_CJMP; + op->jump = ctx->insn->address + hppa_op_as_target(ctx, 2); + op->fail = ctx->insn->address + ctx->insn->size; + break; + case HPPA_INS_OR: + op->type = RZ_ANALYSIS_OP_TYPE_OR; + break; + case HPPA_INS_CALL: + op->type = RZ_ANALYSIS_OP_TYPE_CALL; + op->jump = hppa_op_as_target(ctx, 0); + break; + case HPPA_INS_COMIB: + case HPPA_INS_COMIBT: + case HPPA_INS_COMIBF: + case HPPA_INS_COMB: + case HPPA_INS_COMBT: + case HPPA_INS_COMBF: + op->type = RZ_ANALYSIS_OP_TYPE_CJMP; + op->jump = ctx->insn->address + hppa_op_as_target(ctx, 2); + op->fail = ctx->insn->address + ctx->insn->size; + break; + case HPPA_INS_STB: + case HPPA_INS_STBS: + case HPPA_INS_STBY: + case HPPA_INS_STBYS: + case HPPA_INS_STD: + case HPPA_INS_STDA: + case HPPA_INS_STDBY: + case HPPA_INS_STH: + case HPPA_INS_STHS: + case HPPA_INS_STW: + case HPPA_INS_STWA: + case HPPA_INS_STWAS: + case HPPA_INS_STWS: + case HPPA_INS_STWM: + op->type = RZ_ANALYSIS_OP_TYPE_STORE; + const cs_hppa_op *op2 = hppa_op_get(ctx->insn, 2); + if (op2->type == HPPA_OP_REG && op2->reg == HPPA_REG_GR30 /* SP */) { + op->stackop = RZ_ANALYSIS_STACK_SET; + op->stackptr = 0; + } + op->ptr = (st64)hppa_op_as_disp(ctx, 1); + break; + case HPPA_INS_SUB: + case HPPA_INS_SUBB: + case HPPA_INS_SUBBO: + case HPPA_INS_SUBI: + case HPPA_INS_SUBIO: + case HPPA_INS_SUBO: + case HPPA_INS_SUBT: + case HPPA_INS_SUBTO: + op->type = RZ_ANALYSIS_OP_TYPE_SUB; + break; + case HPPA_INS_XOR: + op->type = RZ_ANALYSIS_OP_TYPE_XOR; + 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 -1; + } + + RzAsmHPPAContext *ctx = a->plugin_data; + if (!hppa_setup_cs_handle(ctx, a->cpu, NULL, a->big_endian)) { + return -1; + } + + op->size = 4; + + 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); + } + if (mask & RZ_ANALYSIS_OP_MASK_VAL) { + hppa_fillvals(ctx, a, op); + } + +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 4; + 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..a7ec7648d6f --- /dev/null +++ b/librz/arch/p/asm/asm_hppa_cs.c @@ -0,0 +1,123 @@ +// 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, a->big_endian)) { + return -1; + } + + op->size = 4; + + ctx->insn = NULL; + ctx->count = cs_disasm(ctx->h, buf, len, a->pc, 1, &ctx->insn); + if (ctx->count <= 0) { + RZ_LOG_ERROR("HPPA: disasm error @ 0x%08" PFMT64x ", len = %d\n", a->pc, len); + rz_asm_op_set_asm(op, "invalid"); + 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); + + op->asm_toks = rz_asm_tokenize_asm_regex(&op->buf_asm, ctx->token_patterns); + +beach: + cs_free(ctx->insn, ctx->count); + ctx->insn = NULL; + ctx->count = 0; + return op->size; +} + +#define TOKEN(_type, _pat) \ + do { \ + RzAsmTokenPattern *pat = RZ_NEW0(RzAsmTokenPattern); \ + pat->type = RZ_ASM_TOKEN_##_type; \ + pat->pattern = strdup(_pat); \ + rz_pvector_push(pvec, pat); \ + } while (0) + +static RZ_OWN RzPVector /**/ *get_token_patterns() { + RzPVector *pvec = rz_pvector_new(rz_asm_token_pattern_free); + if (!pvec) { + return NULL; + } + + TOKEN(META, "(\\[|\\]|-)"); + TOKEN(META, "(\\+[rc]?)"); + + TOKEN(NUMBER, "(0x[[:digit:]abcdef]+)"); + + TOKEN(REGISTER, "([cfstr]{1,2}[[:digit:]]{1,2})|(sp|psw|pc|rp|dp|ret0|ret1|rctr|pidr1|pidr2|pidr3|ccr|sar|iva|eiem|itmr|pcsq|pcoq|iir|isr|ior|ipsw|eirr|flags)"); + + TOKEN(MNEMONIC, "([[:alpha:]]+[[:alnum:]\\,]*([[:alpha:]]+|(<|>|=)+))"); + + TOKEN(SEPARATOR, "([[:blank:]]+)|([,;\\(\\)\\{\\}:])"); + + TOKEN(NUMBER, "([[:digit:]]+)"); + + return pvec; +} + +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; + } + ctx->token_patterns = get_token_patterns(); + rz_asm_compile_token_patterns(ctx->token_patterns); + return true; +} + +static bool fini(void *u) { + if (!u) { + return true; + } + RzAsmHPPAContext *ctx = u; + cs_close(&ctx->h); + rz_pvector_free(ctx->token_patterns); + 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_LITTLE | RZ_SYS_ENDIAN_BIG, + .cpus = "hppa1.1,hppa2.0,hppa2.0w", + .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 diff --git a/librz/bin/format/elf/elf_imports.c b/librz/bin/format/elf/elf_imports.c index 0f8b5f5c0d1..99f225c6c72 100644 --- a/librz/bin/format/elf/elf_imports.c +++ b/librz/bin/format/elf/elf_imports.c @@ -57,6 +57,30 @@ static ut64 get_import_addr_mips(ELFOBJ *bin, RzBinElfReloc *rel) { return plt_addr; } +static ut64 get_import_addr_hppa(ELFOBJ *bin, RzBinElfReloc *rel) { + ut64 plt_addr; + + if (!Elf_(rz_bin_elf_get_dt_info)(bin, DT_PLTGOT, &plt_addr)) { + return UT64_MAX; + } + + ut64 p_plt_addr = Elf_(rz_bin_elf_v2p)(bin, plt_addr); + if (p_plt_addr == UT64_MAX) { + return UT64_MAX; + } + + ut32 tmp; + if (!rz_buf_read_ble32_at(bin->b, p_plt_addr, &tmp, bin->big_endian)) { + return UT64_MAX; + } + + ut64 pos = COMPUTE_PLTGOT_POSITION(rel, plt_addr, 0x0); + ut64 base = tmp; + + base += (pos * 16); + return base; +} + /** * \brief Determines and returns the import address for the given relocation * for the Hexagon architecture. @@ -286,6 +310,8 @@ static ut64 get_import_addr_aux(ELFOBJ *bin, RzBinElfReloc *reloc) { return get_import_addr_arm(bin, reloc); case EM_MIPS: // MIPS32 BIG ENDIAN relocs return get_import_addr_mips(bin, reloc); + case EM_PARISC: + return get_import_addr_hppa(bin, reloc); case EM_RISCV: return get_import_addr_riscv(bin, reloc); case EM_SPARC: diff --git a/librz/bin/format/elf/elf_info.c b/librz/bin/format/elf/elf_info.c index ce84ab800cf..b932b29794f 100644 --- a/librz/bin/format/elf/elf_info.c +++ b/librz/bin/format/elf/elf_info.c @@ -32,7 +32,7 @@ struct class_translation { const char *name; }; -struct cpu_mips_translation { +struct cpu_arch_translation { Elf_(Word) arch; const char *name; }; @@ -229,7 +229,7 @@ static const struct class_translation class_translation_table[] = { { ELFCLASS64, "ELF64" } }; -static const struct cpu_mips_translation cpu_mips_translation_table[] = { +static const struct cpu_arch_translation cpu_mips_translation_table[] = { { EF_MIPS_ARCH_1, "mips1" }, { EF_MIPS_ARCH_2, "mips2" }, { EF_MIPS_ARCH_3, "mips3" }, @@ -241,6 +241,12 @@ static const struct cpu_mips_translation cpu_mips_translation_table[] = { { EF_MIPS_ARCH_64R2, "mips64r2" }, }; +static const struct cpu_arch_translation cpu_hppa_translation_table[] = { + { EFA_PARISC_1_0, "hppa1.0" }, + { EFA_PARISC_1_1, "hppa1.1" }, + { EFA_PARISC_2_0, "hppa2.0" }, +}; + static const struct arch_translation arch_translation_table[] = { { EM_ARC, "arc" }, { EM_ARC_A5, "arc" }, @@ -846,6 +852,18 @@ static char *get_cpu_mips(ELFOBJ *bin) { return strdup(" Unknown mips ISA"); } +static char *get_cpu_hppa(ELFOBJ *bin) { + Elf_(Word) hppa_arch = bin->ehdr.e_flags & EF_PARISC_ARCH; + + for (size_t i = 0; i < RZ_ARRAY_SIZE(cpu_hppa_translation_table); i++) { + if (hppa_arch == cpu_hppa_translation_table[i].arch) { + return strdup(cpu_hppa_translation_table[i].name); + } + } + + return strdup(" Unknown HP PARISC ISA"); +} + static bool is_elf_class64(ELFOBJ *bin) { return bin->ehdr.e_ident[EI_CLASS] == ELFCLASS64; } @@ -1470,8 +1488,13 @@ RZ_OWN char *Elf_(rz_bin_elf_get_cpu)(RZ_NONNULL ELFOBJ *bin) { return NULL; } - if (bin->ehdr.e_machine == EM_MIPS) { - return get_cpu_mips(bin); + switch (bin->ehdr.e_machine) { + case EM_MIPS: + return get_cpu_mips(bin); + case EM_PARISC: + return get_cpu_hppa(bin); + default: + break; } return NULL; diff --git a/test/db/analysis/hppa b/test/db/analysis/hppa new file mode 100644 index 00000000000..c58d73ad734 --- /dev/null +++ b/test/db/analysis/hppa @@ -0,0 +1,11 @@ +NAME=hppa analysis graph +FILE=bins/hppa/elf-Linux-hppa-bash +CMDS=<