From 174a86b02e33cf865ba4a1288f4a228bd31ec1e6 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Mon, 12 Feb 2024 02:21:51 -0500 Subject: [PATCH 1/5] Add check if plugin strcture can be freed. Add combination of flags, to make usage easier in Rust. Add rudimentary API for instruction word decoding. Implement instruction word decoding for Hexagon. Extract reading a new op into a helper function. Enable CFG generation of instruction word archs Invalidate pointer after iword was finished. Generate iword by checking HexInsnContainer Zero values of iword in fini Add flag if a packet should be assumed as valid. Set iword properties in separated function Ignore aop->fail, since it can point to the next instr. within the packet. Extend SetU - Add a getter for length of SetU. - Add a foreach macro. Add helper functions to check if RzAnalysisOp is a jump or call. Don't add duplicate nodes. Don't add duplicated edges to graph. Add track call and jump targets in a Set for iwords. Decode entry node before adding it. Add a test for iword CFG generation. Fix CFG invalid node test. Handle decoding of invalid iwords in CFG. Add the set of call targets to a CFG node of iwords. Revert "Add the set of call targets to a CFG node of iwords." This reverts commit c149237fa6f0a701b555dbd977ffb0fee2686096. Add instruction words as CFG nodes. Distinguish between subtypes of different graph node types. Add getter for log level Degrade to warning Add subtypes to single instructions in an iword CFG node. Lower logging level Check for edge duplicates when adding one. Handle insvalid instructions as EXIT nodes in a CFG (and remove duplicate code). Revert log level increasing Fix hash table init after rebase Extend SetU - Add a getter for length of SetU. - Add a foreach macro. Fix cgraph after SetU update Add binding log function for plugins without varg support. Use rz_io_read_at_mapped since it also reads bytes between mapped regions. Fix memleaks Remove diff Remove check for legal NULL condition and handle it. Fi signature. Replace rz_io_read_at with rz_io_nread_at and add note for others. Add check if function is calssified as an input function. Add missing return register role Lower allowed buffer size. Unify mapped reading from mem Allow decoding if data is read from an umapped region. Remove dot from pattern Add strict option for CFG generation. It will omit nodes outside of the function detected by Rizin. Add docs and assert Fix rebase issues Add a workaround for threads removing instructions too quickly from the packets. Add a bunch of warnings Add (breaking) tests for weird disassembly atterns. Add return register roles Mark jump nodes Mark jumps in the CFG Don't attempt iword decoding outside of map Add CFG gen over function. Fix: Don't add node outside of function to graph Label jumps, tail calls and program exits in CFGs. Fix tail calls of jumpt without known target. Bring enums ins sync --- librz/arch/fcn.c | 20 +- librz/arch/isa/hexagon/hexagon.c | 2 + librz/arch/isa/hexagon/hexagon_arch.c | 94 ++- librz/arch/isa/hexagon/hexagon_arch.h | 3 +- librz/arch/isa/hexagon/hexagon_il.c | 6 +- librz/arch/op.c | 49 ++ librz/arch/p/analysis/analysis_arm_cs.c | 4 + librz/arch/p/analysis/analysis_hexagon.c | 13 + librz/arch/p/analysis/analysis_x86_cs.c | 3 + librz/core/agraph.c | 19 +- librz/core/canalysis.c | 2 +- librz/core/cgraph.c | 445 +++++++++-- librz/core/cmd/cmd_analysis.c | 5 + librz/core/cmd_descs/cmd_analysis.yaml | 8 + librz/core/cmd_descs/cmd_descs.c | 19 + librz/core/cmd_descs/cmd_descs.h | 2 + librz/include/rz_analysis.h | 93 +++ librz/include/rz_core.h | 4 +- librz/include/rz_lib.h | 1 + librz/include/rz_util/rz_graph_drawable.h | 84 ++- librz/include/rz_util/rz_log.h | 1 + librz/io/io.c | 7 + librz/util/graph.c | 35 +- librz/util/graph_drawable.c | 159 +++- librz/util/lib.c | 2 +- librz/util/log.c | 9 + test/db/analysis/hexagon | 23 + test/db/cmd/cmd_graph | 854 +++++++++++++++++----- test/integration/test_analysis_graph.c | 12 +- test/unit/test_graph.c | 2 + test/unit/test_util.c | 1 + 31 files changed, 1659 insertions(+), 322 deletions(-) diff --git a/librz/arch/fcn.c b/librz/arch/fcn.c index 72097bd484b..6678b737f1b 100644 --- a/librz/arch/fcn.c +++ b/librz/arch/fcn.c @@ -2749,5 +2749,23 @@ RZ_API bool rz_analysis_function_is_malloc(const RzAnalysisFunction *fcn) { rz_return_val_if_fail(fcn, false); // TODO We need more metrics here. Just the name is pretty naive. // E.g. we should compare it to signatures and other characterisitics. - return rz_regex_contains(".*\\.([mc]|(re))?alloc.*", fcn->name, RZ_REGEX_ZERO_TERMINATED, RZ_REGEX_EXTENDED, RZ_REGEX_DEFAULT); + return rz_regex_contains(".*([mc]|(re))?alloc.*", fcn->name, RZ_REGEX_ZERO_TERMINATED, RZ_REGEX_EXTENDED, RZ_REGEX_DEFAULT); +} + +/** + * \brief Determines if the given function returns unpredictable input data (e.g. by the user or peripherals). + * + * The current methods of detection (tested in order): + * - Name matches regex ".*\.fread.*" + * + * \param fcn The function to test. + * + * \return true If the function \p fcn is considered an input function. + * \return false Otherwise. + */ +RZ_API bool rz_analysis_function_is_input(const RzAnalysisFunction *fcn) { + rz_return_val_if_fail(fcn, false); + // TODO We need more metrics here. Just the name is pretty naive. + // E.g. we should compare it to signatures and other characterisitics. + return rz_regex_contains(".*fread.*", fcn->name, RZ_REGEX_ZERO_TERMINATED, RZ_REGEX_EXTENDED, RZ_REGEX_DEFAULT); } diff --git a/librz/arch/isa/hexagon/hexagon.c b/librz/arch/isa/hexagon/hexagon.c index 571ed4a8577..90d0bca51ce 100644 --- a/librz/arch/isa/hexagon/hexagon.c +++ b/librz/arch/isa/hexagon/hexagon.c @@ -427,12 +427,14 @@ RZ_API const char *hex_get_reg_in_class(HexRegClass cls, int reg_num, bool get_a int resolve_n_register(const int reg_num, const ut32 addr, const HexPkt *p) { // .new values are documented in Programmers Reference Manual if (reg_num <= 1 || reg_num >= 8) { + RZ_LOG_DEBUG("n_register reg_num out of range.\n"); return UT32_MAX; } ut8 ahead = (reg_num >> 1); ut8 i = hexagon_get_pkt_index_of_addr(addr, p); if (i == UT8_MAX) { + RZ_LOG_DEBUG("Could not get n_register instruction packet index.\n"); return UT32_MAX; } diff --git a/librz/arch/isa/hexagon/hexagon_arch.c b/librz/arch/isa/hexagon/hexagon_arch.c index 80749366680..4c6fe4b840e 100644 --- a/librz/arch/isa/hexagon/hexagon_arch.c +++ b/librz/arch/isa/hexagon/hexagon_arch.c @@ -225,6 +225,7 @@ RZ_API ut8 hexagon_get_pkt_index_of_addr(const ut32 addr, const HexPkt *p) { } ++i; } + RZ_LOG_WARN("Failed to find index in packet for %" PFMT32x, addr); return UT8_MAX; } @@ -241,8 +242,6 @@ static void hex_clear_pkt(RZ_NONNULL HexPkt *p) { p->hw_loop0_addr = 0; p->hw_loop1_addr = 0; p->pkt_addr = 0; - p->last_instr_present = false; - p->is_valid = false; p->last_access = 0; rz_list_purge(p->bin); rz_pvector_clear(p->il_ops); @@ -265,6 +264,7 @@ static HexPkt *hex_get_stale_pkt(HexState *state) { stale_state_pkt = &state->pkts[i]; } } + hex_clear_pkt(stale_state_pkt); return stale_state_pkt; } @@ -291,6 +291,7 @@ RZ_API HexPkt *hex_get_pkt(RZ_BORROW HexState *state, const ut32 addr) { } } } + RZ_LOG_DEBUG("Failed to get packet at 0x%" PFMT32x, addr); return NULL; } @@ -352,6 +353,7 @@ static ut8 get_state_pkt_index(HexState *state, const HexPkt *p) { return i; } } + RZ_LOG_WARN("Failed to find state packet index"); return UT8_MAX; } @@ -891,9 +893,7 @@ static void print_state_pkt(const HexState *state, st32 index, HexBufferAction a * \return The pointer to the added instruction. Null if the instruction could not be copied. */ static HexInsnContainer *hex_add_hic_to_state(HexState *state, const HexInsnContainer *new_hic) { - if (!new_hic) { - return NULL; - } + rz_return_val_if_fail(state && new_hic, NULL); bool add_to_pkt = false; bool new_pkt = false; bool write_to_stale_pkt = false; @@ -1258,9 +1258,12 @@ static inline bool do_decoding_loop(ut64 current_addr, ut64 requested_addr, cons * \param buf The buffer which stores the current opcode. * \param addr The address of the current opcode. * \param copy_result If set, it copies the result. Otherwise it only buffers it in the internal state. + * + * \return true If the decoded instruction was the last instruction in a _valid_ packet. + * \return false Otherwise. */ -RZ_API void hexagon_reverse_opcode(HexReversedOpcode *rz_reverse, const ut64 addr, RzAsm *rz_asm, RzAnalysis *rz_analysis) { - rz_return_if_fail(rz_reverse); +RZ_API RZ_OWN HexInsnContainer *hexagon_reverse_opcode(HexReversedOpcode *rz_reverse, const ut64 addr, RzAsm *rz_asm, RzAnalysis *rz_analysis) { + rz_return_val_if_fail(rz_reverse, NULL); HexState *state; RzBuffer *buffer; perform_hacks(&state, &buffer, &rz_asm, &rz_analysis, rz_reverse); @@ -1270,7 +1273,7 @@ RZ_API void hexagon_reverse_opcode(HexReversedOpcode *rz_reverse, const ut64 add // For bytes buffers (e.g. given in case of `rz-asm`) the address is not a valid seek, but distinct. if (buffer->type == RZ_BUFFER_IO && rz_buf_seek(buffer, addr, RZ_BUF_SET) != addr) { RZ_LOG_DEBUG("Could not seek to address: 0x%" PFMT64x ". Attempting to read out of mapped memory region?\n", addr); - return; + return NULL; } ut64 current_addr = get_pre_decoding_start(buffer, addr); @@ -1309,10 +1312,83 @@ RZ_API void hexagon_reverse_opcode(HexReversedOpcode *rz_reverse, const ut64 add if (!hic) { RZ_LOG_DEBUG("Could not decode packet.\n"); rz_buf_free(buffer); - return; + return NULL; } HexPkt *p = hex_get_pkt(state, hic->addr); rz_reverse->pkt_fully_decoded = p && p->is_valid; copy_asm_ana_ops(state, rz_reverse, hic); rz_buf_free(buffer); + return hic; +} + +static void set_iword_properties(ut32 anaop_type, RzAnalysisInsnWord *iword) { + rz_return_if_fail(iword); + switch (anaop_type & ~RZ_ANALYSIS_OP_HINT_MASK) { + default: + break; + case RZ_ANALYSIS_OP_TYPE_CALL: + case RZ_ANALYSIS_OP_TYPE_UCALL: + case RZ_ANALYSIS_OP_TYPE_RCALL: + case RZ_ANALYSIS_OP_TYPE_ICALL: + case RZ_ANALYSIS_OP_TYPE_IRCALL: + case RZ_ANALYSIS_OP_TYPE_CCALL: + case RZ_ANALYSIS_OP_TYPE_UCCALL: + iword->props |= RZ_ANALYSIS_IWORD_CALL; + break; + case RZ_ANALYSIS_OP_TYPE_JMP: + case RZ_ANALYSIS_OP_TYPE_UJMP: + case RZ_ANALYSIS_OP_TYPE_RJMP: + case RZ_ANALYSIS_OP_TYPE_IJMP: + case RZ_ANALYSIS_OP_TYPE_IRJMP: + case RZ_ANALYSIS_OP_TYPE_CJMP: + case RZ_ANALYSIS_OP_TYPE_RCJMP: + case RZ_ANALYSIS_OP_TYPE_MJMP: + case RZ_ANALYSIS_OP_TYPE_MCJMP: + case RZ_ANALYSIS_OP_TYPE_UCJMP: + iword->props |= RZ_ANALYSIS_IWORD_JUMP; + break; + case RZ_ANALYSIS_OP_TYPE_RET: + iword->props |= RZ_ANALYSIS_IWORD_RET; + break; + } + + switch (anaop_type & RZ_ANALYSIS_OP_HINT_MASK) { + default: + break; + case RZ_ANALYSIS_OP_TYPE_TAIL: + iword->props |= RZ_ANALYSIS_IWORD_TAIL; + break; + case RZ_ANALYSIS_OP_TYPE_COND: + iword->props |= RZ_ANALYSIS_IWORD_COND; + break; + } +} + +RZ_API bool hexagon_decode_iword(RzAnalysis *analysis, HexReversedOpcode *rev, RZ_OUT RzAnalysisInsnWord *iword, ut64 addr) { + rz_return_val_if_fail(rev && iword, false); + iword->addr = addr; + HexInsnContainer *hic = NULL; + do { + hic = hexagon_reverse_opcode(rev, addr, NULL, analysis); + rz_pvector_push(iword->insns, rev->ana_op); + rz_strbuf_appendf(iword->asm_str, "%s\n", hic->text); + iword->size_bytes += 4; + iword->size_bits += 32; + + set_iword_properties(rev->ana_op->type, iword); + if (iword->props & RZ_ANALYSIS_IWORD_CALL && rev->ana_op->jump != UT64_MAX) { + rz_set_u_add(iword->call_targets, rev->ana_op->jump); + } else if (rev->ana_op->jump != UT64_MAX) { + rz_set_u_add(iword->jump_targets, rev->ana_op->jump); + } + + if (hic->pkt_info.last_insn) { + if (rev->ana_op->type != RZ_ANALYSIS_OP_TYPE_RET && !rz_analysis_op_is_jump(rev->ana_op)) { + ut64 next_iword_addr = addr + iword->size_bytes; + rz_set_u_add(iword->jump_targets, next_iword_addr); + } + return true; + } + } while (!hic->pkt_info.last_insn); + return true; } diff --git a/librz/arch/isa/hexagon/hexagon_arch.h b/librz/arch/isa/hexagon/hexagon_arch.h index 94b8a037a71..7e8e58151f9 100644 --- a/librz/arch/isa/hexagon/hexagon_arch.h +++ b/librz/arch/isa/hexagon/hexagon_arch.h @@ -75,7 +75,8 @@ RZ_API void hex_insn_container_free(RZ_NULLABLE HexInsnContainer *c); RZ_API void hex_const_ext_free(RZ_NULLABLE HexConstExt *ce); RZ_IPI RZ_OWN HexState *hexagon_state_new(); RZ_IPI void hexagon_state_fini(RZ_NULLABLE HexState *state); -RZ_API void hexagon_reverse_opcode(HexReversedOpcode *rz_reverse, const ut64 addr, RzAsm *rz_asm, RzAnalysis *rz_analysis); +RZ_API RZ_OWN HexInsnContainer *hexagon_reverse_opcode(HexReversedOpcode *rz_reverse, const ut64 addr, RzAsm *rz_asm, RzAnalysis *rz_analysis); +RZ_API bool hexagon_decode_iword(RzAnalysis *analysis, HexReversedOpcode *rev, RZ_OUT RzAnalysisInsnWord *iword, ut64 addr); RZ_API ut8 hexagon_get_pkt_index_of_addr(const ut32 addr, const HexPkt *p); RZ_API HexLoopAttr hex_get_loop_flag(const HexPkt *p); RZ_API const HexOp *hex_isa_to_reg(const HexInsn *hi, const char isa_id, bool new_reg); diff --git a/librz/arch/isa/hexagon/hexagon_il.c b/librz/arch/isa/hexagon/hexagon_il.c index b6d145eb179..0d6a0d56d49 100644 --- a/librz/arch/isa/hexagon/hexagon_il.c +++ b/librz/arch/isa/hexagon/hexagon_il.c @@ -208,11 +208,11 @@ RZ_IPI bool hex_shuffle_insns(RZ_INOUT HexPkt *p) { } static RzILOpEffect *hex_il_op_to_effect(const HexILOp *il_op, HexPkt *pkt) { - rz_return_val_if_fail(il_op && il_op->get_il_op, NULL); + rz_return_val_if_fail(il_op, NULL); HexInsnPktBundle bundle = { 0 }; bundle.insn = (HexInsn *)il_op->hi; bundle.pkt = pkt; - return il_op->get_il_op(&bundle); + return il_op->get_il_op ? il_op->get_il_op(&bundle) : EMPTY(); } /** @@ -228,7 +228,7 @@ static RZ_OWN RzILOpEffect *hex_pkt_to_il_seq(HexPkt *pkt) { rz_pvector_clear(pkt->il_ops); // We need at least the instruction op and the packet commit. // So if there aren't at least two ops something went wrong. - RZ_LOG_WARN("Invalid il ops sequence! There should be at least two il ops per packet.\n"); + RZ_LOG_DEBUG("Invalid il ops sequence! There should be at least two il ops per packet.\n"); return NULL; } RzILOpEffect *complete_seq = EMPTY(); diff --git a/librz/arch/op.c b/librz/arch/op.c index d703d70bdfb..a035fd16e6d 100644 --- a/librz/arch/op.c +++ b/librz/arch/op.c @@ -2,6 +2,7 @@ // SPDX-FileCopyrightText: 2010-2020 nibble // SPDX-License-Identifier: LGPL-3.0-only +#include #include #include #include @@ -691,3 +692,51 @@ RZ_API int rz_analysis_op_reg_delta(RzAnalysis *analysis, ut64 addr, const char rz_analysis_op_fini(&op); return delta; } + +RZ_API RZ_OWN RzAnalysisInsnWord *rz_analysis_insn_word_new() { + RzAnalysisInsnWord *iword = RZ_NEW0(RzAnalysisInsnWord); + if (!iword) { + return NULL; + } + iword->asm_str = rz_strbuf_new(""); + iword->insns = rz_pvector_new(rz_analysis_op_free); + iword->jump_targets = rz_set_u_new(); + iword->call_targets = rz_set_u_new(); + if (!iword->asm_str || !iword->insns || !iword->jump_targets) { + rz_analysis_insn_word_free(iword); + return NULL; + } + return iword; +} + +RZ_API void rz_analysis_insn_word_free(RZ_OWN RZ_NULLABLE RzAnalysisInsnWord *iword) { + if (!iword) { + return; + } + rz_analysis_insn_word_fini(iword); + free(iword); +} + +RZ_API void rz_analysis_insn_word_setup(RZ_BORROW RZ_NONNULL RzAnalysisInsnWord *iword) { + rz_return_if_fail(iword); + rz_analysis_insn_word_fini(iword); + iword->asm_str = rz_strbuf_new(""); + iword->insns = rz_pvector_new(rz_analysis_op_free); + iword->jump_targets = rz_set_u_new(); + iword->call_targets = rz_set_u_new(); + if (!iword->asm_str || !iword->insns || !iword->jump_targets) { + rz_analysis_insn_word_fini(iword); + } +} + +RZ_API void rz_analysis_insn_word_fini(RZ_OWN RZ_NULLABLE RzAnalysisInsnWord *iword) { + if (!iword) { + return; + } + rz_strbuf_free(iword->asm_str); + rz_pvector_free(iword->insns); + rz_set_u_free(iword->jump_targets); + rz_set_u_free(iword->call_targets); + rz_il_op_effect_free(iword->il_op); + rz_mem_memzero(iword, sizeof(RzAnalysisInsnWord)); +} diff --git a/librz/arch/p/analysis/analysis_arm_cs.c b/librz/arch/p/analysis/analysis_arm_cs.c index 4a2cee72a14..b98832fc28a 100644 --- a/librz/arch/p/analysis/analysis_arm_cs.c +++ b/librz/arch/p/analysis/analysis_arm_cs.c @@ -2181,6 +2181,8 @@ static char *get_reg_profile(RzAnalysis *analysis) { "=A1 x1\n" "=A2 x2\n" "=A3 x3\n" + "=R0 x0\n" + "=R1 x1\n" "=ZF zf\n" "=SF nf\n" "=OF vf\n" @@ -2411,6 +2413,8 @@ static char *get_reg_profile(RzAnalysis *analysis) { "=A1 r1\n" "=A2 r2\n" "=A3 r3\n" + "=R0 r0\n" + "=R1 r1\n" "=ZF zf\n" "=SF nf\n" "=OF vf\n" diff --git a/librz/arch/p/analysis/analysis_hexagon.c b/librz/arch/p/analysis/analysis_hexagon.c index 7949059260b..24c0ce090e8 100644 --- a/librz/arch/p/analysis/analysis_hexagon.c +++ b/librz/arch/p/analysis/analysis_hexagon.c @@ -38,6 +38,18 @@ RZ_API int hexagon_v6_op(RzAnalysis *analysis, RzAnalysisOp *op, ut64 addr, cons return HEX_INSN_SIZE; } +RZ_API bool rz_hexagon_decode_iword(RzAnalysis *a, RZ_OUT RzAnalysisInsnWord *iword, ut64 addr, const ut8 *buf, size_t len, size_t buf_off_iword) { + rz_return_val_if_fail(a && iword && buf, false); + + RzAnalysisOp aop = { 0 }; + HexReversedOpcode rev = { .action = HEXAGON_ANALYSIS, .ana_op = &aop, .asm_op = NULL, .state = NULL, .pkt_fully_decoded = false, .bytes_buf = buf, .bytes_buf_len = len }; + bool success = hexagon_decode_iword(a, &rev, iword, addr); + if (success) { + iword->il_op = hex_get_il_op(addr, true, rev.state); + } + return success; +} + static RzAnalysisILConfig *rz_hexagon_il_config(RzAnalysis *a) { rz_return_val_if_fail(a, NULL); // Hacky getter for the plugin data until RzArch is implemented @@ -749,4 +761,5 @@ RzAnalysisPlugin rz_analysis_plugin_hexagon = { .esil = false, .get_reg_profile = get_reg_profile, .il_config = rz_hexagon_il_config, + .decode_iword = rz_hexagon_decode_iword, }; diff --git a/librz/arch/p/analysis/analysis_x86_cs.c b/librz/arch/p/analysis/analysis_x86_cs.c index 14f0924b99a..c49401accbf 100644 --- a/librz/arch/p/analysis/analysis_x86_cs.c +++ b/librz/arch/p/analysis/analysis_x86_cs.c @@ -3259,6 +3259,7 @@ static char *get_reg_profile(RzAnalysis *analysis) { "=PC ip\n" "=SP sp\n" "=BP bp\n" + "=R0 ax\n" "=A0 ax\n" "=A1 bx\n" "=A2 cx\n" @@ -3314,6 +3315,7 @@ static char *get_reg_profile(RzAnalysis *analysis) { "=PC eip\n" "=SP esp\n" "=BP ebp\n" + "=R0 eax\n" "=A0 eax\n" "=A1 ebx\n" "=A2 ecx\n" @@ -3450,6 +3452,7 @@ static char *get_reg_profile(RzAnalysis *analysis) { "=PC rip\n" "=SP rsp\n" "=BP rbp\n" + "=R0 rax\n" "=A0 rdi\n" "=A1 rsi\n" "=A2 rdx\n" diff --git a/librz/core/agraph.c b/librz/core/agraph.c index 690db959aee..e99b64a0ffd 100644 --- a/librz/core/agraph.c +++ b/librz/core/agraph.c @@ -3709,10 +3709,19 @@ RZ_API RZ_BORROW RzANode *rz_agraph_add_node_from_node_info(RZ_NONNULL const RzA } an->offset = info->def.offset; break; - case RZ_GRAPH_NODE_TYPE_CFG: { - char *annotation = rz_graph_get_node_subtype_annotation(info->subtype, utf8); + case RZ_GRAPH_NODE_TYPE_CFG: + case RZ_GRAPH_NODE_TYPE_CFG_IWORD: { + char *annotation = NULL; + ut64 addr = 0; + if (info->type == RZ_GRAPH_NODE_TYPE_CFG) { + annotation = rz_graph_get_node_subtype_annotation_cfg(info->cfg.subtype, true, utf8); + addr = info->cfg.address; + } else { + annotation = rz_graph_get_node_subtype_annotation_cfg_iword(info->cfg_iword.subtype, true, utf8); + addr = info->cfg_iword.address; + } rz_return_val_if_fail(annotation, NULL); - char *cfg_title = rz_str_appendf(NULL, "0x%" PFMT64x "%s", info->cfg.address, annotation); + char *cfg_title = rz_str_appendf(NULL, "0x%" PFMT64x "%s", addr, annotation); rz_return_val_if_fail(cfg_title, NULL); an = rz_agraph_add_node(g, cfg_title, ""); free(annotation); @@ -3720,12 +3729,12 @@ RZ_API RZ_BORROW RzANode *rz_agraph_add_node_from_node_info(RZ_NONNULL const RzA if (!an) { return NULL; } - an->offset = info->cfg.address; + an->offset = addr; break; } case RZ_GRAPH_NODE_TYPE_ICFG: rz_strf(title, "0x%" PFMT64x "%s", info->icfg.address, - info->subtype & RZ_GRAPH_NODE_SUBTYPE_ICFG_MALLOC ? " (alloc)" : ""); + info->icfg.subtype & RZ_GRAPH_NODE_SUBTYPE_ICFG_MALLOC ? " (alloc)" : ""); an = rz_agraph_add_node(g, title, ""); if (!an) { return NULL; diff --git a/librz/core/canalysis.c b/librz/core/canalysis.c index b729a9a6d00..0e3f76afd74 100644 --- a/librz/core/canalysis.c +++ b/librz/core/canalysis.c @@ -1052,7 +1052,7 @@ RZ_API RzAnalysisOp *rz_core_analysis_op(RzCore *core, ut64 addr, int mask) { goto err_op; } } else { - if (!rz_io_read_at(core->io, addr, buf, sizeof(buf))) { + if (!rz_io_nread_at(core->io, addr, buf, sizeof(buf))) { goto err_op; } ptr = buf; diff --git a/librz/core/cgraph.c b/librz/core/cgraph.c index 041897bd3ad..e9d78e6c347 100644 --- a/librz/core/cgraph.c +++ b/librz/core/cgraph.c @@ -9,6 +9,8 @@ #include #include #include "core_private.h" +#include +#include #include #include #include @@ -502,7 +504,20 @@ RZ_API RZ_OWN RzGraph /**/ *rz_core_graph(RzCore *core, RzCor graph = rz_core_graph_icfg(core); break; case RZ_CORE_GRAPH_TYPE_CFG: - graph = rz_core_graph_cfg(core, addr); + if (core->analysis->cur && core->analysis->cur->decode_iword) { + // Build the instruction word graph. + graph = rz_core_graph_cfg_iwords(core, addr, false); + } else { + graph = rz_core_graph_cfg(core, addr, false); + } + break; + case RZ_CORE_GRAPH_TYPE_CFG_FCN: + if (core->analysis->cur && core->analysis->cur->decode_iword) { + // Build the instruction word graph. + graph = rz_core_graph_cfg_iwords(core, addr, true); + } else { + graph = rz_core_graph_cfg(core, addr, true); + } break; case RZ_CORE_GRAPH_TYPE_DIFF: default: @@ -798,9 +813,9 @@ static RzGraphNode *rz_graph_add_node_info_icfg(RzGraph /**/ rz_return_val_if_fail(graph, NULL); RzGraphNodeInfo *data = NULL; if (rz_analysis_function_is_malloc(fcn)) { - data = rz_graph_create_node_info_icfg(fcn->addr, RZ_GRAPH_NODE_TYPE_ICFG, RZ_GRAPH_NODE_SUBTYPE_ICFG_MALLOC); + data = rz_graph_create_node_info_icfg(fcn->addr, RZ_GRAPH_NODE_SUBTYPE_ICFG_MALLOC); } else { - data = rz_graph_create_node_info_icfg(fcn->addr, RZ_GRAPH_NODE_TYPE_ICFG, RZ_GRAPH_NODE_SUBTYPE_NONE); + data = rz_graph_create_node_info_icfg(fcn->addr, RZ_GRAPH_NODE_SUBTYPE_ICFG_NONE); } if (!data) { rz_warn_if_reached(); @@ -850,7 +865,8 @@ static void extend_icfg(const RzAnalysis *analysis, RZ_BORROW RzGraph /*type != RZ_ANALYSIS_XREF_TYPE_CALL) { continue; } @@ -860,6 +876,7 @@ static void extend_icfg(const RzAnalysis *analysis, RZ_BORROW RzGraph /**/ *rz_core_graph_icfg(RZ_NONNULL RzC return graph; } -static inline bool is_leaf_op(const RzAnalysisOp *op) { - return (op->type & RZ_ANALYSIS_OP_TYPE_MASK) == RZ_ANALYSIS_OP_TYPE_ILL || - (op->type & RZ_ANALYSIS_OP_TYPE_MASK) == RZ_ANALYSIS_OP_TYPE_RET || - (op->type & RZ_ANALYSIS_OP_TYPE_MASK) == RZ_ANALYSIS_OP_TYPE_UNK; -} - static inline bool is_call(const RzAnalysisOp *op) { _RzAnalysisOpType type = (op->type & RZ_ANALYSIS_OP_TYPE_MASK); return type == RZ_ANALYSIS_OP_TYPE_CALL || @@ -917,11 +929,34 @@ static inline bool is_call(const RzAnalysisOp *op) { type == RZ_ANALYSIS_OP_TYPE_UCCALL; } +static inline bool is_tail(const RzAnalysisOp *op) { + return op->type & RZ_ANALYSIS_OP_TYPE_TAIL; +} + +static inline bool is_jump(const RzAnalysisOp *op) { + _RzAnalysisOpType type = (op->type & RZ_ANALYSIS_OP_TYPE_MASK); + return type == RZ_ANALYSIS_OP_TYPE_JMP || + type == RZ_ANALYSIS_OP_TYPE_UJMP || + type == RZ_ANALYSIS_OP_TYPE_RJMP || + type == RZ_ANALYSIS_OP_TYPE_IJMP || + type == RZ_ANALYSIS_OP_TYPE_IRJMP || + type == RZ_ANALYSIS_OP_TYPE_CJMP || + type == RZ_ANALYSIS_OP_TYPE_RCJMP || + type == RZ_ANALYSIS_OP_TYPE_MJMP || + type == RZ_ANALYSIS_OP_TYPE_MCJMP || + type == RZ_ANALYSIS_OP_TYPE_UCJMP; +} + static inline bool is_uncond_jump(const RzAnalysisOp *op) { - return (op->type & RZ_ANALYSIS_OP_TYPE_MASK) == RZ_ANALYSIS_OP_TYPE_JMP && + ut32 op_type = op->type & RZ_ANALYSIS_OP_TYPE_MASK; + return (op_type == RZ_ANALYSIS_OP_TYPE_JMP || op_type == RZ_ANALYSIS_OP_TYPE_UJMP) && !((op->type & RZ_ANALYSIS_OP_HINT_MASK) & RZ_ANALYSIS_OP_TYPE_COND); } +static inline bool is_invalid(const RzAnalysisOp *op) { + return (op->type & RZ_ANALYSIS_OP_TYPE_MASK) == RZ_ANALYSIS_OP_TYPE_ILL; +} + static inline bool is_return(const RzAnalysisOp *op) { return (op->type & RZ_ANALYSIS_OP_TYPE_MASK) == RZ_ANALYSIS_OP_TYPE_RET; } @@ -930,34 +965,99 @@ static inline bool is_cond(const RzAnalysisOp *op) { return (op->type & RZ_ANALYSIS_OP_HINT_MASK) == RZ_ANALYSIS_OP_TYPE_COND; } +static inline bool is_exit(const RzAnalysisOp *op) { + return (op->type & RZ_ANALYSIS_OP_TYPE_MASK) == RZ_ANALYSIS_OP_TYPE_ILL; +} + +static inline bool is_leaf_op(const RzAnalysisOp *op) { + return is_return(op) || is_exit(op); +} + + static inline bool ignore_next_instr(const RzAnalysisOp *op) { // Ignore if: - return is_uncond_jump(op) || (op->fail != UT64_MAX && !is_call(op)); // Except calls, everything which has set fail + return is_uncond_jump(op) || (op->fail != UT64_MAX && !is_call(op)) || is_invalid(op); // Except calls, everything which has set fail } -static RzGraphNodeSubType get_cfg_node_flags(const RzAnalysisOp *op) { - rz_return_val_if_fail(op, RZ_GRAPH_NODE_SUBTYPE_NONE); - RzGraphNodeSubType subtype = RZ_GRAPH_NODE_SUBTYPE_NONE; +static RzGraphNodeCFGSubType get_cfg_node_flags(const RzAnalysisOp *op, bool is_entry) { + rz_return_val_if_fail(op, RZ_GRAPH_NODE_SUBTYPE_CFG_NONE); + RzGraphNodeCFGSubType subtype = RZ_GRAPH_NODE_SUBTYPE_CFG_NONE; + if (is_entry) { + subtype |= RZ_GRAPH_NODE_SUBTYPE_CFG_ENTRY; + } if (is_call(op)) { subtype |= RZ_GRAPH_NODE_SUBTYPE_CFG_CALL; } + if (is_tail(op)) { + subtype |= RZ_GRAPH_NODE_SUBTYPE_CFG_TAIL; + } + if (is_jump(op)) { + subtype |= RZ_GRAPH_NODE_SUBTYPE_CFG_JUMP; + } if (is_return(op)) { subtype |= RZ_GRAPH_NODE_SUBTYPE_CFG_RETURN; } if (is_cond(op)) { subtype |= RZ_GRAPH_NODE_SUBTYPE_CFG_COND; } + if (is_exit(op)) { + subtype |= RZ_GRAPH_NODE_SUBTYPE_CFG_EXIT; + } return subtype; } +static RzGraphNodeCFGIWordSubType get_cfg_iword_node_flags(const RzAnalysisInsnWord *iword) { + rz_return_val_if_fail(iword, RZ_GRAPH_NODE_SUBTYPE_CFG_IWORD_NONE); + RzGraphNodeCFGIWordSubType subtype = RZ_GRAPH_NODE_SUBTYPE_CFG_IWORD_NONE; + if (iword->props & RZ_ANALYSIS_IWORD_RET) { + subtype |= RZ_GRAPH_NODE_SUBTYPE_CFG_IWORD_RETURN; + } + if (iword->props & RZ_ANALYSIS_IWORD_COND) { + subtype |= RZ_GRAPH_NODE_SUBTYPE_CFG_IWORD_COND; + } + if (iword->props & RZ_ANALYSIS_IWORD_TAIL) { + subtype |= RZ_GRAPH_NODE_SUBTYPE_CFG_IWORD_TAIL; + } + return subtype; +} + +/** + * \brief Initializes a instruction word node info struct of a CFG node. + * + * \param iword The instructoin word to build the node from. + * \param subtype The sub types of the node. + * + * \return The initialized RzGraphNodeInfo or NULL in case of failure. + */ +static RzGraphNodeInfo *rz_graph_create_node_info_cfg_iword(const RzAnalysisInsnWord *iword, RzGraphNodeCFGIWordSubType subtype) { + RzGraphNodeInfo *data = RZ_NEW0(RzGraphNodeInfo); + rz_graph_node_info_data_cfg_iword_init(&data->cfg_iword); + data->type = RZ_GRAPH_NODE_TYPE_CFG_IWORD; + data->cfg_iword.subtype = subtype; + data->cfg_iword.address = iword->addr; + bool is_entry = subtype & RZ_GRAPH_NODE_SUBTYPE_CFG_IWORD_ENTRY; + void **it; + rz_pvector_foreach (iword->insns, it) { + const RzAnalysisOp *op = *it; + RzGraphNodeInfoDataCFG *info = RZ_NEW0(RzGraphNodeInfoDataCFG); + info->address = op->addr; + info->call_address = (rz_analysis_op_is_call(op) || rz_analysis_op_is_ccall(op)) ? op->jump : UT64_MAX; + info->jump_address = is_jump(op) ? op->jump : UT64_MAX; + info->next = is_return(op) || is_uncond_jump(op) || is_tail(op) ? UT64_MAX : op->addr + op->size; + info->subtype = get_cfg_node_flags(op, is_entry); + rz_pvector_push(data->cfg_iword.insn, info); + is_entry = false; + } + return data; +} + static RzGraphNode *add_node_info_cfg(RzGraph /**/ *cfg, const RzAnalysisOp *op, bool is_entry) { rz_return_val_if_fail(cfg, NULL); - RzGraphNodeSubType subtype = get_cfg_node_flags(op); - if (is_entry) { - subtype |= RZ_GRAPH_NODE_SUBTYPE_CFG_ENTRY; - } + RzGraphNodeCFGSubType subtype = get_cfg_node_flags(op, is_entry); ut64 call_target = is_call(op) ? op->jump : UT64_MAX; - RzGraphNodeInfo *data = rz_graph_create_node_info_cfg(op->addr, call_target, RZ_GRAPH_NODE_TYPE_CFG, subtype); + ut64 jump_target = rz_analysis_op_is_jump(op) ? op->jump : UT64_MAX; + ut64 next = is_return(op) || is_uncond_jump(op) ? UT64_MAX : op->addr + op->size; + RzGraphNodeInfo *data = rz_graph_create_node_info_cfg(op->addr, call_target, jump_target, next, subtype); if (!data) { return NULL; } @@ -976,6 +1076,8 @@ static RzGraphNode *add_node_info_cfg(RzGraph /**/ *cfg, cons * \param nodes_visited The hash table holding already visited addresses and their node indices in the graph. * \param op_from The RzAnalysisOp the edge originates from. * \param op_to The RzAnalysisOp the edge goes to. + * \param fcn Check the function if it contains \p op_to. \p op_to will not be added to \p to_visit, if it is not within the function. + * If \p fcn is NULL it assumes \p op_to is always added, if it wasn't before. * * \return true On success. * \return false On failure. @@ -984,10 +1086,14 @@ static bool add_edge_to_cfg(RZ_NONNULL RzGraph /**/ *graph, RZ_NONNULL RzVector /**/ *to_visit, RZ_NONNULL HtUU *nodes_visited, const RzAnalysisOp *op_from, - const RzAnalysisOp *op_to) { + const RzAnalysisOp *op_to, + bool to_node_within_fcn) { rz_return_val_if_fail(graph && to_visit && nodes_visited && op_from && op_to, -1); ut64 from = op_from->addr; ut64 to = op_to->addr; + if (!to_node_within_fcn) { + return true; + } bool visited = false; ut64 from_idx = ht_uu_find(nodes_visited, from, &visited); if (!visited && from != to) { @@ -1023,21 +1129,86 @@ static bool add_edge_to_cfg(RZ_NONNULL RzGraph /**/ *graph, return true; } +static bool read_aligned_to_mapped(RzIO *io, ut64 addr, ut8 *buf, size_t *buf_len, size_t leading_bytes) { + if (!rz_io_addr_is_mapped(io, addr)) { + // Invalid read, but it will fill the buffer with invalid data. + rz_io_read_at_mapped(io, addr - leading_bytes, buf, *buf_len); + return false; + } + // Align the address for decoding start to a mapped region. + while (!rz_io_addr_is_mapped(io, addr - leading_bytes) && addr - leading_bytes < addr) { + leading_bytes -= 1; + } + // Align the end of the buffer read to a mapped region. + while (!rz_io_addr_is_mapped(io, addr + *buf_len) && addr + *buf_len > addr) { + *buf_len -= 1; + } + rz_io_read_at_mapped(io, addr - leading_bytes, buf, *buf_len); + return true; +} + +static st32 decode_op_at(RZ_BORROW RzCore *core, + ut64 addr, + RZ_BORROW ut8 *buf, + size_t buf_len, + RZ_OUT RzAnalysisOp *target_op) { + rz_return_val_if_fail(core && core->analysis && core->io && buf, -1); + size_t bl = buf_len; + if (!read_aligned_to_mapped(core->io, addr, buf, &bl, 0)) { + RZ_LOG_ERROR("read_aligned_to_mapped() read from unmapped region at 0x%" PFMT64x ".\n", addr); + } + int disas_bytes = rz_analysis_op(core->analysis, target_op, addr, buf, bl, RZ_ANALYSIS_OP_MASK_DISASM); + if (disas_bytes <= 0 && target_op->type == RZ_ANALYSIS_OP_TYPE_ILL) { + // Illegal instruction, return something positive + return 1; + } + return disas_bytes; +} + +static void assign_tails_exits(RZ_BORROW RzGraph *graph, HtUU *nodes_visited, const RzAnalysisOp *ana_op) { + rz_return_if_fail(graph && ana_op); + bool found = false; + ut64 addr = ana_op->addr; + ut64 node_idx = ht_uu_find(nodes_visited, addr, &found); + RzGraphNode *node = rz_graph_get_node(graph, node_idx); + rz_return_if_fail(node && found); + RzGraphNodeInfo *data = node->data; + if (is_call(ana_op)) { + // Calls an exit procedure like abort, stack_chk_fail etc. + data->cfg.subtype |= RZ_GRAPH_NODE_SUBTYPE_CFG_EXIT; + } else if (is_jump(ana_op)) { + // tail call + data->cfg.subtype |= RZ_GRAPH_NODE_SUBTYPE_CFG_TAIL; + } +} + +static inline bool tail_exit_candidate(bool to_node_within_fcn, bool next_within_fcn, const RzAnalysisOp *curr_op) { + return (is_call(curr_op) && !next_within_fcn) || (!to_node_within_fcn && is_jump(curr_op) && !is_cond(curr_op)); +} + /** * \brief Get the procedual control flow graph (CFG) at an address. * Calls are not followed. * * \param core The current core. * \param addr The CFG entry point. + * \param within_fcn If true, only nodes within the function at \p addr are added to the CFG. * * \return The CFG at address \p addr or NULL in case of failure. */ -RZ_API RZ_OWN RzGraph /**/ *rz_core_graph_cfg(RZ_NONNULL RzCore *core, ut64 addr) { +RZ_API RZ_OWN RzGraph /**/ *rz_core_graph_cfg(RZ_NONNULL RzCore *core, ut64 addr, bool within_fcn) { rz_return_val_if_fail(core && core->analysis && core->io, NULL); RzGraph *graph = rz_graph_new(); if (!graph) { return NULL; } + RzAnalysisFunction *fcn = rz_analysis_get_function_at(core->analysis, addr); + if (within_fcn && !fcn) { + RZ_LOG_WARN("Cannot generate CFG for 0x%" PFMT64x ". addr doesn't point to a function entrypoint.", addr); + return NULL; + } else if (!within_fcn) { + fcn = NULL; + } // Visited instructions. Indexed by instruction address, value is index in graph. HtUU *nodes_visited = ht_uu_new(); @@ -1046,14 +1217,13 @@ RZ_API RZ_OWN RzGraph /**/ *rz_core_graph_cfg(RZ_NONNULL RzCo // Add entry node ut8 buf[64] = { 0 }; - if (rz_io_nread_at(core->io, addr, buf, sizeof(buf)) < 0) { - RZ_LOG_ERROR("Could not generate CFG at 0x%" PFMT64x ". rz_io_nread_at() failed at 0x%" PFMT64x ".\n", addr, addr); - goto error; - } RzAnalysisOp curr_op = { 0 }; RzAnalysisOp target_op = { 0 }; - int disas_bytes = rz_analysis_op(core->analysis, &curr_op, addr, buf, sizeof(buf), RZ_ANALYSIS_OP_MASK_DISASM); + st32 disas_bytes = decode_op_at(core, addr, buf, sizeof(buf), &curr_op); RzGraphNode *entry = add_node_info_cfg(graph, &curr_op, true); + if (disas_bytes <= 0) { + goto fini; + } ht_uu_insert(nodes_visited, addr, entry->idx); rz_vector_push(to_visit, &addr); @@ -1061,63 +1231,58 @@ RZ_API RZ_OWN RzGraph /**/ *rz_core_graph_cfg(RZ_NONNULL RzCo ut64 cur_addr = 0; rz_vector_pop(to_visit, &cur_addr); - if (rz_io_nread_at(core->io, cur_addr, buf, sizeof(buf)) < 0) { - RZ_LOG_ERROR("Could not generate CFG at 0x%" PFMT64x ". rz_io_nread_at() failed at 0x%" PFMT64x ".\n", addr, cur_addr); - goto error; - } - - disas_bytes = rz_analysis_op(core->analysis, &curr_op, cur_addr, buf, sizeof(buf), RZ_ANALYSIS_OP_MASK_DISASM); + disas_bytes = decode_op_at(core, cur_addr, buf, sizeof(buf), &curr_op); if (disas_bytes <= 0 || is_leaf_op(&curr_op)) { // A leaf. It was added before to the graph by the parent node. rz_analysis_op_fini(&curr_op); continue; } - if (curr_op.jump != UT64_MAX && !is_call(&curr_op)) { - if (rz_io_nread_at(core->io, curr_op.jump, buf, sizeof(buf)) < 0) { - RZ_LOG_ERROR("Could not generate CFG at 0x%" PFMT64x ". rz_io_nread_at() failed at 0x%" PFMT64x ".\n", addr, cur_addr); - goto error; - } - if (rz_analysis_op(core->analysis, &target_op, curr_op.jump, buf, sizeof(buf), RZ_ANALYSIS_OP_MASK_DISASM) <= 0) { + bool to_node_within_fcn = true; + bool add_jump = curr_op.jump != UT64_MAX && !is_call(&curr_op); + bool add_fail = curr_op.fail != UT64_MAX && !is_call(&curr_op); + if (add_jump) { + if (decode_op_at(core, curr_op.jump, buf, sizeof(buf), &target_op) <= 0) { rz_analysis_op_fini(&target_op); goto error; } - if (!add_edge_to_cfg(graph, to_visit, nodes_visited, &curr_op, &target_op)) { + to_node_within_fcn = fcn ? rz_analysis_function_contains(fcn, target_op.addr) : true; + if (!add_edge_to_cfg(graph, to_visit, nodes_visited, &curr_op, &target_op, to_node_within_fcn)) { goto error; } rz_analysis_op_fini(&target_op); } - if (curr_op.fail != UT64_MAX && !is_call(&curr_op)) { - if (rz_io_nread_at(core->io, curr_op.fail, buf, sizeof(buf)) < 0) { - RZ_LOG_ERROR("Could not generate CFG at 0x%" PFMT64x ". rz_io_nread_at() failed at 0x%" PFMT64x ".\n", addr, cur_addr); - goto error; - } - if (rz_analysis_op(core->analysis, &target_op, curr_op.fail, buf, sizeof(buf), RZ_ANALYSIS_OP_MASK_DISASM) <= 0) { + if (add_fail) { + if (decode_op_at(core, curr_op.fail, buf, sizeof(buf), &target_op) <= 0) { rz_analysis_op_fini(&target_op); goto error; } - if (!add_edge_to_cfg(graph, to_visit, nodes_visited, &curr_op, &target_op)) { + to_node_within_fcn = fcn ? rz_analysis_function_contains(fcn, target_op.addr) : true; + if (!add_edge_to_cfg(graph, to_visit, nodes_visited, &curr_op, &target_op, to_node_within_fcn)) { goto error; } rz_analysis_op_fini(&target_op); } + ut64 next_addr = cur_addr + disas_bytes; + bool next_within_fcn = fcn ? rz_analysis_function_contains(fcn, next_addr) : true; + if (within_fcn && !next_within_fcn && tail_exit_candidate(to_node_within_fcn && add_jump, next_within_fcn, &curr_op)) { + assign_tails_exits(graph, nodes_visited, &curr_op); + rz_analysis_op_fini(&curr_op); + continue; + } + if (ignore_next_instr(&curr_op)) { rz_analysis_op_fini(&curr_op); continue; } // Add next instruction - ut64 next_addr = cur_addr + disas_bytes; - if (rz_io_nread_at(core->io, next_addr, buf, sizeof(buf)) < 0) { - RZ_LOG_ERROR("Could not generate CFG at 0x%" PFMT64x ". rz_io_nread_at() failed at 0x%" PFMT64x ".\n", addr, cur_addr); - goto error; - } - if (rz_analysis_op(core->analysis, &target_op, next_addr, buf, sizeof(buf), RZ_ANALYSIS_OP_MASK_DISASM) <= 0) { + if (decode_op_at(core, next_addr, buf, sizeof(buf), &target_op) <= 0) { rz_analysis_op_fini(&target_op); goto error; } - if (!add_edge_to_cfg(graph, to_visit, nodes_visited, &curr_op, &target_op)) { + if (!add_edge_to_cfg(graph, to_visit, nodes_visited, &curr_op, &target_op, to_node_within_fcn)) { goto error; } rz_analysis_op_fini(&target_op); @@ -1135,3 +1300,175 @@ RZ_API RZ_OWN RzGraph /**/ *rz_core_graph_cfg(RZ_NONNULL RzCo graph = NULL; goto fini; } + +static RzGraphNode *add_iword_to_cfg(RzGraph /**/ *cfg, const RzAnalysisInsnWord *iword, bool is_entry) { + rz_return_val_if_fail(cfg, NULL); + RzGraphNodeCFGIWordSubType subtype = get_cfg_iword_node_flags(iword); + if (is_entry) { + subtype |= RZ_GRAPH_NODE_SUBTYPE_CFG_IWORD_ENTRY; + } + RzGraphNodeInfo *data = rz_graph_create_node_info_cfg_iword(iword, subtype); + if (!data) { + return NULL; + } + RzGraphNode *node = rz_graph_add_nodef(cfg, data, rz_graph_free_node_info); + if (!node) { + rz_graph_free_node_info(data); + } + return node; +} + +static bool add_iword_edge_to_cfg(RZ_NONNULL RzGraph /**/ *graph, + RZ_NONNULL RzVector /**/ *to_visit, + RZ_NONNULL HtUU *nodes_visited, + const RzAnalysisInsnWord *irowrd_from, + const RzAnalysisInsnWord *iword_to, + bool to_node_in_fcn) { + if (!to_node_in_fcn) { + return true; + } + rz_return_val_if_fail(graph && to_visit && nodes_visited && irowrd_from && iword_to, -1); + ut64 from = irowrd_from->addr; + ut64 to = iword_to->addr; + bool visited = false; + ut64 from_idx = ht_uu_find(nodes_visited, from, &visited); + if (!visited && from != to) { + RZ_LOG_ERROR("'from' node should have been added before. 0x%" PFMT64x " -> 0x%" PFMT64x "\n", from, to); + return false; + } + ut64 to_idx = ht_uu_find(nodes_visited, to, &visited); + + RzGraphNode *to_node = NULL; + if (visited) { + to_node = rz_graph_get_node(graph, to_idx); + } else { + to_node = add_iword_to_cfg(graph, iword_to, false); + } + if (!to_node) { + RZ_LOG_ERROR("Could not add node at 0x%" PFMT64x "\n", to); + return false; + } + to_idx = to_node->idx; + if (from == to) { + from_idx = to_idx; + } + + if (from != to && !visited) { + // The target node wasn't visited before. Otherwise this is a back-edge. + rz_vector_push(to_visit, &to); + } + + ht_uu_insert(nodes_visited, to, to_node->idx); + if (!rz_graph_adjacent(graph, rz_graph_get_node(graph, from_idx), to_node)) { + rz_graph_add_edge(graph, rz_graph_get_node(graph, from_idx), to_node); + } + return true; +} + +static st32 decode_iword_at(RZ_BORROW RzCore *core, + ut64 addr, + RZ_BORROW ut8 *buf, + size_t buf_len, + RZ_OUT RzAnalysisInsnWord *target_iword) { + rz_return_val_if_fail(core && core->analysis && core->io && buf && core->analysis->cur && core->analysis->cur->decode_iword, -1); + size_t leading_bytes = addr < 8 ? addr : 8; + size_t bl = buf_len; + if (!read_aligned_to_mapped(core->io, addr, buf, &bl, leading_bytes)) { + RZ_LOG_ERROR("read_aligned_to_mapped() read from unmapped region at 0x%" PFMT64x ".\n", addr); + return -1; + } + bool success = core->analysis->cur->decode_iword(core->analysis, target_iword, addr, buf, bl, leading_bytes); + return success ? target_iword->size_bytes : -1; +} + +/** + * \brief Get the procedual control flow graph (CFG) of instruction words at an address. + * Calls are not followed. + * + * \param core The current core. + * \param addr The CFG entry point. + * \param within_fcn If true, only nodes within the function at \p addr are added to the CFG. + * + * \return The CFG at address \p addr or NULL in case of failure. + */ +RZ_API RZ_OWN RzGraph /**/ *rz_core_graph_cfg_iwords(RZ_NONNULL RzCore *core, ut64 addr, bool within_fcn) { + rz_return_val_if_fail(core && core->analysis && core->io, NULL); + RzGraph *graph = rz_graph_new(); + if (!graph) { + return NULL; + } + + RzAnalysisFunction *fcn = rz_analysis_get_function_at(core->analysis, addr); + if (within_fcn && !fcn) { + RZ_LOG_WARN("Cannot generate CFG for 0x%" PFMT64x ". addr doesn't point to a function entrypoint.", addr); + return NULL; + } else if (!within_fcn) { + fcn = NULL; + } + + // Visited instructions. Indexed by instruction address, value is index in graph. + HtUU *nodes_visited = ht_uu_new(); + // Addresses to visit. + RzVector *to_visit = rz_vector_new(sizeof(ut64), NULL, NULL); + + // Add entry node + ut8 buf[64] = { 0 }; + RzAnalysisInsnWord cur_iword = { 0 }; + rz_analysis_insn_word_setup(&cur_iword); + + st32 disas_bytes = decode_iword_at(core, addr, buf, sizeof(buf), &cur_iword); + RzGraphNode *entry = add_iword_to_cfg(graph, &cur_iword, true); + if (disas_bytes <= 0) { + goto fini; + } + ht_uu_insert(nodes_visited, addr, entry->idx); + rz_vector_push(to_visit, &addr); + + while (rz_vector_len(to_visit) > 0) { + ut64 cur_addr = 0; + rz_vector_pop(to_visit, &cur_addr); + rz_analysis_insn_word_setup(&cur_iword); + + disas_bytes = decode_iword_at(core, cur_addr, buf, sizeof(buf), &cur_iword); + if (disas_bytes <= 0) { + // If the decoding was invalid we do not add it to the graph. + rz_analysis_insn_word_fini(&cur_iword); + continue; + } + // Add all neighbors to graph + RzAnalysisInsnWord target_iword = { 0 }; + RzIterator *iter = rz_set_u_as_iter(cur_iword.jump_targets); + ut64 *target; + rz_iterator_foreach(iter, target) { + rz_analysis_insn_word_setup(&target_iword); + if (decode_iword_at(core, *target, buf, sizeof(buf), &target_iword) <= 0) { + rz_analysis_insn_word_fini(&target_iword); + continue; + } + bool target_within_fcn = fcn ? rz_analysis_function_contains(fcn, *target) : true; + bool found = false; + ht_uu_find(nodes_visited, *target, &found); + if (!found && target_within_fcn) { + rz_vector_push(to_visit, &target); + } + if (!add_iword_edge_to_cfg(graph, to_visit, nodes_visited, &cur_iword, &target_iword, target_within_fcn)) { + rz_analysis_insn_word_fini(&target_iword); + goto error; + } + rz_analysis_insn_word_fini(&target_iword); + } + } + +fini: + rz_analysis_insn_word_fini(&cur_iword); + rz_vector_free(to_visit); + ht_uu_free(nodes_visited); + return graph; + +error: + rz_analysis_insn_word_fini(&cur_iword); + rz_warn_if_reached(); + rz_graph_free(graph); + graph = NULL; + goto fini; +} diff --git a/librz/core/cmd/cmd_analysis.c b/librz/core/cmd/cmd_analysis.c index 1fea6e5ef4a..b7ee3b6f0de 100644 --- a/librz/core/cmd/cmd_analysis.c +++ b/librz/core/cmd/cmd_analysis.c @@ -4780,6 +4780,11 @@ RZ_IPI RzCmdStatus rz_analysis_graph_cfg_handler(RzCore *core, int argc, const c return bool2status(rz_core_graph_print(core, core->offset, RZ_CORE_GRAPH_TYPE_CFG, format)); } +RZ_IPI RzCmdStatus rz_analysis_graph_cfg_fcn_handler(RzCore *core, int argc, const char **argv) { + const RzCoreGraphFormat format = rz_core_graph_format_from_string(argv[1]); + return bool2status(rz_core_graph_print(core, core->offset, RZ_CORE_GRAPH_TYPE_CFG_FCN, format)); +} + RZ_IPI RzCmdStatus rz_analysis_graph_custom_handler(RzCore *core, int argc, const char **argv) { const RzCoreGraphFormat format = rz_core_graph_format_from_string(argv[1]); return bool2status(rz_core_agraph_print(core, format)); diff --git a/librz/core/cmd_descs/cmd_analysis.yaml b/librz/core/cmd_descs/cmd_analysis.yaml index 20df567d2b6..2deea24a7df 100644 --- a/librz/core/cmd_descs/cmd_analysis.yaml +++ b/librz/core/cmd_descs/cmd_analysis.yaml @@ -1083,6 +1083,14 @@ commands: type: RZ_CMD_ARG_TYPE_CHOICES default_value: "ascii" choices_cb: rz_analysis_graph_format_choices + - name: agFf + summary: Control flow graph (without calls) of a function + cname: analysis_graph_cfg_fcn + args: + - name: format + type: RZ_CMD_ARG_TYPE_CHOICES + default_value: "ascii" + choices_cb: rz_analysis_graph_format_choices - name: agf summary: Basic blocks function graph cname: analysis_graph_bb_function diff --git a/librz/core/cmd_descs/cmd_descs.c b/librz/core/cmd_descs/cmd_descs.c index ac3a8cfd9d0..71893f060c7 100644 --- a/librz/core/cmd_descs/cmd_descs.c +++ b/librz/core/cmd_descs/cmd_descs.c @@ -225,6 +225,7 @@ static const RzCmdDescArg analysis_graph_callgraph_function_args[2]; static const RzCmdDescArg analysis_graph_callgraph_global_args[2]; static const RzCmdDescArg analysis_graph_icfg_args[2]; static const RzCmdDescArg analysis_graph_cfg_args[2]; +static const RzCmdDescArg analysis_graph_cfg_fcn_args[2]; static const RzCmdDescArg analysis_graph_bb_function_args[2]; static const RzCmdDescArg analysis_graph_imports_args[2]; static const RzCmdDescArg analysis_graph_refs_args[2]; @@ -4258,6 +4259,21 @@ static const RzCmdDescHelp analysis_graph_cfg_help = { .args = analysis_graph_cfg_args, }; +static const RzCmdDescArg analysis_graph_cfg_fcn_args[] = { + { + .name = "format", + .type = RZ_CMD_ARG_TYPE_CHOICES, + .default_value = "ascii", + .choices.choices_cb = rz_analysis_graph_format_choices, + + }, + { 0 }, +}; +static const RzCmdDescHelp analysis_graph_cfg_fcn_help = { + .summary = "Control flow graph (without calls) of a function", + .args = analysis_graph_cfg_fcn_args, +}; + static const RzCmdDescArg analysis_graph_bb_function_args[] = { { .name = "format", @@ -19849,6 +19865,9 @@ RZ_IPI void rzshell_cmddescs_init(RzCore *core) { RzCmdDesc *analysis_graph_cfg_cd = rz_cmd_desc_argv_new(core->rcmd, ag_cd, "agF", rz_analysis_graph_cfg_handler, &analysis_graph_cfg_help); rz_warn_if_fail(analysis_graph_cfg_cd); + RzCmdDesc *analysis_graph_cfg_fcn_cd = rz_cmd_desc_argv_new(core->rcmd, ag_cd, "agFf", rz_analysis_graph_cfg_fcn_handler, &analysis_graph_cfg_fcn_help); + rz_warn_if_fail(analysis_graph_cfg_fcn_cd); + RzCmdDesc *analysis_graph_bb_function_cd = rz_cmd_desc_argv_new(core->rcmd, ag_cd, "agf", rz_analysis_graph_bb_function_handler, &analysis_graph_bb_function_help); rz_warn_if_fail(analysis_graph_bb_function_cd); diff --git a/librz/core/cmd_descs/cmd_descs.h b/librz/core/cmd_descs/cmd_descs.h index b66299846bd..5f7ca4ceb48 100644 --- a/librz/core/cmd_descs/cmd_descs.h +++ b/librz/core/cmd_descs/cmd_descs.h @@ -479,6 +479,8 @@ RZ_IPI RzCmdStatus rz_analysis_graph_callgraph_global_handler(RzCore *core, int RZ_IPI RzCmdStatus rz_analysis_graph_icfg_handler(RzCore *core, int argc, const char **argv); // "agF" RZ_IPI RzCmdStatus rz_analysis_graph_cfg_handler(RzCore *core, int argc, const char **argv); +// "agFf" +RZ_IPI RzCmdStatus rz_analysis_graph_cfg_fcn_handler(RzCore *core, int argc, const char **argv); // "agf" RZ_IPI RzCmdStatus rz_analysis_graph_bb_function_handler(RzCore *core, int argc, const char **argv); // "agi" diff --git a/librz/include/rz_analysis.h b/librz/include/rz_analysis.h index 6a35627cfab..3969485f331 100644 --- a/librz/include/rz_analysis.h +++ b/librz/include/rz_analysis.h @@ -943,6 +943,81 @@ typedef struct rz_analysis_op_t { RzAnalysisDataType datatype; } RzAnalysisOp; +static inline bool rz_analysis_op_is_call(const RzAnalysisOp *op) { + bool is_call = (op->type == RZ_ANALYSIS_OP_TYPE_CALL || + op->type == RZ_ANALYSIS_OP_TYPE_UCALL || + op->type == RZ_ANALYSIS_OP_TYPE_RCALL || + op->type == RZ_ANALYSIS_OP_TYPE_ICALL || + op->type == RZ_ANALYSIS_OP_TYPE_IRCALL); + return is_call; +} + +static inline bool rz_analysis_op_is_jump(const RzAnalysisOp *op) { + bool is_jump = (op->type == RZ_ANALYSIS_OP_TYPE_JMP || + op->type == RZ_ANALYSIS_OP_TYPE_UJMP || + op->type == RZ_ANALYSIS_OP_TYPE_RJMP || + op->type == RZ_ANALYSIS_OP_TYPE_IJMP || + op->type == RZ_ANALYSIS_OP_TYPE_IRJMP); + return is_jump; +} + +static inline bool rz_analysis_op_is_return(const RzAnalysisOp *op) { + return (op->type == RZ_ANALYSIS_OP_TYPE_RET); +} + +static inline bool rz_analysis_op_is_creturn(const RzAnalysisOp *op) { + return (op->type == RZ_ANALYSIS_OP_TYPE_CRET); +} + +static inline bool rz_analysis_op_is_cjump(const RzAnalysisOp *op) { + return rz_analysis_op_is_jump(op) && op->type & RZ_ANALYSIS_OP_TYPE_COND; +} + +static inline bool rz_analysis_op_is_ccall(const RzAnalysisOp *op) { + return rz_analysis_op_is_call(op) && op->type & RZ_ANALYSIS_OP_TYPE_COND; +} + +/** + * \brief Property flags for instruction words. + */ +typedef enum { + RZ_ANALYSIS_IWORD_COND = 0x80000000, ///< Conditional property. + RZ_ANALYSIS_IWORD_TAIL = 0x40000000, ///< Tail call property. + RZ_ANALYSIS_IWORD_NONE = 0, ///< Unset property + RZ_ANALYSIS_IWORD_R_MEM = 1 << 0, ///< Reads memory + RZ_ANALYSIS_IWORD_W_MEM = 1 << 1, ///< Writes memory + RZ_ANALYSIS_IWORD_JUMP = 1 << 2, ///< Jumps to a different address (no call) + RZ_ANALYSIS_IWORD_CALL = 1 << 3, ///< Calls a sub-procedure. + RZ_ANALYSIS_IWORD_RET = 1 << 4, ///< Returns from a sub-procedure. + RZ_ANALYSIS_IWORD_EXIT = 1 << 5, ///< Exits the program. + RZ_ANALYSIS_IWORD_CR_MEM = RZ_ANALYSIS_IWORD_R_MEM | RZ_ANALYSIS_IWORD_COND, ///< Conditionally reads memory + RZ_ANALYSIS_IWORD_CW_MEM = RZ_ANALYSIS_IWORD_W_MEM | RZ_ANALYSIS_IWORD_COND, ///< Conditionally writes memory + RZ_ANALYSIS_IWORD_CJUMP = RZ_ANALYSIS_IWORD_JUMP | RZ_ANALYSIS_IWORD_COND, ///< Conditionally jumps to a different address. + RZ_ANALYSIS_IWORD_CCALL = RZ_ANALYSIS_IWORD_CALL | RZ_ANALYSIS_IWORD_COND, ///< Conditionally jumps to a different address. + RZ_ANALYSIS_IWORD_CRET = RZ_ANALYSIS_IWORD_RET | RZ_ANALYSIS_IWORD_COND, ///< Conditionally returns from a sub-procedure. +} RzAnalysisIWordProperties; + +/** + * \brief An instruction word. It is atomically executed on the processor. + * Might contain multiple instructions. + */ +typedef struct { + ut32 size_bits; ///< Instruction word size in bits. + ut32 size_bytes; ///< Instruction word size in bytes. + ut64 addr; ///< Address the instruction word is located. + RzStrBuf *asm_str; ///< The whole asm string. Single instructions are separated by a newline. + RzPVector /**/ *insns; ///< Instructions forming the instruction word. + RzSetU *jump_targets; ///< Set of addresses this iword possibly jumps to. This includes the next instr. word if there is any. + RzSetU *call_targets; ///< Set of addresses this iword calls. + RzAnalysisLiftedILOp il_op; ///< The complete IL operation of this instr. word. + RzAnalysisIWordProperties props; ///< Properties of this instruction word. +} RzAnalysisInsnWord; + +RZ_API RZ_OWN RzAnalysisInsnWord *rz_analysis_insn_word_new(); +RZ_API void rz_analysis_insn_word_setup(RZ_BORROW RZ_NONNULL RzAnalysisInsnWord *iword); +RZ_API void rz_analysis_insn_word_free(RZ_OWN RZ_NULLABLE RzAnalysisInsnWord *iword); +RZ_API void rz_analysis_insn_word_fini(RZ_OWN RZ_NULLABLE RzAnalysisInsnWord *iword); + #define RZ_TYPE_COND_SINGLE(x) (!x->arg[1] || x->arg[0] == x->arg[1]) typedef struct rz_analysis_cond_t { @@ -1346,6 +1421,22 @@ typedef struct rz_analysis_esil_memory_region_t { // TODO: rm data + len typedef int (*RzAnalysisOpCallback)(RzAnalysis *a, RzAnalysisOp *op, ut64 addr, const ut8 *data, int len, RzAnalysisOpMask mask); +/** + * \brief The callback to decode a single instruction word. + * + * \param a The RzAnalysis to use. + * \param iword The pre-allocated instruction word struct to fill. + * \param addr The address where the instruction word is starts. + * \param buf The buffer with the bytes to decode. + * \param len The total length of the buffer in bytes. + * \param buf_off_iword Offset into \p buf, where the instruction word bytes start. The bytes before can be used for context. + * TODO: Should be replaced with a proper view into the IO layer? Something more sophisticated for sure. + * + * \return true On successful decoding. + * \return false One failure + */ +typedef bool (*RzAnalysisIWordCallback)(RzAnalysis *a, RZ_OUT RzAnalysisInsnWord *iword, ut64 addr, const ut8 *buf, size_t len, size_t buf_off_iword); + typedef bool (*RzAnalysisRegProfCallback)(RzAnalysis *a); typedef char *(*RzAnalysisRegProfGetCallback)(RzAnalysis *a); typedef int (*RzAnalysisFPBBCallback)(RzAnalysis *a, RzAnalysisBlock *bb); @@ -1382,6 +1473,7 @@ typedef struct rz_analysis_plugin_t { // legacy rz_analysis_functions RzAnalysisOpCallback op; + RzAnalysisIWordCallback decode_iword; RzAnalysisRegProfGetCallback get_reg_profile; @@ -2355,6 +2447,7 @@ RZ_API RZ_BORROW RzAnalysisVar *rz_analysis_function_get_arg_idx(RZ_NONNULL RzAn RZ_API RZ_OWN RzList /**/ *rz_analysis_types_from_fcn(RzAnalysis *analysis, RzAnalysisFunction *fcn); RZ_API RZ_OWN RzCallable *rz_analysis_function_derive_type(RzAnalysis *analysis, RzAnalysisFunction *f); RZ_API bool rz_analysis_function_is_malloc(const RzAnalysisFunction *fcn); +RZ_API bool rz_analysis_function_is_input(const RzAnalysisFunction *fcn); /* PDB */ RZ_API RzType *rz_type_db_pdb_parse(const RzTypeDB *typedb, RzPdbTpiStream *stream, RzPdbTpiType *type); diff --git a/librz/include/rz_core.h b/librz/include/rz_core.h index 290c9f7c095..2c172833971 100644 --- a/librz/include/rz_core.h +++ b/librz/include/rz_core.h @@ -793,6 +793,7 @@ typedef enum { RZ_CORE_GRAPH_TYPE_IL, ///< RzIL graph RZ_CORE_GRAPH_TYPE_ICFG, ///< Inter-procedual control flow graph RZ_CORE_GRAPH_TYPE_CFG, ///< control flow graph (without calls) + RZ_CORE_GRAPH_TYPE_CFG_FCN, ///< control flow graph (without calls) of a function. RZ_CORE_GRAPH_TYPE_UNK ///< Unknown graph } RzCoreGraphType; @@ -806,7 +807,8 @@ RZ_API RZ_OWN RzGraph /**/ *rz_core_graph_line(RzCore *core, RZ_API RZ_OWN RzGraph /**/ *rz_core_graph_il(RZ_NONNULL RzCore *core, ut64 addr); RZ_API RZ_OWN RzGraph /**/ *rz_core_graph(RzCore *core, RzCoreGraphType type, ut64 addr); RZ_API RZ_OWN RzGraph /**/ *rz_core_graph_icfg(RZ_NONNULL RzCore *core); -RZ_API RZ_OWN RzGraph /**/ *rz_core_graph_cfg(RZ_NONNULL RzCore *core, ut64 addr); +RZ_API RZ_OWN RzGraph /**/ *rz_core_graph_cfg(RZ_NONNULL RzCore *core, ut64 addr, bool within_fcn); +RZ_API RZ_OWN RzGraph /**/ *rz_core_graph_cfg_iwords(RZ_NONNULL RzCore *core, ut64 addr, bool within_fcn); RZ_API RzCoreGraphFormat rz_core_graph_format_from_string(RZ_NULLABLE const char *x); RZ_API RzCoreGraphType rz_core_graph_type_from_string(RZ_NULLABLE const char *x); diff --git a/librz/include/rz_lib.h b/librz/include/rz_lib.h index f652ba80a4a..dc115972bc6 100644 --- a/librz/include/rz_lib.h +++ b/librz/include/rz_lib.h @@ -91,6 +91,7 @@ typedef struct rz_lib_struct_t { void *data; ///< pointer to data handled by plugin handler (e.g. RzBinPlugin, RzAsmPlugin, etc.) const char *version; ///< rizin version this plugin was compiled for void (*free)(void *data); + bool is_plugin_owned; ///< If true, Rizin must not free this object. If false, Rizin must free it. } RzLibStruct; typedef RzLibStruct *(*RzLibStructFunc)(void); diff --git a/librz/include/rz_util/rz_graph_drawable.h b/librz/include/rz_util/rz_graph_drawable.h index 8d836c24cc2..2d79be88ada 100644 --- a/librz/include/rz_util/rz_graph_drawable.h +++ b/librz/include/rz_util/rz_graph_drawable.h @@ -1,6 +1,7 @@ #ifndef RZ_GRAPH_DRAWABLE_H #define RZ_GRAPH_DRAWABLE_H +#include #include #include #include @@ -15,18 +16,45 @@ typedef enum { RZ_GRAPH_NODE_TYPE_NONE = 0, ///< No type for this node specified. RZ_GRAPH_NODE_TYPE_DEFAULT, ///< Node contains a title string, a body string and an absract offset value. RZ_GRAPH_NODE_TYPE_CFG, ///< Node is part of an control flow graph of a procedure. + RZ_GRAPH_NODE_TYPE_CFG_IWORD, ///< Node is part of an control flow graph of instruction words. RZ_GRAPH_NODE_TYPE_ICFG, ///< Node is part of an inter-procedural control flow graph. } RzGraphNodeType; typedef enum { - RZ_GRAPH_NODE_SUBTYPE_NONE = 0, ///< No details given to this node. + RZ_GRAPH_NODE_SUBTYPE_ICFG_NONE = 0, ///< No details given to this node. + RZ_GRAPH_NODE_SUBTYPE_ICFG_MALLOC = 1 << 5, ///< Node represents a memory allocating procedure. +} RzGraphNodeiCFGSubType; + +typedef enum { + RZ_GRAPH_NODE_SUBTYPE_CFG_NONE = 0, ///< No details given to this node. RZ_GRAPH_NODE_SUBTYPE_CFG_ENTRY = 1 << 0, ///< Entry node of the procedure CFG. RZ_GRAPH_NODE_SUBTYPE_CFG_CALL = 1 << 1, ///> A node which calls another procedure. RZ_GRAPH_NODE_SUBTYPE_CFG_RETURN = 1 << 2, ///< A return node of the procedure. RZ_GRAPH_NODE_SUBTYPE_CFG_EXIT = 1 << 3, ///< A node which exits the program (precedure does not return). RZ_GRAPH_NODE_SUBTYPE_CFG_COND = 1 << 4, ///< A conditional instruction node. - RZ_GRAPH_NODE_SUBTYPE_ICFG_MALLOC = 1 << 5, ///< Node represents a memory allocating procedure. -} RzGraphNodeSubType; + RZ_GRAPH_NODE_SUBTYPE_CFG_JUMP = 1 << 5, ///> A node which jumps to another node. + RZ_GRAPH_NODE_SUBTYPE_CFG_TAIL = 1 << 6, ///> A tail call node. + RZ_GRAPH_NODE_SUBTYPE_CFG_ENTRY_CALL = RZ_GRAPH_NODE_SUBTYPE_CFG_ENTRY | RZ_GRAPH_NODE_SUBTYPE_CFG_CALL, + RZ_GRAPH_NODE_SUBTYPE_CFG_ENTRY_RETURN = RZ_GRAPH_NODE_SUBTYPE_CFG_ENTRY | RZ_GRAPH_NODE_SUBTYPE_CFG_RETURN, + RZ_GRAPH_NODE_SUBTYPE_CFG_ENTRY_EXIT = RZ_GRAPH_NODE_SUBTYPE_CFG_ENTRY | RZ_GRAPH_NODE_SUBTYPE_CFG_EXIT, +} RzGraphNodeCFGSubType; + +/** + * \brief Flags which describes instruction word nodes in a CFG. + * Note: These flags are *not* a replacement for the flags assigned to each single instruction within the node. + * But they are kept in sync with RzGraphNodeCFGSubType, so they can use the same parser. + */ +typedef enum { + RZ_GRAPH_NODE_SUBTYPE_CFG_IWORD_NONE = 0, ///< No details given to this node. + RZ_GRAPH_NODE_SUBTYPE_CFG_IWORD_ENTRY = 1 << 0, ///< Entry node of the procedure CFG with iwords + // Call = 1 << 1 + RZ_GRAPH_NODE_SUBTYPE_CFG_IWORD_RETURN = 1 << 2, ///< A return node of the procedure. + RZ_GRAPH_NODE_SUBTYPE_CFG_IWORD_EXIT = 1 << 3, ///< Node exits the program. + RZ_GRAPH_NODE_SUBTYPE_CFG_IWORD_COND = 1 << 4, ///< A conditional instruction word. + // Jump = 1 << 1 + RZ_GRAPH_NODE_SUBTYPE_CFG_IWORD_TAIL = 1 << 6, ///< A tail call node. + RZ_GRAPH_NODE_SUBTYPE_CFG_IWORD_ENTRY_RETURN = RZ_GRAPH_NODE_SUBTYPE_CFG_ENTRY | RZ_GRAPH_NODE_SUBTYPE_CFG_RETURN, +} RzGraphNodeCFGIWordSubType; typedef struct { char *title; @@ -40,24 +68,32 @@ typedef struct { ut64 offset; } RzGraphNodeInfoDataDefault; +/** + * \brief Info struct for a CFG node with single instructions. + */ typedef struct { - /** - * \brief Address of the node. - */ - ut64 address; - /** - * \brief Address of called procedure, if node is of type RZ_GRAPH_NODE_TYPE_CFG_CALL. - * It is set to UT64_MAX if invalid. - */ - ut64 call_address; + ut64 address; ///< Address of the node. + ut64 call_address; ///< Address of called procedure, if node is of type RZ_GRAPH_NODE_TYPE_CFG_CALL. It is set to UT64_MAX if invalid. + ut64 jump_address; ///< Address this instr. jumps to. It is set to UT64_MAX if invalid. + ut64 next; ///< Address of following instruction. It is set to UT64_MAX if it is a return instruction. + RzGraphNodeCFGSubType subtype; ///< Optional flags which describe the node further. } RzGraphNodeInfoDataCFG; +/** + * \brief A node of a CFG with instruction words. + * Each instruction word can consist of multiiple instructions. + * So an instruction word node is an unification of all its member instructions. + */ typedef struct { - /** - * \brief Address of the node. - */ - ut64 address; + ut64 address; ///< Address of the instruction word. + RzPVector /* RzGraphNodeInfoDataCFG * */ *insn; ///< Single instruction node. + RzGraphNodeCFGIWordSubType subtype; ///< Optional flags which describe the node further. +} RzGraphNodeInfoDataCFGIWord; + +typedef struct { + ut64 address; ///< Address of the node. bool is_malloc; ///< Flag set if this node is a memory allocating function. + RzGraphNodeiCFGSubType subtype; ///< Optional flags which describe the node further. } RzGraphNodeInfoDataICFG; /** @@ -66,25 +102,25 @@ typedef struct { * Provides minimal information to draw something without output format specific details. */ typedef struct rz_analysis_graph_node_info_t { - /** - * \brief Optional flags which describe the node further. - */ - RzGraphNodeType type; - RzGraphNodeSubType subtype; + RzGraphNodeType type; ///< Node type. Determines which node info is set below. union { RzGraphNodeInfoDataDefault def; RzGraphNodeInfoDataCFG cfg; + RzGraphNodeInfoDataCFGIWord cfg_iword; RzGraphNodeInfoDataICFG icfg; }; } RzGraphNodeInfo; -RZ_API RZ_OWN char *rz_graph_get_node_subtype_annotation(RzGraphNodeSubType subtype, bool utf8); +RZ_API RZ_OWN char *rz_graph_get_node_subtype_annotation_cfg(RzGraphNodeCFGSubType subtype, bool letter_abbr, bool utf8); +RZ_API RZ_OWN char *rz_graph_get_node_subtype_annotation_cfg_iword(RzGraphNodeCFGIWordSubType subtype, bool letter_abbr, bool utf8); RZ_API RZ_OWN RzGraphNodeInfo *rz_graph_get_node_info_data(RZ_BORROW void *data); RZ_API void rz_graph_free_node_info(RZ_NULLABLE void *ptr); RZ_API RzGraphNodeInfo *rz_graph_create_node_info_default(const char *title, const char *body, ut64 offset); -RZ_API RzGraphNodeInfo *rz_graph_create_node_info_icfg(ut64 address, RzGraphNodeType type, RzGraphNodeSubType subtype); -RZ_API RzGraphNodeInfo *rz_graph_create_node_info_cfg(ut64 address, ut64 call_target_addr, RzGraphNodeType type, RzGraphNodeSubType subtype); +RZ_API RzGraphNodeInfo *rz_graph_create_node_info_icfg(ut64 address, RzGraphNodeiCFGSubType subtype); +RZ_API RzGraphNodeInfo *rz_graph_create_node_info_cfg(ut64 address, ut64 call_target_addr, ut64 jump_target_addr, ut64 next, RzGraphNodeCFGSubType subtype); RZ_API RzGraphNode *rz_graph_add_node_info(RzGraph /**/ *graph, const char *title, const char *body, ut64 offset); +RZ_API void rz_graph_node_info_data_cfg_iword_init(RZ_BORROW RzGraphNodeInfoDataCFGIWord *info); +RZ_API void rz_graph_node_info_data_cfg_iword_fini(RZ_NULLABLE RZ_OWN RzGraphNodeInfoDataCFGIWord *node_info); /** * @brief Convert graph to Graphviz dot format. diff --git a/librz/include/rz_util/rz_log.h b/librz/include/rz_util/rz_log.h index fef8e9bb7ff..a7caedc703e 100644 --- a/librz/include/rz_util/rz_log.h +++ b/librz/include/rz_util/rz_log.h @@ -65,6 +65,7 @@ extern "C" { // Called by rz_core to set the configuration variables RZ_API void rz_log_set_level(RzLogLevel level); +RZ_API RzLogLevel rz_log_get_level(); RZ_API void rz_log_set_abortlevel(RzLogLevel level); RZ_API bool rz_log_set_file(RZ_NULLABLE const char *filename); RZ_API void rz_log_set_show_sources(bool show_sources); diff --git a/librz/io/io.c b/librz/io/io.c index bfde065ebf2..7b4791c3d65 100644 --- a/librz/io/io.c +++ b/librz/io/io.c @@ -295,6 +295,13 @@ static bool rz_io_vwrite_at(RzIO *io, ut64 vaddr, const ut8 *buf, size_t len) { // For physical mode, the interface is broken because the actual read bytes are // not available. This requires fixes in all call sites. RZ_API bool rz_io_read_at(RzIO *io, ut64 addr, ut8 *buf, size_t len) { + // NOTE: + // Greetings! If you reached this code with your debugger, because + // "something is not read from memory", step no further! + // First try to replace the call with `rz_io_nread_at()`, + // it maybe fixes it already. + // If not, you can still venture downwards of course. + // Good luck! rz_return_val_if_fail(io && buf && len >= 0, false); if (len == 0) { return false; diff --git a/librz/util/graph.c b/librz/util/graph.c index 67b3f16cebf..05c41a4b021 100644 --- a/librz/util/graph.c +++ b/librz/util/graph.c @@ -201,12 +201,32 @@ RZ_API void rz_graph_del_node(RzGraph *t, RZ_OWN RzGraphNode *n) { t->n_nodes--; } +/** + * \brief Adds an edge (\p from -> \p to) to the graph. + * If the edge was already added, won't add a duplicate. + * + * \param t The graph to add the edge to. + * \param from The origin node of the edge. + * \param to The destination node of the edge. + */ RZ_API void rz_graph_add_edge(RzGraph *t, RzGraphNode *from, RzGraphNode *to) { rz_graph_add_edge_at(t, from, to, -1); } +/** + * \brief Adds an edge (\p from -> \p to) to the graph at \p from->out_nodes[\p nth]. + * If the edge was already added, it won't add a duplicate. + * + * \param t The graph to add the edge to. + * \param from The origin node of the edge. + * \param to The destination node of the edge. + * \param nth The position in the \p from->out_notes list the \p to node should be added. + */ RZ_API void rz_graph_add_edge_at(RzGraph *t, RzGraphNode *from, RzGraphNode *to, int nth) { if (from && to) { + if (rz_list_contains(from->out_nodes, to)) { + return; + } rz_list_insert(from->out_nodes, nth, to); rz_list_append(from->all_neighbours, to); rz_list_append(to->in_nodes, from); @@ -271,7 +291,20 @@ RZ_API const RzList *rz_graph_get_nodes(const RzGraph *g) { return g ? g->nodes : NULL; } -/* true if there is an edge from the node `from` to the node `to` */ +/** + * \brief Checks if the edge \p from -> \p to exists in the graph. + * For this it checks the neighbors of \p from. + * + * \param g The graph to check. + * \param from The pointer to the source node of the edge. The pointer must be a node in the graph. + * \param to The destination node of the edge. The pointer must be a node in the graph. + * + * NOTE: It only compares the pointer of \p to against the neighbor list of \p from. + * If the pointer doesn't match it returns false. Even if the node content is the same. + * + * \returns true If there is an edge from the node `from` to the node `to` + * \return false Otherwise + */ RZ_API bool rz_graph_adjacent(const RzGraph *g, const RzGraphNode *from, const RzGraphNode *to) { if (!g || !from) { return false; diff --git a/librz/util/graph_drawable.c b/librz/util/graph_drawable.c index ece90e00e0f..98dc1cd63b2 100644 --- a/librz/util/graph_drawable.c +++ b/librz/util/graph_drawable.c @@ -2,45 +2,93 @@ // SPDX-FileCopyrightText: 2020 karliss // SPDX-License-Identifier: LGPL-3.0-only +#include #include #include +#include /** * \brief Translates the \p subtype flags of a node to its annotation symbols. * * \param subtype The sub-type flags of the node. + * \param letter_abbr If true, a single letter or UTF8 character abbreviation is returned. A word otherwise. * \param utf8 If true, the symbols will be UTF-8 characters. If false, they are in ASCII. * * \return A string with all symbols. */ -RZ_API RZ_OWN char *rz_graph_get_node_subtype_annotation(RzGraphNodeSubType subtype, bool utf8) { +RZ_API RZ_OWN char *rz_graph_get_node_subtype_annotation_cfg(RzGraphNodeCFGSubType subtype, bool letter_abbr, bool utf8) { char *annotation = rz_str_newf(" "); - if (!utf8) { + if (!utf8 || !letter_abbr) { annotation = rz_str_append(annotation, "("); } - if (subtype == RZ_GRAPH_NODE_SUBTYPE_NONE) { - annotation = rz_str_append(annotation, utf8 ? "○" : "."); - if (!utf8) { + if (subtype == RZ_GRAPH_NODE_SUBTYPE_CFG_NONE) { + annotation = rz_str_append(annotation, letter_abbr ? (utf8 ? "○" : ".") : "none"); + if (!utf8 || !letter_abbr) { annotation = rz_str_append(annotation, ")"); } return annotation; } + if (subtype & RZ_GRAPH_NODE_SUBTYPE_CFG_JUMP) { + annotation = rz_str_append(annotation, letter_abbr ? (utf8 ? "↷" : "j") : "jump"); + } if (subtype & RZ_GRAPH_NODE_SUBTYPE_CFG_ENTRY) { - annotation = rz_str_append(annotation, utf8 ? "↓" : "e"); + annotation = rz_str_append(annotation, letter_abbr ? (utf8 ? "↓" : "e") : "entry"); } if (subtype & RZ_GRAPH_NODE_SUBTYPE_CFG_CALL) { - annotation = rz_str_append(annotation, utf8 ? "⇢" : "C"); + annotation = rz_str_append(annotation, letter_abbr ? (utf8 ? "⇢" : "C") : "call"); } if (subtype & RZ_GRAPH_NODE_SUBTYPE_CFG_RETURN) { - annotation = rz_str_append(annotation, utf8 ? "↑" : "r"); + annotation = rz_str_append(annotation, letter_abbr ? (utf8 ? "↑" : "r") : "return"); } if (subtype & RZ_GRAPH_NODE_SUBTYPE_CFG_COND) { - annotation = rz_str_append(annotation, utf8 ? "⤹" : "c"); + annotation = rz_str_append(annotation, letter_abbr ? (utf8 ? "⤹" : "c") : "cond"); } if (subtype & RZ_GRAPH_NODE_SUBTYPE_CFG_EXIT) { - annotation = rz_str_append(annotation, utf8 ? "⭳" : "E"); + annotation = rz_str_append(annotation, letter_abbr ? (utf8 ? "⭳" : "E") : "exit"); + } + if (subtype & RZ_GRAPH_NODE_SUBTYPE_CFG_TAIL) { + annotation = rz_str_append(annotation, letter_abbr ? (utf8 ? "⇡" : "t") : "tail"); + } + if (!utf8 || !letter_abbr) { + annotation = rz_str_append(annotation, ")"); + } + return annotation; +} + +/** + * \brief Translates the \p subtype flags of a node to its annotation symbols. + * + * \param subtype The sub-type flags of the node. + * \param letter_abbr If true, a single letter or UTF8 character abbreviation is returned. A word otherwise. + * \param utf8 If true, the symbols will be UTF-8 characters. If false, they are in ASCII. + * + * \return A string with all symbols. + */ +RZ_API RZ_OWN char *rz_graph_get_node_subtype_annotation_cfg_iword(RzGraphNodeCFGIWordSubType subtype, bool letter_abbr, bool utf8) { + char *annotation = rz_str_newf(" "); + if (!utf8 || !letter_abbr) { + annotation = rz_str_append(annotation, "("); + } + if (subtype == RZ_GRAPH_NODE_SUBTYPE_CFG_IWORD_NONE) { + annotation = rz_str_append(annotation, letter_abbr ? (utf8 ? "○" : ".") : "none"); + if (!utf8 || !letter_abbr) { + annotation = rz_str_append(annotation, ")"); + } + return annotation; + } + if (subtype & RZ_GRAPH_NODE_SUBTYPE_CFG_IWORD_ENTRY) { + annotation = rz_str_append(annotation, letter_abbr ? (utf8 ? "↓" : "e") : "entry"); + } + if (subtype & RZ_GRAPH_NODE_SUBTYPE_CFG_IWORD_RETURN) { + annotation = rz_str_append(annotation, letter_abbr ? (utf8 ? "↑" : "r") : "return"); } - if (!utf8) { + if (subtype & RZ_GRAPH_NODE_SUBTYPE_CFG_IWORD_COND) { + annotation = rz_str_append(annotation, letter_abbr ? (utf8 ? "⤹" : "c") : "cond"); + } + if (subtype & RZ_GRAPH_NODE_SUBTYPE_CFG_IWORD_TAIL) { + annotation = rz_str_append(annotation, letter_abbr ? (utf8 ? "⇡" : "t") : "tail"); + } + if (!utf8 || !letter_abbr) { annotation = rz_str_append(annotation, ")"); } return annotation; @@ -63,6 +111,7 @@ RZ_API RZ_OWN RzGraphNodeInfo *rz_graph_get_node_info_data(RZ_BORROW void *data) return NULL; case RZ_GRAPH_NODE_TYPE_DEFAULT: case RZ_GRAPH_NODE_TYPE_CFG: + case RZ_GRAPH_NODE_TYPE_CFG_IWORD: case RZ_GRAPH_NODE_TYPE_ICFG: break; } @@ -81,6 +130,9 @@ RZ_API void rz_graph_free_node_info(RZ_NULLABLE void *ptr) { case RZ_GRAPH_NODE_TYPE_CFG: case RZ_GRAPH_NODE_TYPE_ICFG: break; + case RZ_GRAPH_NODE_TYPE_CFG_IWORD: + rz_graph_node_info_data_cfg_iword_fini(&info->cfg_iword); + break; case RZ_GRAPH_NODE_TYPE_DEFAULT: free(info->def.body); free(info->def.title); @@ -104,7 +156,6 @@ RZ_API RzGraphNodeInfo *rz_graph_create_node_info_default(const char *title, con return NULL; } data->type = RZ_GRAPH_NODE_TYPE_DEFAULT; - data->subtype = RZ_GRAPH_NODE_SUBTYPE_NONE; data->def.title = RZ_STR_DUP(title); data->def.body = RZ_STR_DUP(body); data->def.offset = offset; @@ -116,22 +167,43 @@ RZ_API RzGraphNodeInfo *rz_graph_create_node_info_default(const char *title, con * * \param address The address of the instruction this node represents. * \param call_target_addr The address of the procedure called, if this node is a call. + * \param jump_target_addr The address of the an instruction, if this node is a jump. + * \param next The address of the next instruction, if not a return. * \param flags Additional flags which describe the node. * * \return The initialized RzGraphNodeInfo or NULL in case of failure. */ -RZ_API RzGraphNodeInfo *rz_graph_create_node_info_cfg(ut64 address, ut64 call_target_addr, RzGraphNodeType type, RzGraphNodeSubType subtype) { +RZ_API RzGraphNodeInfo *rz_graph_create_node_info_cfg( + ut64 address, + ut64 call_target_addr, + ut64 jump_target_addr, + ut64 next, + RzGraphNodeCFGSubType subtype) { RzGraphNodeInfo *data = RZ_NEW0(RzGraphNodeInfo); if (!data) { return NULL; } data->type = RZ_GRAPH_NODE_TYPE_CFG; - data->subtype = subtype; + data->cfg.subtype = subtype; data->cfg.address = address; data->cfg.call_address = call_target_addr; + data->cfg.jump_address = jump_target_addr; + data->cfg.next = next; return data; } +RZ_API void rz_graph_node_info_data_cfg_iword_init(RZ_BORROW RzGraphNodeInfoDataCFGIWord *info) { + info->address = 0; + info->insn = rz_pvector_new(free); +} + +RZ_API void rz_graph_node_info_data_cfg_iword_fini(RZ_NULLABLE RZ_OWN RzGraphNodeInfoDataCFGIWord *node_info) { + if (!node_info) { + return; + } + rz_pvector_free(node_info->insn); +} + /** * \brief Initializes a node info struct of an iCFG node. * @@ -140,13 +212,13 @@ RZ_API RzGraphNodeInfo *rz_graph_create_node_info_cfg(ut64 address, ut64 call_ta * * \return The initialized RzGraphNodeInfo or NULL in case of failure. */ -RZ_API RzGraphNodeInfo *rz_graph_create_node_info_icfg(ut64 address, RzGraphNodeType type, RzGraphNodeSubType subtype) { +RZ_API RzGraphNodeInfo *rz_graph_create_node_info_icfg(ut64 address, RzGraphNodeiCFGSubType subtype) { RzGraphNodeInfo *data = RZ_NEW0(RzGraphNodeInfo); if (!data) { return NULL; } data->type = RZ_GRAPH_NODE_TYPE_ICFG; - data->subtype = subtype; + data->icfg.subtype = subtype; data->icfg.address = address; data->icfg.is_malloc = subtype & RZ_GRAPH_NODE_SUBTYPE_ICFG_MALLOC; return data; @@ -200,26 +272,17 @@ RZ_API RZ_OWN char *rz_graph_drawable_to_dot(RZ_NONNULL RzGraph /*cfg.address); - if (print_node->subtype & RZ_GRAPH_NODE_SUBTYPE_CFG_ENTRY) { - rz_strbuf_append(label, " (entry)"); - } - if (print_node->subtype & RZ_GRAPH_NODE_SUBTYPE_CFG_CALL) { - rz_strbuf_append(label, " (call)"); - } - if (print_node->subtype & RZ_GRAPH_NODE_SUBTYPE_CFG_RETURN) { - rz_strbuf_append(label, " (ret)"); - } - if (print_node->subtype & RZ_GRAPH_NODE_SUBTYPE_CFG_COND) { - rz_strbuf_append(label, " (cond)"); - } - if (print_node->subtype & RZ_GRAPH_NODE_SUBTYPE_CFG_EXIT) { - rz_strbuf_append(label, " (exit)"); - } + rz_strbuf_append(label, rz_graph_get_node_subtype_annotation_cfg(print_node->cfg.subtype, false, false)); + url = rz_strbuf_get(label); + break; + case RZ_GRAPH_NODE_TYPE_CFG_IWORD: + rz_strbuf_appendf(label, "0x%" PFMT64x, print_node->cfg_iword.address); + rz_strbuf_append(label, rz_graph_get_node_subtype_annotation_cfg_iword(print_node->cfg_iword.subtype, false, false)); url = rz_strbuf_get(label); break; case RZ_GRAPH_NODE_TYPE_ICFG: rz_strbuf_appendf(label, "0x%" PFMT64x, print_node->icfg.address); - if (print_node->subtype == RZ_GRAPH_NODE_SUBTYPE_ICFG_MALLOC) { + if (print_node->icfg.subtype == RZ_GRAPH_NODE_SUBTYPE_ICFG_MALLOC) { rz_strbuf_append(label, " (alloc)"); } url = rz_strbuf_get(label); @@ -279,13 +342,32 @@ RZ_API void rz_graph_drawable_to_json(RZ_NONNULL RzGraph /**/ pj_kb(pj, "is_malloc", print_node->type & RZ_GRAPH_NODE_SUBTYPE_ICFG_MALLOC); } else if (print_node->type == RZ_GRAPH_NODE_TYPE_CFG) { pj_kn(pj, "address", print_node->cfg.address); - pj_kb(pj, "is_call", print_node->type & RZ_GRAPH_NODE_SUBTYPE_CFG_CALL); - if (print_node->subtype & RZ_GRAPH_NODE_SUBTYPE_CFG_CALL && print_node->cfg.call_address != UT64_MAX) { + pj_kb(pj, "is_call", print_node->cfg.subtype & RZ_GRAPH_NODE_SUBTYPE_CFG_CALL); + if (print_node->cfg.subtype & RZ_GRAPH_NODE_SUBTYPE_CFG_CALL && print_node->cfg.call_address != UT64_MAX) { pj_kn(pj, "call_address", print_node->cfg.call_address); } - pj_kb(pj, "is_entry", print_node->subtype & RZ_GRAPH_NODE_SUBTYPE_CFG_ENTRY); - pj_kb(pj, "is_exit", print_node->subtype & RZ_GRAPH_NODE_SUBTYPE_CFG_EXIT); - pj_kb(pj, "is_return", print_node->subtype & RZ_GRAPH_NODE_SUBTYPE_CFG_RETURN); + pj_kb(pj, "is_entry", print_node->cfg.subtype & RZ_GRAPH_NODE_SUBTYPE_CFG_ENTRY); + pj_kb(pj, "is_exit", print_node->cfg.subtype & RZ_GRAPH_NODE_SUBTYPE_CFG_EXIT); + pj_kb(pj, "is_return", print_node->cfg.subtype & RZ_GRAPH_NODE_SUBTYPE_CFG_RETURN); + pj_kb(pj, "is_cond", print_node->cfg.subtype & RZ_GRAPH_NODE_SUBTYPE_CFG_COND); + } else if (print_node->type == RZ_GRAPH_NODE_TYPE_CFG_IWORD) { + pj_kn(pj, "address", print_node->cfg_iword.address); + pj_kb(pj, "is_entry", print_node->cfg_iword.subtype & RZ_GRAPH_NODE_SUBTYPE_CFG_IWORD_ENTRY); + pj_kb(pj, "is_return", print_node->cfg_iword.subtype & RZ_GRAPH_NODE_SUBTYPE_CFG_IWORD_RETURN); + pj_kb(pj, "is_cond", print_node->cfg_iword.subtype & RZ_GRAPH_NODE_SUBTYPE_CFG_IWORD_COND); + pj_k(pj, "instructions"); + pj_a(pj); + void **it; + rz_pvector_foreach (print_node->cfg_iword.insn, it) { + RzGraphNodeInfoDataCFG *inode = *it; + pj_o(pj); + pj_kn(pj, "address", inode->address); + if (inode->call_address != UT64_MAX) { + pj_kn(pj, "call_address", inode->call_address); + } + pj_end(pj); + } + pj_end(pj); } pj_k(pj, "out_nodes"); pj_a(pj); @@ -388,6 +470,9 @@ RZ_API RZ_OWN char *rz_graph_drawable_to_gml(RZ_NONNULL RzGraph /*cfg.address); break; + case RZ_GRAPH_NODE_TYPE_CFG_IWORD: + label = rz_strf(tmp, "0x%" PFMT64x, print_node->cfg_iword.address); + break; case RZ_GRAPH_NODE_TYPE_ICFG: label = rz_strf(tmp, "0x%" PFMT64x, print_node->icfg.address); break; diff --git a/librz/util/lib.c b/librz/util/lib.c index 7905bfe6637..afb9bc25ba3 100644 --- a/librz/util/lib.c +++ b/librz/util/lib.c @@ -223,7 +223,7 @@ RZ_API bool rz_lib_open(RzLib *lib, RZ_NONNULL const char *file) { rz_sys_dlclose(handler); } - if (strf) { + if (strf && !stru->is_plugin_owned) { free(stru); } return res; diff --git a/librz/util/log.c b/librz/util/log.c index 5ae8b8e6f97..35dabd4da14 100644 --- a/librz/util/log.c +++ b/librz/util/log.c @@ -3,6 +3,7 @@ // SPDX-FileCopyrightText: 2007-2018 ret2libc // SPDX-License-Identifier: LGPL-3.0-only +#include "rz_util/rz_log.h" #include #include #include @@ -68,6 +69,14 @@ RZ_API void rz_log_set_level(RzLogLevel level) { rz_th_lock_leave(logcfg.lock); } +RZ_API RzLogLevel rz_log_get_level() { + log_init(); + rz_th_lock_enter(logcfg.lock); + RzLogLevel level = logcfg.level; + rz_th_lock_leave(logcfg.lock); + return level; +} + /** * \brief Sets the log level when to abort execution * diff --git a/test/db/analysis/hexagon b/test/db/analysis/hexagon index 3d0032fffa6..25c09d64471 100644 --- a/test/db/analysis/hexagon +++ b/test/db/analysis/hexagon @@ -1460,6 +1460,29 @@ EXPECT=< 0x00008f20 / R1:0 = combine(R18,R19) +| 0x00008f24 | R3:2 = combine(R16,R17) +| 0x00008f28 | immext(##0xd980) +| 0x00008f2c \ R4 = ##obj._Mbstate +| 0x00008f30 [ R17:16 = memd(R29+#0x8) ; R19:18 = memd(R29+#0x0) +| 0x00008f34 / jump sym._Mbtowcx +\ 0x00008f38 \ LR:FP = deallocframe(FP):raw + .--------------. + | 0x8f04 (e) | + `--------------' + v + | + | + .--------------. + | 0x8f0c (.) | + `--------------' + v + | + | + .--------------. + | 0x8f10 (.) | + `--------------' + v + | + | + .--------------. + | 0x8f18 (c) | + `--------------' + t f + | | + .-------' | + | '-----. + | | +.--------------. | +| 0x8f1c (.) | | +`--------------' | + v | + | | + '-------. | + | .-----' + | | + .--------------. + | 0x8f20 (.) | + `--------------' + v + | + | + .--------------. + | 0x8f30 (.) | + `--------------' + v + | + | + .---------------. + | 0x8f34 (rt) | + `---------------' +EOF +RUN + +NAME=x86 tail call +FILE=bins/pe/bcc1.ex +CMDS=< 0x00401012 mov eax, dword [0x411133] ; [0x411133:4]=0 +| | 0x00401017 shl eax, 2 +| | 0x0040101a mov dword [0x411137], eax ; [0x411137:4]=0 +| | 0x0040101f push edx +| | 0x00401020 push 0 +| | 0x00401022 call sub.KERNEL32.DLL_GetModuleHandleA +| | 0x00401027 mov edx, eax +| | 0x00401029 call fcn.0040710c +| | 0x0040102e pop edx +| | 0x0040102f call fcn.004064a8 +| | 0x00401034 call fcn.00407110 +| | 0x00401039 push 0 +| | 0x0040103b call fcn.00408058 +| | 0x00401040 pop ecx +| | 0x00401041 push 0x4110dc +| | 0x00401046 push 0 +| | 0x00401048 call sub.KERNEL32.DLL_GetModuleHandleA +| | 0x0040104d mov dword [0x41113b], eax ; [0x41113b:4]=0 +| | 0x00401052 push 0 +\ |,=< 0x00401054 jmp fcn.0040dfd0 +.-----------------. +| 0x401000 (je) | +`-----------------' + v + | + | +.----------------. +| 0x401012 (.) | +`----------------' + v + | + | +.----------------. +| 0x401017 (.) | +`----------------' + v + | + | +.----------------. +| 0x40101a (.) | +`----------------' + v + | + | +.----------------. +| 0x40101f (.) | +`----------------' + v + | + | +.----------------. +| 0x401020 (.) | +`----------------' + v + | + | +.----------------. +| 0x401022 (C) | +`----------------' + v + | + | +.----------------. +| 0x401027 (.) | +`----------------' + v + | + | +.----------------. +| 0x401029 (C) | +`----------------' + v + | + | +.----------------. +| 0x40102e (.) | +`----------------' + v + | + | +.----------------. +| 0x40102f (C) | +`----------------' + v + | + | +.----------------. +| 0x401034 (C) | +`----------------' + v + | + | +.----------------. +| 0x401039 (.) | +`----------------' + v + | + | +.----------------. +| 0x40103b (C) | +`----------------' + v + | + | +.----------------. +| 0x401040 (.) | +`----------------' + v + | + | +.----------------. +| 0x401041 (.) | +`----------------' + v + | + | +.----------------. +| 0x401046 (.) | +`----------------' + v + | + | +.----------------. +| 0x401048 (C) | +`----------------' + v + | + | +.----------------. +| 0x40104d (.) | +`----------------' + v + | + | +.----------------. +| 0x401052 (.) | +`----------------' + v + | + | +.-----------------. +| 0x401054 (jt) | +`-----------------' +EOF +RUN + +NAME=aarch64 tail call +FILE=bins/elf/static-glibc-2.27 +CMDS=< 0x00217665 call sym.imp.__stack_chk_fail ; void __stack_chk_fail(void) + .----------------. + | 0x2175f1 (e) | + `----------------' + v + | + | + .----------------. + | 0x2175f6 (.) | + `----------------' + v + | + | + .-----------------. + | 0x2175ff (jc) | + `-----------------' + t f + | | + .------------' | + | '------. + | | +.-----------------. .----------------. +| 0x217665 (CE) | | 0x217601 (.) | +`-----------------' `----------------' + v + | + | + .----------------. + | 0x217605 (.) | + `----------------' + v + | + | + .----------------. + | 0x217606 (.) | + `----------------' + v + | + | + .----------------. + | 0x217607 (r) | + `----------------' +EOF +RUN + +NAME=arm tail call no target +FILE=bins/elf/analysis/arm-ls +CMDS=<n_nodes, 24, "data graph node count"); mu_assert_eq(g->n_edges, 25, "data graph edge count"); @@ -275,31 +275,31 @@ bool test_analysis_graph_cfg() { // (because they might have been added in different order). RzGraphNodeInfo *info = rz_graph_get_node_info_data(rz_graph_get_node(g, 0)->data); mu_assert_eq(info->type, RZ_GRAPH_NODE_TYPE_CFG, "info type"); - mu_assert_eq(info->subtype, RZ_GRAPH_NODE_SUBTYPE_CFG_ENTRY, "info subtype"); + mu_assert_eq(info->cfg.subtype, RZ_GRAPH_NODE_SUBTYPE_CFG_ENTRY, "info subtype"); mu_assert_eq(info->cfg.address, 0x117a, "info address"); mu_assert_eq(info->cfg.call_address, UT64_MAX, "info call address"); info = rz_graph_get_node_info_data(rz_graph_get_node(g, 3)->data); mu_assert_eq(info->type, RZ_GRAPH_NODE_TYPE_CFG, "info type"); - mu_assert_eq(info->subtype, RZ_GRAPH_NODE_SUBTYPE_CFG_CALL, "info subtype"); + mu_assert_eq(info->cfg.subtype, RZ_GRAPH_NODE_SUBTYPE_CFG_CALL, "info subtype"); mu_assert_eq(info->cfg.address, 0x1182, "info address"); mu_assert_eq(info->cfg.call_address, 0x1050, "info call address"); info = rz_graph_get_node_info_data(rz_graph_get_node(g, 10)->data); mu_assert_eq(info->type, RZ_GRAPH_NODE_TYPE_CFG, "info type"); - mu_assert_eq(info->subtype, RZ_GRAPH_NODE_SUBTYPE_CFG_COND, "info subtype"); + mu_assert_eq(info->cfg.subtype, RZ_GRAPH_NODE_SUBTYPE_CFG_COND, "info subtype"); mu_assert_eq(info->cfg.address, 0x11a7, "info address"); mu_assert_eq(info->cfg.call_address, UT64_MAX, "info call address"); info = rz_graph_get_node_info_data(rz_graph_get_node(g, 23)->data); mu_assert_eq(info->type, RZ_GRAPH_NODE_TYPE_CFG, "info type"); - mu_assert_eq(info->subtype, RZ_GRAPH_NODE_SUBTYPE_CFG_CALL, "info subtype"); + mu_assert_eq(info->cfg.subtype, RZ_GRAPH_NODE_SUBTYPE_CFG_CALL, "info subtype"); mu_assert_eq(info->cfg.address, 0x11cd, "info address"); mu_assert_eq(info->cfg.call_address, UT64_MAX, "info call address"); info = rz_graph_get_node_info_data(rz_graph_get_node(g, 18)->data); mu_assert_eq(info->type, RZ_GRAPH_NODE_TYPE_CFG, "info type"); - mu_assert_eq(info->subtype, RZ_GRAPH_NODE_SUBTYPE_CFG_RETURN, "info subtype"); + mu_assert_eq(info->cfg.subtype, RZ_GRAPH_NODE_SUBTYPE_CFG_RETURN, "info subtype"); mu_assert_eq(info->cfg.address, 0x11d3, "info address"); mu_assert_eq(info->cfg.call_address, UT64_MAX, "info call address"); diff --git a/test/unit/test_graph.c b/test/unit/test_graph.c index 4f0746f5576..3f5ec9618a9 100644 --- a/test/unit/test_graph.c +++ b/test/unit/test_graph.c @@ -90,6 +90,8 @@ static bool test_legacy_graph(void) { rz_graph_add_edge(g, gn7, gn8); rz_graph_add_edge(g, gn8, gn9); mu_assert_eq(g->n_edges, 17, "n_edges"); + rz_graph_add_edge(g, gn8, gn9); + mu_assert_eq(g->n_edges, 17, "n_edges"); rz_graph_del_edge(g, gn8, gn9); mu_assert_eq(rz_graph_adjacent(g, gn8, gn9), false, "is_adjacent.0"); mu_assert_eq(g->n_edges, 16, "n_edges.1"); diff --git a/test/unit/test_util.c b/test/unit/test_util.c index 4851e91cdff..a9f302bb1d4 100644 --- a/test/unit/test_util.c +++ b/test/unit/test_util.c @@ -2,6 +2,7 @@ // SPDX-License-Identifier: LGPL-3.0-only #include +#include #include "minunit.h" bool test_file_slurp(void) { From a5138a51d39b4ed264b91fb1cb5a559f96b25a26 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Fri, 18 Oct 2024 07:12:37 -0500 Subject: [PATCH 2/5] Pass a buffer instead of constant array to iword decoder --- librz/arch/p/analysis/analysis_hexagon.c | 6 +++--- librz/core/cgraph.c | 8 +------- librz/include/rz_analysis.h | 2 +- 3 files changed, 5 insertions(+), 11 deletions(-) diff --git a/librz/arch/p/analysis/analysis_hexagon.c b/librz/arch/p/analysis/analysis_hexagon.c index 24c0ce090e8..acb9c0dfaf8 100644 --- a/librz/arch/p/analysis/analysis_hexagon.c +++ b/librz/arch/p/analysis/analysis_hexagon.c @@ -38,11 +38,11 @@ RZ_API int hexagon_v6_op(RzAnalysis *analysis, RzAnalysisOp *op, ut64 addr, cons return HEX_INSN_SIZE; } -RZ_API bool rz_hexagon_decode_iword(RzAnalysis *a, RZ_OUT RzAnalysisInsnWord *iword, ut64 addr, const ut8 *buf, size_t len, size_t buf_off_iword) { - rz_return_val_if_fail(a && iword && buf, false); +RZ_API bool rz_hexagon_decode_iword(RzAnalysis *a, RZ_OUT RzAnalysisInsnWord *iword, ut64 addr, RzBuffer *buffer) { + rz_return_val_if_fail(a && iword, false); RzAnalysisOp aop = { 0 }; - HexReversedOpcode rev = { .action = HEXAGON_ANALYSIS, .ana_op = &aop, .asm_op = NULL, .state = NULL, .pkt_fully_decoded = false, .bytes_buf = buf, .bytes_buf_len = len }; + HexReversedOpcode rev = { .action = HEXAGON_ANALYSIS, .ana_op = &aop, .asm_op = NULL, .state = NULL, .pkt_fully_decoded = false, .bytes_buf = NULL, .bytes_buf_len = 0 }; bool success = hexagon_decode_iword(a, &rev, iword, addr); if (success) { iword->il_op = hex_get_il_op(addr, true, rev.state); diff --git a/librz/core/cgraph.c b/librz/core/cgraph.c index e9d78e6c347..2b87c4dd64c 100644 --- a/librz/core/cgraph.c +++ b/librz/core/cgraph.c @@ -1371,13 +1371,7 @@ static st32 decode_iword_at(RZ_BORROW RzCore *core, size_t buf_len, RZ_OUT RzAnalysisInsnWord *target_iword) { rz_return_val_if_fail(core && core->analysis && core->io && buf && core->analysis->cur && core->analysis->cur->decode_iword, -1); - size_t leading_bytes = addr < 8 ? addr : 8; - size_t bl = buf_len; - if (!read_aligned_to_mapped(core->io, addr, buf, &bl, leading_bytes)) { - RZ_LOG_ERROR("read_aligned_to_mapped() read from unmapped region at 0x%" PFMT64x ".\n", addr); - return -1; - } - bool success = core->analysis->cur->decode_iword(core->analysis, target_iword, addr, buf, bl, leading_bytes); + bool success = core->analysis->cur->decode_iword(core->analysis, target_iword, addr, NULL); return success ? target_iword->size_bytes : -1; } diff --git a/librz/include/rz_analysis.h b/librz/include/rz_analysis.h index 3969485f331..e8d1a715c42 100644 --- a/librz/include/rz_analysis.h +++ b/librz/include/rz_analysis.h @@ -1435,7 +1435,7 @@ typedef int (*RzAnalysisOpCallback)(RzAnalysis *a, RzAnalysisOp *op, ut64 addr, * \return true On successful decoding. * \return false One failure */ -typedef bool (*RzAnalysisIWordCallback)(RzAnalysis *a, RZ_OUT RzAnalysisInsnWord *iword, ut64 addr, const ut8 *buf, size_t len, size_t buf_off_iword); +typedef bool (*RzAnalysisIWordCallback)(RzAnalysis *a, RZ_OUT RzAnalysisInsnWord *iword, ut64 addr, RzBuffer *buffer); typedef bool (*RzAnalysisRegProfCallback)(RzAnalysis *a); typedef char *(*RzAnalysisRegProfGetCallback)(RzAnalysis *a); From 6b924c74a83a5a05c55345fdbf244b5aca03b18e Mon Sep 17 00:00:00 2001 From: Rot127 Date: Fri, 18 Oct 2024 07:27:01 -0500 Subject: [PATCH 3/5] Fix segfault during free of stack value --- librz/arch/p/analysis/analysis_hexagon.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/librz/arch/p/analysis/analysis_hexagon.c b/librz/arch/p/analysis/analysis_hexagon.c index acb9c0dfaf8..052991c3b1d 100644 --- a/librz/arch/p/analysis/analysis_hexagon.c +++ b/librz/arch/p/analysis/analysis_hexagon.c @@ -41,8 +41,8 @@ RZ_API int hexagon_v6_op(RzAnalysis *analysis, RzAnalysisOp *op, ut64 addr, cons RZ_API bool rz_hexagon_decode_iword(RzAnalysis *a, RZ_OUT RzAnalysisInsnWord *iword, ut64 addr, RzBuffer *buffer) { rz_return_val_if_fail(a && iword, false); - RzAnalysisOp aop = { 0 }; - HexReversedOpcode rev = { .action = HEXAGON_ANALYSIS, .ana_op = &aop, .asm_op = NULL, .state = NULL, .pkt_fully_decoded = false, .bytes_buf = NULL, .bytes_buf_len = 0 }; + RzAnalysisOp *aop = RZ_NEW0(RzAnalysisOp); + HexReversedOpcode rev = { .action = HEXAGON_ANALYSIS, .ana_op = aop, .asm_op = NULL, .state = NULL, .pkt_fully_decoded = false, .bytes_buf = NULL, .bytes_buf_len = 0 }; bool success = hexagon_decode_iword(a, &rev, iword, addr); if (success) { iword->il_op = hex_get_il_op(addr, true, rev.state); From 3d552c67b3f7eb453416b3e2b3de92013ca53b9a Mon Sep 17 00:00:00 2001 From: Rot127 Date: Fri, 18 Oct 2024 08:05:37 -0500 Subject: [PATCH 4/5] Fix logs and invalid decodes --- librz/arch/isa/hexagon/hexagon_arch.c | 4 ++-- librz/arch/p/analysis/analysis_hexagon.c | 4 +++- librz/arch/p/asm/asm_hexagon.c | 4 +++- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/librz/arch/isa/hexagon/hexagon_arch.c b/librz/arch/isa/hexagon/hexagon_arch.c index 4c6fe4b840e..8ec9d8c3cb3 100644 --- a/librz/arch/isa/hexagon/hexagon_arch.c +++ b/librz/arch/isa/hexagon/hexagon_arch.c @@ -291,7 +291,7 @@ RZ_API HexPkt *hex_get_pkt(RZ_BORROW HexState *state, const ut32 addr) { } } } - RZ_LOG_DEBUG("Failed to get packet at 0x%" PFMT32x, addr); + RZ_LOG_DEBUG("Failed to get packet at 0x%" PFMT32x "\n", addr); return NULL; } @@ -1152,7 +1152,7 @@ static RZ_BORROW HexInsnContainer *decode_hic(HexState *state, HexReversedOpcode // Add to state as not yet fully decoded packet. HexInsnContainer *hic = hex_add_hic_to_state(state, &hic_new); if (!hic) { - RZ_LOG_ERROR("Could not add incsturction container to state.\n"); + RZ_LOG_ERROR("Could not add instruction container to state.\n"); return NULL; } HexPkt *p = hex_get_pkt(state, hic->addr); diff --git a/librz/arch/p/analysis/analysis_hexagon.c b/librz/arch/p/analysis/analysis_hexagon.c index 052991c3b1d..26eac812b40 100644 --- a/librz/arch/p/analysis/analysis_hexagon.c +++ b/librz/arch/p/analysis/analysis_hexagon.c @@ -30,7 +30,9 @@ RZ_API int hexagon_v6_op(RzAnalysis *analysis, RzAnalysisOp *op, ut64 addr, cons // Disassemble as many instructions as possible from the buffer. HexReversedOpcode rev = { .action = HEXAGON_ANALYSIS, .ana_op = op, .asm_op = NULL, .state = NULL, .pkt_fully_decoded = false, .bytes_buf = buf, .bytes_buf_len = len }; - hexagon_reverse_opcode(&rev, addr, NULL, analysis); + if (!hexagon_reverse_opcode(&rev, addr, NULL, analysis)) { + return -1; + } if (mask & RZ_ANALYSIS_OP_MASK_IL) { op->il_op = hex_get_il_op(addr, rev.pkt_fully_decoded, rev.state); } diff --git a/librz/arch/p/asm/asm_hexagon.c b/librz/arch/p/asm/asm_hexagon.c index 65cb6866f43..c3666a2149a 100644 --- a/librz/arch/p/asm/asm_hexagon.c +++ b/librz/arch/p/asm/asm_hexagon.c @@ -185,7 +185,9 @@ static int disassemble(RzAsm *a, RzAsmOp *op, const ut8 *buf, int l) { ut32 addr = (ut32)a->pc; HexReversedOpcode rev = { .action = HEXAGON_DISAS, .ana_op = NULL, .asm_op = op, .state = NULL, .pkt_fully_decoded = false, .bytes_buf = buf, .bytes_buf_len = l }; - hexagon_reverse_opcode(&rev, addr, a, NULL); + if (!hexagon_reverse_opcode(&rev, addr, a, NULL)) { + rz_strbuf_append(&op->buf_asm, "invalid"); + } return HEX_INSN_SIZE; } From adebf3ea6c2eb5c7df45c3ef043f7ad66d18dd35 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Thu, 24 Oct 2024 13:59:17 -0500 Subject: [PATCH 5/5] Implement handling of core plugin configurations --- librz/arch/asm.c | 4 ++-- librz/core/canalysis.c | 2 +- librz/core/cautocmpl.c | 2 +- librz/core/cmd/cmd_eval.c | 6 +++--- librz/core/core.c | 2 +- librz/core/cplugin.c | 38 +++++++++++++++++++++++++++++--------- librz/core/p/core_dex.c | 5 +++-- librz/core/p/core_java.c | 5 +++-- librz/include/rz_core.h | 23 +++++++++++++++++++++-- 9 files changed, 64 insertions(+), 23 deletions(-) diff --git a/librz/arch/asm.c b/librz/arch/asm.c index 6310e3b5471..e7b062a779d 100644 --- a/librz/arch/asm.c +++ b/librz/arch/asm.c @@ -425,7 +425,7 @@ RZ_API bool rz_asm_use_assembler(RzAsm *a, const char *name) { static void set_plugin_configs(RZ_BORROW RzCore *core, const char *plugin_name, RZ_OWN RzConfig *pcfg) { rz_return_if_fail(pcfg && core); rz_config_lock(pcfg, 1); - if (!ht_sp_insert(core->plugin_configs, plugin_name, pcfg)) { + if (!ht_sp_insert(core->plugins_config, plugin_name, pcfg)) { RZ_LOG_WARN("Plugin '%s' was already added.\n", plugin_name); } } @@ -438,7 +438,7 @@ static void set_plugin_configs(RZ_BORROW RzCore *core, const char *plugin_name, */ static void remove_plugin_config(RZ_BORROW RzCore *core, const char *plugin_name) { rz_return_if_fail(core && plugin_name); - ht_sp_delete(core->plugin_configs, plugin_name); + ht_sp_delete(core->plugins_config, plugin_name); } // TODO: this can be optimized using rz_str_hash() diff --git a/librz/core/canalysis.c b/librz/core/canalysis.c index 0e3f76afd74..33e0d464cec 100644 --- a/librz/core/canalysis.c +++ b/librz/core/canalysis.c @@ -3944,7 +3944,7 @@ static void core_analysis_using_plugins(RzCore *core) { rz_iterator_foreach(it, val) { RzCorePlugin *plugin = *val; if (plugin->analysis) { - plugin->analysis(core); + plugin->analysis(core, NULL); } } rz_iterator_free(it); diff --git a/librz/core/cautocmpl.c b/librz/core/cautocmpl.c index d3e0be1b526..e1a06fea182 100644 --- a/librz/core/cautocmpl.c +++ b/librz/core/cautocmpl.c @@ -557,7 +557,7 @@ static void autocmplt_cmd_arg_eval_key(RzCore *core, RzLineNSCompletionResult *r } } RzConfig **plugin_cfg; - RzIterator *it = ht_sp_as_iter(core->plugin_configs); + RzIterator *it = ht_sp_as_iter(core->plugins_config); rz_iterator_foreach(it, plugin_cfg) { rz_list_foreach ((*plugin_cfg)->nodes, iter, bt) { if (!strncmp(bt->name, s, len)) { diff --git a/librz/core/cmd/cmd_eval.c b/librz/core/cmd/cmd_eval.c index d83ab48652e..a5c0a7f8399 100644 --- a/librz/core/cmd/cmd_eval.c +++ b/librz/core/cmd/cmd_eval.c @@ -352,7 +352,7 @@ static void print_all_plugin_configs(const RzCore *core) { RzConfig **cfg; RzCmdStateOutput state = { 0 }; rz_cmd_state_output_init(&state, RZ_OUTPUT_MODE_QUIET); - RzIterator *it = ht_sp_as_iter(core->plugin_configs); + RzIterator *it = ht_sp_as_iter(core->plugins_config); rz_iterator_foreach(it, cfg) { rz_core_config_print_all(*cfg, "", &state); } @@ -376,10 +376,10 @@ static RZ_BORROW RzConfig *eval_get_config_obj_by_key(const RzCore *core, const const char *second_dot = strchr(first_dot + 1, '.'); bool cfg_found = false; if (!second_dot) { - cfg = ht_sp_find(core->plugin_configs, first_dot + 1, &cfg_found); + cfg = ht_sp_find(core->plugins_config, first_dot + 1, &cfg_found); } else { char *config_name = rz_sub_str_ptr(config_str, first_dot + 1, second_dot - 1); - cfg = ht_sp_find(core->plugin_configs, config_name, &cfg_found); + cfg = ht_sp_find(core->plugins_config, config_name, &cfg_found); free(config_name); } if (!cfg_found) { diff --git a/librz/core/core.c b/librz/core/core.c index ac47f72c350..38ee28f3241 100644 --- a/librz/core/core.c +++ b/librz/core/core.c @@ -1508,7 +1508,7 @@ RZ_API bool rz_core_init(RzCore *core) { core->cmdremote = 0; core->incomment = false; core->config = NULL; - core->plugin_configs = ht_sp_new(HT_STR_DUP, NULL, (HtSPFreeValue)rz_config_free); + core->plugins_config = ht_sp_new(HT_STR_DUP, NULL, (HtSPFreeValue)rz_config_free); core->http_up = false; ZERO_FILL(core->root_cmd_descriptor); core->print = rz_print_new(); diff --git a/librz/core/cplugin.c b/librz/core/cplugin.c index d1c9b6bb450..756e83921c2 100644 --- a/librz/core/cplugin.c +++ b/librz/core/cplugin.c @@ -7,6 +7,7 @@ #include #include #include "rz_core_plugins.h" +#include static RzCorePlugin *core_static_plugins[] = { RZ_CORE_STATIC_PLUGINS }; @@ -18,12 +19,15 @@ RZ_API bool rz_core_plugin_fini(RzCore *core) { rz_iterator_foreach(iter, val) { RzCorePlugin *plugin = *val; if (plugin->fini) { - plugin->fini(core); + bool found = false; + void *pdata = ht_sp_find(core->plugins_data, plugin->name, &found); + plugin->fini(core, found ? &pdata : NULL); } } rz_iterator_free(iter); ht_sp_free(core->plugins); - ht_sp_free(core->plugin_configs); + ht_sp_free(core->plugins_data); + ht_sp_free(core->plugins_config); core->plugins = NULL; return true; } @@ -31,31 +35,47 @@ RZ_API bool rz_core_plugin_fini(RzCore *core) { RZ_API bool rz_core_plugin_add(RzCore *core, RZ_NONNULL RzCorePlugin *plugin) { rz_return_val_if_fail(core, false); rz_return_val_if_fail(plugin && plugin->init && plugin->name && plugin->author && plugin->license, false); - // TODO: Add config from core plugin. - if (!ht_sp_insert(core->plugins, plugin->name, plugin)) { + bool found = false; + if (found) { RZ_LOG_WARN("Plugin '%s' was already added.\n", plugin->name); + return true; } - if (!plugin->init(core)) { + + ht_sp_insert(core->plugins, plugin->name, plugin); + ht_sp_insert(core->plugins_data, plugin->name, NULL); + HtSPKv *pdata = ht_sp_find_kv(core->plugins_data, plugin->name, NULL); + if (!plugin->init(core, &pdata->value)) { ht_sp_delete(core->plugins, plugin->name); + ht_sp_delete(core->plugins_data, plugin->name); return false; } + + if (plugin->get_config) { + RzConfig *pcfg = plugin->get_config(pdata->value); + rz_config_lock(pcfg, 1); + ht_sp_insert(core->plugins_config, plugin->name, pcfg); + } return true; } RZ_API bool rz_core_plugin_del(RzCore *core, RZ_NONNULL RzCorePlugin *plugin) { rz_return_val_if_fail(core && plugin, false); - ht_sp_delete(core->plugin_configs, plugin->name); - if (plugin->fini && !plugin->fini(core)) { + ht_sp_delete(core->plugins_config, plugin->name); + HtSPKv *pdata = ht_sp_find_kv(core->plugins_data, plugin->name, NULL); + if (plugin->fini && !plugin->fini(core, &pdata->value)) { return false; } + ht_sp_delete(core->plugins_data, plugin->name); + ht_sp_delete(core->plugins_config, plugin->name); return ht_sp_delete(core->plugins, plugin->name); } RZ_API bool rz_core_plugin_init(RzCore *core) { - int i; bool res = true; core->plugins = ht_sp_new(HT_STR_DUP, NULL, NULL); - for (i = 0; i < RZ_ARRAY_SIZE(core_static_plugins); i++) { + core->plugins_data = ht_sp_new(HT_STR_DUP, NULL, NULL); + core->plugins_config = ht_sp_new(HT_STR_DUP, NULL, (HtSPFreeValue)rz_config_free); + for (size_t i = 0; i < RZ_ARRAY_SIZE(core_static_plugins); i++) { if (!rz_core_plugin_add(core, core_static_plugins[i])) { RZ_LOG_ERROR("core: error loading core plugin '%s'\n", core_static_plugins[i]->name); res = false; diff --git a/librz/core/p/core_dex.c b/librz/core/p/core_dex.c index 6b172d80e29..fd4c021bfbb 100644 --- a/librz/core/p/core_dex.c +++ b/librz/core/p/core_dex.c @@ -290,7 +290,7 @@ static const RzCmdDescHelp dex_usage = { static_description_without_args(dexs, "prints the dex structure"); static_description_without_args(dexe, "prints the dex exported methods"); -static bool rz_cmd_dex_init_handler(RzCore *core) { +static bool rz_cmd_dex_init_handler(RzCore *core, void **private_data) { RzCmd *rcmd = core->rcmd; RzCmdDesc *root_cd = rz_cmd_get_root(rcmd); if (!root_cd) { @@ -309,7 +309,7 @@ static bool rz_cmd_dex_init_handler(RzCore *core) { return true; } -static bool rz_cmd_dex_fini_handler(RzCore *core) { +static bool rz_cmd_dex_fini_handler(RzCore *core, void **private_data) { RzCmd *rcmd = core->rcmd; RzCmdDesc *cd = rz_cmd_get_desc(rcmd, "dex"); rz_return_val_if_fail(cd, false); @@ -324,6 +324,7 @@ RzCorePlugin rz_core_plugin_dex = { .version = "1.0", .init = rz_cmd_dex_init_handler, .fini = rz_cmd_dex_fini_handler, + .get_config = NULL, }; #ifndef RZ_PLUGIN_INCORE diff --git a/librz/core/p/core_java.c b/librz/core/p/core_java.c index 97a294f54b2..2e922877ee6 100644 --- a/librz/core/p/core_java.c +++ b/librz/core/p/core_java.c @@ -291,7 +291,7 @@ static const RzCmdDescHelp name_help(javar) = { .args = name_args(javar), }; -static bool rz_cmd_java_init_handler(RzCore *core) { +static bool rz_cmd_java_init_handler(RzCore *core, void **private_data) { RzCmd *rcmd = core->rcmd; RzCmdDesc *root_cd = rz_cmd_get_root(rcmd); if (!root_cd) { @@ -315,7 +315,7 @@ static bool rz_cmd_java_init_handler(RzCore *core) { return true; } -static bool rz_cmd_java_fini_handler(RzCore *core) { +static bool rz_cmd_java_fini_handler(RzCore *core, void **private_data) { RzCmd *rcmd = core->rcmd; RzCmdDesc *cd = rz_cmd_get_desc(rcmd, "java"); rz_return_val_if_fail(cd, false); @@ -330,6 +330,7 @@ RzCorePlugin rz_core_plugin_java = { .version = "1.0", .init = rz_cmd_java_init_handler, .fini = rz_cmd_java_fini_handler, + .get_config = NULL, }; #ifndef RZ_PLUGIN_INCORE diff --git a/librz/include/rz_core.h b/librz/include/rz_core.h index 2c172833971..194cfaa8af8 100644 --- a/librz/include/rz_core.h +++ b/librz/include/rz_core.h @@ -95,7 +95,24 @@ typedef enum { RZ_CORE_WRITE_OP_SHIFT_RIGHT, ///< Write the shift right of existing byte and argument value } RzCoreWriteOp; -typedef bool (*RzCorePluginCallback)(RzCore *core); +/** + * \brief RzCore plugin callback. + * + * \param core the Core this plugin is assigned to. + * \param plugin_data Memory location to place the plugins private data pointer. + * + * \example + * ``` + * void plugin_init(RzCore *core, void **plugin_data) { + * *plugin_data = malloc(sizeof(PrivatePluginData)); + * } + * + * void plugin_fini(RzCore *core, void **plugin_data) { + * free(*plugin_data); + * } + * ``` + */ +typedef bool (*RzCorePluginCallback)(RzCore *core, void **plugin_data); typedef struct rz_core_plugin_t { const char *name; @@ -106,6 +123,7 @@ typedef struct rz_core_plugin_t { RzCorePluginCallback init; ///< Is called when the plugin is loaded by rizin RzCorePluginCallback fini; ///< Is called when the plugin is unloaded by rizin RzCorePluginCallback analysis; ///< Is called when automatic analysis is performed. + RZ_OWN RzConfig *(*get_config)(void *plugin_data); ///< Return private configuration of this plugin. } RzCorePlugin; typedef struct rz_core_rtr_host_t RzCoreRtrHost; @@ -253,8 +271,9 @@ struct rz_core_t { // They are used in pointer passing hacks in rz_types.h. RzIO *io; HtSP /**/ *plugins; ///< List of registered core plugins + HtSP /**/ *plugins_data; ///< Core plugins private data. + HtSP /*: RzConfig>*/ *plugins_config; ///< Pointers to plugin configurations. Indexed by "plugins." RzConfig *config; - HtSP /*: RzConfig>*/ *plugin_configs; ///< Pointers to plugin configurations. Indexed by "plugins." ut64 offset; // current seek ut64 prompt_offset; // temporarily set to offset to have $$ in expressions always stay the same during temp seeks ut32 blocksize;