From ce2ca0892a8fe42f546b697ec9055fccf70a1933 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=A4rkl?= Date: Sun, 30 Aug 2026 17:56:08 +0200 Subject: [PATCH] Restore changes not included in initial absint merge --- doc/abstract_interpreter/state_diagrams.svg | 261 +++++++ librz/arch/analysis.c | 71 ++ librz/arch/fcn.c | 24 + librz/arch/p/analysis/analysis_hexagon.c | 1 + librz/arch/xrefs.c | 83 +++ librz/bin/format/mach0/mach0.c | 10 + librz/bin/p/bin_elf.inc | 6 + librz/core/canalysis.c | 1 - librz/core/cmd/cmd_analysis.c | 2 + librz/core/cmd/cmd_inquiry.c | 96 +++ librz/core/cmd_descs/cmd_analysis.yaml | 24 + librz/core/cmd_descs/cmd_descs.c | 38 + librz/core/cmd_descs/cmd_descs.h | 2 + librz/core/core.c | 3 + librz/core/libs.c | 2 + librz/include/meson.build | 4 +- librz/include/rz_analysis.h | 33 +- librz/include/rz_bin.h | 6 + librz/include/rz_core.h | 2 + librz/include/rz_inquiry.h | 104 +++ librz/include/rz_inquiry/rz_bcfg.h | 77 ++ librz/include/rz_lib.h | 1 + librz/include/rz_th.h | 1 + librz/inquiry/absint/interpreter.c | 1 + librz/inquiry/absint/val_domain_constant.c | 18 +- .../inquiry/algorithms/revng_fcn_detection.c | 260 +++++++ librz/inquiry/bcfg.c | 349 +++++++++ librz/inquiry/inquiry.c | 341 +++++++++ librz/inquiry/meson.build | 3 + librz/util/thread_queue.c | 13 + test/db/inquiry/interpreter/unmapped_fcn_loop | 155 ++++ test/db/inquiry/interpreter/xrefs | 685 ++++++++++++++++++ 32 files changed, 2673 insertions(+), 4 deletions(-) create mode 100644 doc/abstract_interpreter/state_diagrams.svg create mode 100644 librz/include/rz_inquiry.h create mode 100644 librz/include/rz_inquiry/rz_bcfg.h create mode 100644 librz/inquiry/algorithms/revng_fcn_detection.c create mode 100644 librz/inquiry/bcfg.c create mode 100644 librz/inquiry/inquiry.c create mode 100644 test/db/inquiry/interpreter/unmapped_fcn_loop create mode 100644 test/db/inquiry/interpreter/xrefs diff --git a/doc/abstract_interpreter/state_diagrams.svg b/doc/abstract_interpreter/state_diagrams.svg new file mode 100644 index 0000000000..0c00768e4b --- /dev/null +++ b/doc/abstract_interpreter/state_diagrams.svg @@ -0,0 +1,261 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ls + exp + Error + + + + + + + + + + + + + + B I + → + + + + → + + + T + + + + + + + + + + + wait + - + + + + + + + + + T + + wait + - + + + + + + + + + B II + B III + → + + + + → + + + + + + + + + + + + wait + - + + + + + + + + + clear + open + B IV + → + + + T + + + + + + + + + + + wait + - + + + + + + + + + clear + B IV + → + + + T + + + + + + emulation + error + + + + + + + + + + + + + + init + B I - Get entry point + + + + + + + emulation + B II + memory + B III + basic block + + + + + + + + + clean + B IV + sync + + + + + + + T + + + term + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + T + + + + + + + + + diff --git a/librz/arch/analysis.c b/librz/arch/analysis.c index f852133267..8d4861f6e4 100644 --- a/librz/arch/analysis.c +++ b/librz/arch/analysis.c @@ -993,6 +993,77 @@ RZ_API bool rz_analysis_op_is_call(RZ_NONNULL const RzAnalysisOp *op) { } } +/** + * \brief Checks if \p op changes the control flow. + * It checks op->type, op->eob for it. So any jump, call, trap, or illegal + * instruction. + * It also returns true for conditional instructions. + * + * \return True if \p op changes the control flow. False otherwise. + */ +RZ_API bool rz_analysis_op_changes_control_flow(RZ_NONNULL const RzAnalysisOp *op) { + rz_return_val_if_fail(op, false); + switch (op->type & RZ_ANALYSIS_OP_TYPE_MASK) { + 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: + 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: + case RZ_ANALYSIS_OP_TYPE_RET: + case RZ_ANALYSIS_OP_TYPE_CRET: + case RZ_ANALYSIS_OP_TYPE_ILL: + case RZ_ANALYSIS_OP_TYPE_UNK: + case RZ_ANALYSIS_OP_TYPE_TRAP: + case RZ_ANALYSIS_OP_TYPE_SWI: + case RZ_ANALYSIS_OP_TYPE_CSWI: + case RZ_ANALYSIS_OP_TYPE_LEAVE: + case RZ_ANALYSIS_OP_TYPE_SWITCH: + return true; + default: + return false; + } +} + +/** + * \brief Returns true if the \p op is a direct call. + */ +RZ_API bool rz_analysis_op_is_direct_call(RZ_NONNULL const RzAnalysisOp *op) { + rz_return_val_if_fail(op, false); + switch (op->type & RZ_ANALYSIS_OP_TYPE_MASK) { + case RZ_ANALYSIS_OP_TYPE_CALL: + case RZ_ANALYSIS_OP_TYPE_CCALL: + return true; + default: + return false; + } +} + +/** + * \brief Returns true if the \p op is a direct call. + */ +RZ_API bool rz_analysis_op_is_direct_jump(RZ_NONNULL const RzAnalysisOp *op) { + rz_return_val_if_fail(op, false); + switch (op->type & RZ_ANALYSIS_OP_TYPE_MASK) { + case RZ_ANALYSIS_OP_TYPE_JMP: + case RZ_ANALYSIS_OP_TYPE_CJMP: + return true; + default: + return false; + } +} + RZ_API void rz_analysis_purge(RzAnalysis *analysis) { rz_analysis_hint_clear(analysis); rz_interval_tree_fini(&analysis->meta); diff --git a/librz/arch/fcn.c b/librz/arch/fcn.c index fa4eeb3108..714c5f49d8 100644 --- a/librz/arch/fcn.c +++ b/librz/arch/fcn.c @@ -1854,6 +1854,30 @@ RZ_API RzAnalysisFunction *rz_analysis_get_function_byname(RzAnalysis *a, const return NULL; } +/** + * \brief Adds a basic block to the RzAnalysis. + * It disassembles it to determine the instructions and their offset. + */ +RZ_API bool rz_analysis_add_bb(RzAnalysis *a, ut64 addr, ut64 size) { + rz_return_val_if_fail(a && size > 0, false); + if (size > a->opt.bb_max_size) { + RZ_LOG_ERROR("Cannot allocate such big bb of %" PFMT64d " bytes at 0x%08" PFMT64x "\n", (st64)size, addr); + rz_warn_if_reached(); + return false; + } + RzAnalysisBlock *block = rz_analysis_get_block_at(a, addr); + if (block) { + rz_analysis_delete_block(block); + } + + block = rz_analysis_create_block(a, addr, size); + if (!block) { + return false; + } + rz_analysis_block_analyze_ops(block); + return true; +} + /* rename RzAnalysisFunctionBB.add() */ RZ_API bool rz_analysis_fcn_add_bb(RzAnalysis *a, RzAnalysisFunction *fcn, ut64 addr, ut64 size, ut64 jump, ut64 fail) { if (size == 0) { diff --git a/librz/arch/p/analysis/analysis_hexagon.c b/librz/arch/p/analysis/analysis_hexagon.c index 1f7e50527f..fe8d9c1717 100644 --- a/librz/arch/p/analysis/analysis_hexagon.c +++ b/librz/arch/p/analysis/analysis_hexagon.c @@ -30,6 +30,7 @@ RZ_API int hexagon_v6_op(RzAnalysis *analysis, RzAnalysisOp *op, ut64 addr, cons hexagon_reverse_opcode(&rev, addr, NULL, analysis); HexPkt *p = hex_get_pkt(rev.state, addr); if (p) { + op->hexagon_pkt_addr = p->pkt_addr; rev.pkt_fully_decoded = p->is_valid; if (mask & RZ_ANALYSIS_OP_MASK_INSN_PKT) { op->size = rz_list_length(p->bin) * HEX_INSN_SIZE; diff --git a/librz/arch/xrefs.c b/librz/arch/xrefs.c index c50af82916..42f7402e7c 100644 --- a/librz/arch/xrefs.c +++ b/librz/arch/xrefs.c @@ -216,6 +216,8 @@ RZ_API const char *rz_analysis_xrefs_type_tostring(RzAnalysisXRefType type) { return "STRING"; case RZ_ANALYSIS_XREF_TYPE_MEM_WRITE: return "MEM_WRITE"; + case RZ_ANALYSIS_XREF_TYPE_RETURN: + return "RETURN"; case RZ_ANALYSIS_XREF_TYPE_NULL: default: return "UNKNOWN"; @@ -310,6 +312,87 @@ RZ_API const char *rz_analysis_ref_type_tostring(RzAnalysisXRefType t) { return "string"; case RZ_ANALYSIS_XREF_TYPE_MEM_WRITE: return "write"; + case RZ_ANALYSIS_XREF_TYPE_CALL_RET: + return "call_ret_pt"; + case RZ_ANALYSIS_XREF_TYPE_RETURN: + return "return"; } return "unknown"; } + +static bool read_up_to(RzAnalysis *analysis, ut64 addr, ut8 *buf, size_t buf_size) { + rz_mem_set_num(buf, buf_size, 0); + do { + if (analysis->iob.read_at(analysis->iob.io, addr, buf, buf_size) > 0) { + return true; + } + buf_size--; + } while (buf_size > 0); + return false; +} + +/** + * \brief Returns all control flow targets of call/jmp instructions within the \p sections. + * NOTE: This function disassembles all instructions within the boundaries and checks for calls/jmps. + * It DOES NOT use the existing xrefs. + * + * \param analysis The analysis plugin. + * \param sections The RzBinSections to disassemble to find call/jmp instructions. + * \param include_call_return_pts If true, it will add addresses after a call instruction as well. + * \param cf_targets The found call targets of all disassemble calls. + * + * \return True in case of success, false otherwise. + */ +RZ_API bool rz_analysis_get_all_cf_targets(RzAnalysis *analysis, + const RzPVector /**/ *sections, + bool include_call_return_pts, + RZ_NONNULL RZ_OUT RzSetU *cf_targets) { + rz_return_val_if_fail(analysis && analysis->cur && sections && cf_targets, false); + size_t buf_size = (analysis->cur->bits / 8) * 16; + ut8 *buf = RZ_NEWS0(ut8, buf_size); + if (!buf_size || !buf) { + return false; + } + + RzAnalysisOp op = { 0 }; + void **it; + rz_pvector_foreach (sections, it) { + RzBinSection *sec = *it; + if (!(sec->perm & RZ_PERM_X)) { + continue; + } + ut64 addr = sec->vaddr; + while (addr < sec->vaddr + sec->size) { + if (!read_up_to(analysis, addr, buf, buf_size)) { + RZ_LOG_WARN("Failed to read memory at 0x%" PFMT64x " size: %" PFMTSZu ".\n", addr, buf_size); + rz_analysis_op_fini(&op); + break; + } + if (rz_analysis_op(analysis, &op, addr, buf, buf_size, RZ_ANALYSIS_OP_MASK_BASIC) <= 0) { + rz_analysis_op_fini(&op); + addr += op.size; + continue; + } + // Only add jump targets going to executable regions. + bool op_is_call = rz_analysis_op_is_direct_call(&op); + bool op_is_jump = rz_analysis_op_is_direct_jump(&op); + if ((op_is_call || op_is_jump) && op.jump != UT64_MAX) { + rz_set_u_add(cf_targets, op.jump); + if (op.fail != UT64_MAX) { + if (op_is_jump) { + rz_set_u_add(cf_targets, op.fail); + } + } + } + if (include_call_return_pts && op_is_call) { + // If it is a call, also add the following instruction as reference. + // Because it is likely a return point. + rz_set_u_add(cf_targets, op.addr + op.size); + } + addr += op.size; + rz_analysis_op_fini(&op); + } + } + free(buf); + return true; +} diff --git a/librz/bin/format/mach0/mach0.c b/librz/bin/format/mach0/mach0.c index 0375375e52..c5c6dd5e9f 100644 --- a/librz/bin/format/mach0/mach0.c +++ b/librz/bin/format/mach0/mach0.c @@ -2028,6 +2028,13 @@ RzPVector /**/ *MACH0_(get_maps)(RzBinFile *bf) { return ret; } +static bool is_linking_related_section(ut64 sec_type) { + return sec_type & S_SYMBOL_STUBS || + sec_type & S_LAZY_SYMBOL_POINTERS || + sec_type & S_NON_LAZY_SYMBOL_POINTERS || + sec_type & S_LAZY_DYLIB_SYMBOL_POINTERS; +} + RzPVector /**/ *MACH0_(get_segments)(RzBinFile *bf) { struct MACH0_(obj_t) *bin = bf->o->bin_obj; if (bin->sections_cache) { @@ -2077,6 +2084,9 @@ RzPVector /**/ *MACH0_(get_segments)(RzBinFile *bf) { s->flags = bin->sects[i].flags & 0xFFFFFF00; // XXX flags s->paddr = (ut64)bin->sects[i].offset; + if (is_linking_related_section(s->type)) { + s->layout.role = RZ_BIN_SECTION_ROLE_LINKING; + } int segment_index = 0; // s->perm =prot2perm (bin->segs[j].initprot); for (j = 0; j < bin->nsegs; j++) { diff --git a/librz/bin/p/bin_elf.inc b/librz/bin/p/bin_elf.inc index 5f32a8132f..181070c575 100644 --- a/librz/bin/p/bin_elf.inc +++ b/librz/bin/p/bin_elf.inc @@ -857,6 +857,12 @@ static RzPVector /**/ *sections_obj(ELFOBJ *obj, size_t psize) { ptr->layout.element_size = sizeof(Elf_(Addr)); ptr->layout.count = section->size / sizeof(Elf_(Addr)); } + if (section->type == SHT_RELA || + section->type == SHT_DYNAMIC || + section->type == SHT_DYNSYM || + (section->name && strstr(section->name, ".plt"))) { + ptr->layout.role = RZ_BIN_SECTION_ROLE_LINKING; + } ptr->size = section->type != SHT_NOBITS ? section->size : 0; ptr->vsize = section->size; ptr->paddr = section->offset; diff --git a/librz/core/canalysis.c b/librz/core/canalysis.c index cdf2b3edf9..6d3199f456 100644 --- a/librz/core/canalysis.c +++ b/librz/core/canalysis.c @@ -2,7 +2,6 @@ // SPDX-FileCopyrightText: 2009-2020 nibble // SPDX-License-Identifier: LGPL-3.0-only -#include #include #include #include diff --git a/librz/core/cmd/cmd_analysis.c b/librz/core/cmd/cmd_analysis.c index c903ecc7b8..ff74efd79b 100644 --- a/librz/core/cmd/cmd_analysis.c +++ b/librz/core/cmd/cmd_analysis.c @@ -2126,9 +2126,11 @@ RZ_IPI RzCmdStatus rz_analysis_function_xrefs_handler(RzCore *core, int argc, co rz_cons_printf("0x%08" PFMT64x " ", xref->to); break; case RZ_ANALYSIS_XREF_TYPE_MEM_WRITE: + case RZ_ANALYSIS_XREF_TYPE_CALL_RET: case RZ_ANALYSIS_XREF_TYPE_CODE: case RZ_ANALYSIS_XREF_TYPE_CALL: case RZ_ANALYSIS_XREF_TYPE_DATA: + case RZ_ANALYSIS_XREF_TYPE_RETURN: rz_cons_printf("0x%08" PFMT64x " ", xref->to); rz_core_seek(core, xref->from, 1); rz_core_print_disasm_instructions(core, 0, 1); diff --git a/librz/core/cmd/cmd_inquiry.c b/librz/core/cmd/cmd_inquiry.c index cd6ea30bfe..f57284ae98 100644 --- a/librz/core/cmd/cmd_inquiry.c +++ b/librz/core/cmd/cmd_inquiry.c @@ -12,3 +12,99 @@ RZ_IPI RzCmdStatus rz_inquiry_analyze_all_handler(RzCore *core, int argc, const rz_return_val_if_fail(core->analysis && core->io && core->bin->cur && core->bin->cur->o, RZ_CMD_STATUS_ERROR); return rz_core_inquiry_analyze_all(core) ? RZ_CMD_STATUS_OK : RZ_CMD_STATUS_ERROR; } + +static RzVector /**/ *get_ignored_code_regions( + const RzPVector /**/ *symbols, + RzPVector /**/ *sections, + const RzPVector /**/ *io_maps) { + void **it; + RzVector *v = rz_vector_new(sizeof(RzInterval), NULL, NULL); + rz_pvector_foreach (sections, it) { + RzBinSection *sec = *it; + if (sec->layout.role == RZ_BIN_SECTION_ROLE_LINKING || !(sec->perm & RZ_PERM_X)) { + RzInterval itv = { .addr = sec->vaddr, .size = sec->vsize }; + rz_vector_push(v, &itv); + } + } + rz_pvector_free(sections); + + rz_pvector_foreach (io_maps, it) { + RzIOMap *map = *it; + if (!(map->perm & RZ_PERM_X)) { + RzInterval itv = { .addr = map->itv.addr, .size = map->itv.size }; + rz_vector_push(v, &itv); + } + } + + rz_pvector_foreach (symbols, it) { + RzBinSymbol *sym = *it; + if (sym->is_imported) { + RzInterval itv = { .addr = sym->vaddr, .size = sym->size }; + rz_vector_push(v, &itv); + } + } + return v; +} + +RZ_IPI RzCmdStatus rz_inquiry_interpreter_prototype_handler(RzCore *core, int argc, const char **argv) { + rz_return_val_if_fail(core->analysis && core->io && core->bin->cur && core->bin->cur->o, RZ_CMD_STATUS_ERROR); + RzSetU *entry_points = rz_set_u_new(); + if (!entry_points) { + return RZ_CMD_STATUS_ERROR; + } + bool run_fcn_detection = RZ_STR_EQ(argv[1], "-f"); + + ut64 entry_point; + if (argc == (run_fcn_detection ? 2 : 1)) { + // No specific entry point given. Pick the one provided by the bin plugin. + entry_point = rz_bin_get_first_entrypoint(core->bin->cur->o); + rz_set_u_add(entry_points, entry_point); + } else { + // Add all entry points given as arguments. + for (size_t i = (run_fcn_detection ? 2 : 1); i < argc; i++) { + ut64 entry_point = rz_num_get(core->num, argv[i]); + rz_set_u_add(entry_points, entry_point); + } + } + RzAbsIntDriverConfig config = { + .analysis = core->analysis, + .io = core->io, + .entry_points = entry_points, + .dimens = RZ_ABSINT_RESULT_DIMEN_XREFS, + .trace_opts = RZ_ABSINT_TRACE_NONE + }; + bool success = rz_absint_driver_run(&config); + rz_set_u_free(entry_points); + eprintf("Finished reference recovery: %s\n", success ? "OK" : "FAIL"); + if (!success) { + return RZ_CMD_STATUS_ERROR; + } + + RzSetU *symbol_addresses = rz_set_u_new(); + if (!symbol_addresses || !rz_inquiry_get_fcn_symbol_addr(core, symbol_addresses)) { + rz_warn_if_reached(); + return RZ_CMD_STATUS_ERROR; + } + const RzPVector *symbols = rz_bin_object_get_symbols(core->bin->cur->o); + RzPVector *fcns = rz_pvector_new((RzPVectorFree)rz_inquiry_function_free); + + if (!fcns || !symbols) { + return RZ_CMD_STATUS_ERROR; + } + if (run_fcn_detection) { + RzVector *ignored_code_regions = get_ignored_code_regions( + rz_bin_object_get_symbols(core->bin->cur->o), + rz_bin_object_get_sections(core->bin->cur->o), + rz_io_maps(core->io)); + eprintf("Perform function deduction: "); + success &= rz_inquiry_function_deduction(core->analysis, core->inquiry, symbol_addresses, symbols, ignored_code_regions, fcns); + rz_vector_free(ignored_code_regions); + eprintf("%s\n", success ? "OK" : "FAIL"); + } + rz_set_u_free(symbol_addresses); + + success &= rz_inquiry_convert_and_add_to_analysis(core->analysis, core->inquiry, fcns, symbols); + rz_pvector_free(fcns); + + return success ? RZ_CMD_STATUS_OK : RZ_CMD_STATUS_ERROR; +} diff --git a/librz/core/cmd_descs/cmd_analysis.yaml b/librz/core/cmd_descs/cmd_analysis.yaml index 2ca2b093f9..23852852fc 100644 --- a/librz/core/cmd_descs/cmd_analysis.yaml +++ b/librz/core/cmd_descs/cmd_analysis.yaml @@ -2630,3 +2630,27 @@ commands: summary: analyze all functions cname: inquiry_analyze_all args: [] + - name: aIp + summary: Abstract Interpreter Prototype + cname: inquiry_interpreter_prototype + args: + - name: f + type: RZ_CMD_ARG_TYPE_OPTION + flags: RZ_CMD_ARG_FLAG_OPTION + default_value: 0 + - name: entry_points + type: RZ_CMD_ARG_TYPE_RZNUM + flags: RZ_CMD_ARG_FLAG_ARRAY + optional: true + details: + - name: Examples + entries: + - text: "aIp" + arg_str: "" + comment: "Run prototype RzIL analysis detecting cross references." + - text: "aIp" + arg_str: " 0x1000" + comment: "Run prototype RzIL analysis starting at address 0x1000." + - text: "aIp" + arg_str: " -f" + comment: "Run prototype RzIL analysis with function detection." diff --git a/librz/core/cmd_descs/cmd_descs.c b/librz/core/cmd_descs/cmd_descs.c index d0d1234170..fba1ba3159 100644 --- a/librz/core/cmd_descs/cmd_descs.c +++ b/librz/core/cmd_descs/cmd_descs.c @@ -66,6 +66,7 @@ static const RzCmdDescDetail analysis_hint_set_optype_details[2]; static const RzCmdDescDetail analysis_hint_set_immbase_details[3]; static const RzCmdDescDetail analysis_hint_set_offset_details[2]; static const RzCmdDescDetail analyze_esil_insn_access_details[4]; +static const RzCmdDescDetail inquiry_interpreter_prototype_details[2]; static const RzCmdDescDetail basefind_compute_details[2]; static const RzCmdDescDetail cmd_cmp_unified_details[2]; static const RzCmdDescDetail cw_details[2]; @@ -428,6 +429,7 @@ static const RzCmdDescArg analyze_esil_emu_fcn_find_args_args[2]; static const RzCmdDescArg analyze_esil_int_list_load_args[2]; static const RzCmdDescArg analyze_esil_int_remove_args[2]; static const RzCmdDescArg analyze_esil_insn_access_args[4]; +static const RzCmdDescArg inquiry_interpreter_prototype_args[3]; static const RzCmdDescArg block_args[2]; static const RzCmdDescArg block_decrease_args[2]; static const RzCmdDescArg block_increase_args[2]; @@ -8469,6 +8471,39 @@ static const RzCmdDescHelp inquiry_analyze_all_help = { .args = inquiry_analyze_all_args, }; +static const RzCmdDescDetailEntry inquiry_interpreter_prototype_Examples_detail_entries[] = { + { .text = "aIp", .arg_str = "", .comment = "Run prototype RzIL analysis detecting cross references." }, + { .text = "aIp", .arg_str = " 0x1000", .comment = "Run prototype RzIL analysis starting at address 0x1000." }, + { .text = "aIp", .arg_str = " -f", .comment = "Run prototype RzIL analysis with function detection." }, + { 0 }, +}; +static const RzCmdDescDetail inquiry_interpreter_prototype_details[] = { + { .name = "Examples", .entries = inquiry_interpreter_prototype_Examples_detail_entries }, + { 0 }, +}; +static const RzCmdDescArg inquiry_interpreter_prototype_args[] = { + { + .name = "f", + .type = RZ_CMD_ARG_TYPE_OPTION, + .flags = RZ_CMD_ARG_FLAG_OPTION, + .default_value = "0", + + }, + { + .name = "entry_points", + .type = RZ_CMD_ARG_TYPE_RZNUM, + .flags = RZ_CMD_ARG_FLAG_ARRAY, + .optional = true, + + }, + { 0 }, +}; +static const RzCmdDescHelp inquiry_interpreter_prototype_help = { + .summary = "Abstract Interpreter Prototype", + .details = inquiry_interpreter_prototype_details, + .args = inquiry_interpreter_prototype_args, +}; + static const RzCmdDescHelp b_help = { .summary = "Display or change the block size", }; @@ -23937,6 +23972,9 @@ RZ_IPI void rzshell_cmddescs_init(RzCore *core) { RzCmdDesc *inquiry_analyze_all_cd = rz_cmd_desc_argv_new(core->rcmd, aI_cd, "aIa", rz_inquiry_analyze_all_handler, &inquiry_analyze_all_help); rz_warn_if_fail(inquiry_analyze_all_cd); + RzCmdDesc *inquiry_interpreter_prototype_cd = rz_cmd_desc_argv_new(core->rcmd, aI_cd, "aIp", rz_inquiry_interpreter_prototype_handler, &inquiry_interpreter_prototype_help); + rz_warn_if_fail(inquiry_interpreter_prototype_cd); + RzCmdDesc *b_cd = rz_cmd_desc_group_state_new(core->rcmd, root_cd, "b", RZ_OUTPUT_MODE_STANDARD | RZ_OUTPUT_MODE_JSON, rz_block_handler, &block_help, &b_help); rz_warn_if_fail(b_cd); RzCmdDesc *block_decrease_cd = rz_cmd_desc_argv_new(core->rcmd, b_cd, "b-", rz_block_decrease_handler, &block_decrease_help); diff --git a/librz/core/cmd_descs/cmd_descs.h b/librz/core/cmd_descs/cmd_descs.h index 4c3933302d..b737e05709 100644 --- a/librz/core/cmd_descs/cmd_descs.h +++ b/librz/core/cmd_descs/cmd_descs.h @@ -969,6 +969,8 @@ RZ_IPI RzCmdStatus rz_analyze_esil_insn_access_handler(RzCore *core, int argc, c RZ_IPI RzCmdStatus rz_inquiry_analyze_function_handler(RzCore *core, int argc, const char **argv); // "aIa" RZ_IPI RzCmdStatus rz_inquiry_analyze_all_handler(RzCore *core, int argc, const char **argv); +// "aIp" +RZ_IPI RzCmdStatus rz_inquiry_interpreter_prototype_handler(RzCore *core, int argc, const char **argv); // "b" RZ_IPI RzCmdStatus rz_block_handler(RzCore *core, int argc, const char **argv, RzCmdStateOutput *state); // "b-" diff --git a/librz/core/core.c b/librz/core/core.c index c3ba662955..41488cded7 100644 --- a/librz/core/core.c +++ b/librz/core/core.c @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: 2009-2020 pancake // SPDX-License-Identifier: LGPL-3.0-only +#include "rz_inquiry.h" #include #include #include @@ -1615,6 +1616,7 @@ RZ_API bool rz_core_init(RzCore *core) { /* XXX memory leak */ return false; } + core->inquiry = rz_inquiry_new(); core->ev = rz_event_new(core); core->max_cmd_depth = RZ_CONS_CMD_DEPTH + 1; core->sdb = sdb_new(NULL, "rzkv.sdb", 0); // XXX: path must be in home? @@ -1870,6 +1872,7 @@ RZ_API void rz_core_fini(RzCore *c) { RZ_FREE_CUSTOM(c->hash, rz_hash_free); RZ_FREE_CUSTOM(c->ropchain, rz_list_free); RZ_FREE_CUSTOM(c->ev, rz_event_free); + RZ_FREE_CUSTOM(c->inquiry, rz_inquiry_free); RZ_FREE(c->cmdlog); RZ_FREE(c->lastsearch); RZ_FREE(c->cons->pager); diff --git a/librz/core/libs.c b/librz/core/libs.c index 9ee9e7010e..fc26a64510 100644 --- a/librz/core/libs.c +++ b/librz/core/libs.c @@ -37,6 +37,7 @@ CB(bin, bin) CB(demangler, bin->demangler) CB(egg, egg) CB(hash, hash) +CB(inquiry, inquiry) static bool lib_arch_cb(RzLibPlugin *pl, void *user, void *data) { RzArchPlugin *hand = (RzArchPlugin *)data; @@ -122,6 +123,7 @@ RZ_API void rz_core_loadlibs_init(RzCore *core) { DF(EGG, "egg plugins", egg); DF(HASH, "hash plugins", hash); DF(ARCH, "(dis)assembler & analysis plugins", arch); + DF(INTERPRETER, "inquiry interpreter plugins", inquiry); core->times->loadlibs_init_time = rz_time_now_mono() - prev; } diff --git a/librz/include/meson.build b/librz/include/meson.build index 63b9e145b4..8f7af9aa35 100644 --- a/librz/include/meson.build +++ b/librz/include/meson.build @@ -29,6 +29,7 @@ include_files = [ 'rz_heap_glibc.h', 'rz_heap_jemalloc.h', 'rz_il.h', + 'rz_inquiry.h', 'rz_io.h', 'rz_lang.h', 'rz_lib.h', @@ -146,7 +147,8 @@ install_headers(rz_util_files, install_dir: join_paths(rizin_incdir, 'rz_util')) rz_inquiry_files = [ 'rz_inquiry/rz_absint.h', - 'rz_inquiry/rz_il_cache.h' + 'rz_inquiry/rz_il_cache.h', + 'rz_inquiry/rz_bcfg.h', ] install_headers(rz_inquiry_files, install_dir: join_paths(rizin_incdir, 'rz_inquiry')) diff --git a/librz/include/rz_analysis.h b/librz/include/rz_analysis.h index 2f75fd6b47..ad7c4d3c7b 100644 --- a/librz/include/rz_analysis.h +++ b/librz/include/rz_analysis.h @@ -358,7 +358,7 @@ typedef enum { RZ_ANALYSIS_OP_MASK_DISASM = (1 << 4), // It fills RzAnalysisop->mnemonic // should be RzAnalysisOp->disasm // only from rz_core_analysis_op() RZ_ANALYSIS_OP_MASK_IL = (1 << 5), // It fills RzAnalysisop->il_op RZ_ANALYSIS_OP_MASK_INSN_PKT = (1 << 6), // Set op.size to the size of the instruction packet, not the single instruction. - RZ_ANALYSIS_OP_MASK_ALL = RZ_ANALYSIS_OP_MASK_ESIL | RZ_ANALYSIS_OP_MASK_VAL | RZ_ANALYSIS_OP_MASK_HINT | RZ_ANALYSIS_OP_MASK_OPEX | RZ_ANALYSIS_OP_MASK_DISASM | RZ_ANALYSIS_OP_MASK_IL + RZ_ANALYSIS_OP_MASK_ALL = RZ_ANALYSIS_OP_MASK_ESIL | RZ_ANALYSIS_OP_MASK_VAL | RZ_ANALYSIS_OP_MASK_HINT | RZ_ANALYSIS_OP_MASK_OPEX | RZ_ANALYSIS_OP_MASK_DISASM | RZ_ANALYSIS_OP_MASK_IL | RZ_ANALYSIS_OP_MASK_INSN_PKT } RzAnalysisOpMask; typedef enum { @@ -889,6 +889,7 @@ typedef struct rz_analysis_op_t { RzAnalysisSwitchOp *switch_op; RzAnalysisHint hint; RzAnalysisDataType datatype; + ut64 hexagon_pkt_addr; } RzAnalysisOp; #define RZ_TYPE_COND_SINGLE(x) (!x->arg[1] || x->arg[0] == x->arg[1]) @@ -968,13 +969,33 @@ typedef enum { */ RZ_ANALYSIS_XREF_TYPE_MEM_READ = RZ_ANALYSIS_XREF_TYPE_DATA, RZ_ANALYSIS_XREF_TYPE_MEM_WRITE = 'w', ///< Memory write reference + + RZ_ANALYSIS_XREF_TYPE_CALL_RET = 'r', // Call return point (next pc after a call). + RZ_ANALYSIS_XREF_TYPE_RETURN = 'R', // A returning jump } RzAnalysisXRefType; typedef struct rz_analysis_ref_t { + ut64 bb_addr; ///< Temporary member to make prototype function detection work. ut64 from; ut64 to; RzAnalysisXRefType type; } RzAnalysisXRef; + +/** + * \brief A struct combining information about an instruction storing the + * next instruction pointer of a basic block. + * This indicates that the ending branch instruction in a basic block is + * a call instruction. + */ +typedef struct { + ut64 bb_addr; ///< The address of the basic block which stores the NPC, WARNING: may not actually be filled yet! + ut64 store_addr; ///< The address of the instruction packet storing the NPC. Might be 0, if it was not added by the interpreter. + ut64 candidate_addr; ///< Address of the call candidate instruction packet. Might be 0, if it was not added by the interpreter. + ut64 npc; ///< The NPC stored. Should point after a basic block. + ut64 target; ///< The address the call candidate jumps to. + bool in_mem; ///< True if the NPC is written to memory. False if it is written to a register. +} RzAnalysisCallCandidate; + RZ_API const char *rz_analysis_ref_type_tostring(RzAnalysisXRefType t); /* represents a reference line from one address (from) to another (to) */ @@ -1509,6 +1530,10 @@ RZ_API bool rz_analysis_op_fini(RzAnalysisOp *op); RZ_API int rz_analysis_op_reg_delta(RzAnalysis *analysis, ut64 addr, const char *name); RZ_API bool rz_analysis_op_is_eob(const RzAnalysisOp *op); RZ_API bool rz_analysis_op_is_call(RZ_NONNULL const RzAnalysisOp *op); +RZ_API bool rz_analysis_op_changes_control_flow(RZ_NONNULL const RzAnalysisOp *op); +RZ_API bool rz_analysis_op_is_direct_call(RZ_NONNULL const RzAnalysisOp *op); +RZ_API bool rz_analysis_op_is_call(RZ_NONNULL const RzAnalysisOp *op); +RZ_API bool rz_analysis_op_is_direct_jump(RZ_NONNULL const RzAnalysisOp *op); RZ_API RzList /**/ *rz_analysis_op_list_new(void); RZ_API int rz_analysis_op(RZ_NONNULL RzAnalysis *analysis, RZ_OUT RzAnalysisOp *op, ut64 addr, const ut8 *data, ut64 len, RzAnalysisOpMask mask); RZ_API RzAnalysisOp *rz_analysis_op_hexstr(RzAnalysis *analysis, ut64 addr, const char *hexstr); @@ -1576,6 +1601,7 @@ RZ_API int rz_analysis_fcn(RzAnalysis *analysis, RzAnalysisFunction *fcn, ut64 a RZ_API int rz_analysis_fcn_del(RzAnalysis *analysis, ut64 addr); RZ_API int rz_analysis_fcn_del_locs(RzAnalysis *analysis, ut64 addr); RZ_API bool rz_analysis_fcn_add_bb(RzAnalysis *analysis, RzAnalysisFunction *fcn, ut64 addr, ut64 size, ut64 jump, ut64 fail); +RZ_API bool rz_analysis_add_bb(RzAnalysis *a, ut64 addr, ut64 size); RZ_API bool rz_analysis_check_fcn(RzAnalysis *analysis, ut8 *buf, ut16 bufsz, ut64 addr, ut64 low, ut64 high); RZ_API void rz_analysis_function_check_bp_use(RzAnalysisFunction *fcn); @@ -1615,6 +1641,11 @@ RZ_API bool rz_analysis_xrefs_set(RzAnalysis *analysis, ut64 from, ut64 to, RzAn RZ_API bool rz_analysis_xrefs_deln(RzAnalysis *analysis, ut64 from, ut64 to, RzAnalysisXRefType type); RZ_API bool rz_analysis_xref_del(RzAnalysis *analysis, ut64 from, ut64 to); +RZ_API bool rz_analysis_get_all_cf_targets(RzAnalysis *analysis, + const RzPVector /**/ *sections, + bool include_call_return_pts, + RZ_NONNULL RZ_OUT RzSetU *branch_targets); + /* var.c */ RZ_API RZ_BORROW RzAnalysisVar *rz_analysis_function_set_var( RzAnalysisFunction *fcn, diff --git a/librz/include/rz_bin.h b/librz/include/rz_bin.h index bcc3bd6558..b390eeb452 100644 --- a/librz/include/rz_bin.h +++ b/librz/include/rz_bin.h @@ -632,12 +632,18 @@ typedef struct rz_bin_map_t { #define RZ_BIN_MAX_HANDLED_LAYOUT_SIZE (1024 * 1024 * 2) +typedef enum { + RZ_BIN_SECTION_ROLE_UNSPECIFIED = 0, + RZ_BIN_SECTION_ROLE_LINKING, ///< Section is used for (dynamic) linking. +} RzBinSectionRole; + /** * \brief A repetitive data layout of a section. The type will be applied to * element_size * element_count. */ typedef struct rz_bin_section_format_t { int /* RzAnalysisMetaType */ type; ///< The meta type of the elements. + RzBinSectionRole role; ///< The role a section has. ut64 element_size; ///< Size of an element in this section. size_t count; ///< The number of consecutive elements in this section. } RzBinSectionLayout; diff --git a/librz/include/rz_core.h b/librz/include/rz_core.h index 4c4becd780..89206c19fe 100644 --- a/librz/include/rz_core.h +++ b/librz/include/rz_core.h @@ -4,6 +4,7 @@ #ifndef RZ_CORE_H #define RZ_CORE_H +#include #include #include #include @@ -294,6 +295,7 @@ struct rz_core_t { ut8 ptr_alignment_III; // NOTE: Do not change the order of fields above! // They are used in pointer passing hacks in rz_types.h. + RzInquiry *inquiry; RzIO *io; HtSP /**/ *plugins; ///< List of registered core plugins HtSP /**/ *plugin_contexts; ///< Per-core plugin state diff --git a/librz/include/rz_inquiry.h b/librz/include/rz_inquiry.h new file mode 100644 index 0000000000..a80fa0d798 --- /dev/null +++ b/librz/include/rz_inquiry.h @@ -0,0 +1,104 @@ +// SPDX-FileCopyrightText: 2025 RizinOrg +// SPDX-License-Identifier: LGPL-3.0-only + +/** + * \file The header file for the RzInquiry module provides the declarations + * for RzInquiry plugins. As well as access to broader analysis functions. + */ + +#ifndef RZ_INQUIRY +#define RZ_INQUIRY + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +/** + * \brief The number of iterations inquiry checks for a user given signal. + * Checking it costs performance, so it is just checked the X iterations. + */ +#define RZ_INQUIRY_CHECK_USER_SIGNAL_ITC 1000 + +typedef struct rz_inquiry_plugin_t { + const char *name; + const char *author; + const char *version; + const char *desc; + const char *license; + RzAbsIntValueDomain *value_domain; +} RzInquiryPlugin; + +typedef struct { + /** + * \brief RzInquiry interpreter plugins. Indexed by name. + */ + HtSP /**/ *plugins; + HtSP /**/ *plugins_data; + + HtUP /**/ *call_candidates; ///< Indexed by address of basic block with the call candidate. + RzVector /**/ *dynamic_xrefs; ///< All xrefs the interpreter detected. + RzInquiryBCFG *bcfg; ///< The control flow graph all the basic blocks build. +} RzInquiry; + +RZ_IPI bool rz_inquiry_bcfg_complement(RzInquiry *iq, RzVector /**/ *insn_to_insn_edges); + +RZ_IPI void rz_inquiry_add_xref(RzInquiry *iq, const RzAnalysisXRef *xref); + +RZ_API bool rz_inquiry_plugin_add(RZ_BORROW RZ_NONNULL RzInquiry *inquiry, RZ_NONNULL RzInquiryPlugin *plugin); +RZ_API bool rz_inquiry_plugin_del(RZ_BORROW RZ_NONNULL RzInquiry *inquiry, RZ_NONNULL RzInquiryPlugin *plugin); + +RZ_API RZ_OWN RzInquiry *rz_inquiry_new(void); +RZ_API void rz_inquiry_free(RZ_OWN RZ_NULLABLE RzInquiry *q); + +RZ_API bool rz_inquiry_xref_interpreter_filter(RZ_NONNULL const RzAnalysisXRef *xref, RZ_NONNULL const RzPVector /**/ *allowed_segments); + +RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzSetU /**/ *entry_points); + +RZ_API bool rz_inquiry_function_deduction( + RZ_NONNULL RZ_BORROW RzAnalysis *analysis, + RZ_NONNULL RZ_BORROW RzInquiry *inquiry, + RZ_NONNULL RZ_BORROW RzSetU *symbol_addresses, + RZ_NONNULL const RzPVector /**/ *symbols, + RZ_NONNULL const RzVector /**/ *ignored_code, + RZ_NONNULL RZ_OUT RzPVector /**/ *inquiry_fcns); + +//============ +// Algorithms +//============ + +/** + * \brief A simple function deduced by inquiry algorithms. + */ +typedef struct { + RzVector /**/ *entry_points; + RzInquiryBCFG *bcfg; +} RzInquiryFunction; + +RZ_IPI RZ_OWN RzInquiryFunction *rz_inquiry_function_new(); +RZ_API RZ_OWN char *rz_inquiry_function_str(const RzInquiryFunction *fcn); + +RZ_API bool rz_inquiry_get_fcn_symbol_addr(RzCore *core, RZ_OUT RzSetU *symbol_targets); + +RZ_API void rz_inquiry_function_free(RZ_NULLABLE RZ_OWN RzInquiryFunction *fcn); + +RZ_API bool rz_inquiry_algo_revng_fcn_detection( + RzSetU *symbol_addresses, + const HtUP /**/ *call_candidates, + const RzInquiryBCFG *bcfg, + RZ_OUT RzPVector /**/ *fcns, + RZ_NONNULL const RzVector /**/ *ignored_code); + +RZ_IPI bool rz_inquiry_convert_and_add_to_analysis( + RzAnalysis *analysis, + RzInquiry *inquiry, + const RzPVector /**/ *fcns, + const RzPVector /**/ *symbols); + +#ifdef __cplusplus +} +#endif +#endif // RZ_INQUIRY diff --git a/librz/include/rz_inquiry/rz_bcfg.h b/librz/include/rz_inquiry/rz_bcfg.h new file mode 100644 index 0000000000..32924a84d3 --- /dev/null +++ b/librz/include/rz_inquiry/rz_bcfg.h @@ -0,0 +1,77 @@ +// SPDX-FileCopyrightText: 2026 Rot127 +// SPDX-License-Identifier: LGPL-3.0-only + +#ifndef RZ_INQUIRY_BCFG_H +#define RZ_INQUIRY_BCFG_H + +#include +#include + +/** + * \brief A block of instructions which end in a jump/call/exit. + */ +typedef struct { + ut64 addr; + ut64 size; +} RzInquiryBlock; + +/** + * \brief The different kind of BB CFG edges. + */ +typedef enum { + RZ_INQUIRY_BCFG_EDGE_TYPE_NONE = 0, + /** + * \brief An control flow change via a RzIL JMP. The "from" BB ends with a jump. + */ + RZ_INQUIRY_BCFG_EDGE_TYPE_JMP, + /** + * \brief A control flow edge between two blocks where the "from" block does not end with a JUMP. + * It is between two blocks which were split from a single one. + */ + RZ_INQUIRY_BCFG_EDGE_TYPE_CF, + /** + * \brief An control flow change via a RzIL JMP but by a call candidate. + * The "from" BB ends with a jump, very likely to a procedure. + */ + RZ_INQUIRY_BCFG_EDGE_TYPE_CALL, + /** + * \brief This edge does not depict a control flow edge. + * It is an opaque edge between a call instruction and the (possible) return point of the procedure called. + * Note that the return point is often, but not always, the instruction at the following memory address after the call. + * There are multiple exceptions to it though, like tail calls, non-returning procedures, or delayed calls. + */ + RZ_INQUIRY_BCFG_EDGE_TYPE_CALL_RET, + /** + * \brief An edge from a return instruction to its target. + */ + RZ_INQUIRY_BCFG_EDGE_TYPE_RETURN, +} RzInquiryBCFGEdgeType; + +/** + * \brief A control flow graph with blocks as nodes. + * Edges can be of different jump, and call/return type. + */ +typedef struct { + /** + * \brief The CFG discovered during interpretation. + * The node data are the addresses of basic blocks, cast to (void *). + */ + RzGraph /**/ *graph; +} RzInquiryBCFG; + +RZ_IPI RZ_OWN RzInquiryBCFG *rz_inquiry_bcfg_new(RzGraphImplType impl_type); +RZ_IPI void rz_inquiry_bcfg_free(RZ_NULLABLE RZ_OWN RzInquiryBCFG *bcfg); +RZ_IPI bool rz_inquiry_bcfg_add_block(RzInquiryBCFG *cfg, ut64 addr, ut64 size); + +RZ_IPI bool rz_inquiry_bcfg_get_block(const RzInquiryBCFG *cfg, ut64 bb_addr, RZ_OUT RzInquiryBlock *bb); +RZ_IPI bool rz_inquiry_bcfg_del_out_edges(RzInquiryBCFG *cfg, ut64 bb_addr); +RZ_IPI bool rz_inquiry_bcfg_add_edge(RzInquiryBCFG *cfg, ut64 from_bb, ut64 to_bb, RzInquiryBCFGEdgeType type); +RZ_IPI bool rz_inquiry_bcfg_add_edge_xref(RzInquiryBCFG *cfg, const RzVector /**/ *xrefs); +RZ_IPI bool rz_inquiry_bcfg_del_edge(RzInquiryBCFG *cfg, ut64 from_bb, ut64 to_bb); +RZ_API RZ_OWN RzIterator /**/ *rz_inquiry_bcfg_get_outgoing_edges(const RzInquiryBCFG *cfg, ut64 bb_addr); +RZ_API RZ_OWN RzIterator /**/ *rz_inquiry_bcfg_get_incoming_edges(const RzInquiryBCFG *cfg, ut64 bb_addr); + +RZ_IPI bool rz_inquiry_bcfg_reduce(RzInquiryBCFG *cfg); +RZ_IPI RZ_OWN char *rz_inquiry_bcfg_as_dot(const RzInquiryBCFG *bcfg, RZ_NULLABLE const char *name); + +#endif // RZ_INQUIRY_BCFG_H diff --git a/librz/include/rz_lib.h b/librz/include/rz_lib.h index f652ba80a4..e0a00bfcea 100644 --- a/librz/include/rz_lib.h +++ b/librz/include/rz_lib.h @@ -51,6 +51,7 @@ typedef enum { RZ_LIB_TYPE_EGG, ///< rz_egg plugin RZ_LIB_TYPE_DEMANGLER, ///< demanglers RZ_LIB_TYPE_ARCH, ///< demanglers + RZ_LIB_TYPE_INTERPRETER, ///< Analysis interpreters RZ_LIB_TYPE_UNKNOWN } RzLibType; diff --git a/librz/include/rz_th.h b/librz/include/rz_th.h index aedbcf24a8..9a4b2109e4 100644 --- a/librz/include/rz_th.h +++ b/librz/include/rz_th.h @@ -104,6 +104,7 @@ RZ_API bool rz_th_queue_is_full(RZ_NONNULL RzThreadQueue *queue); RZ_API size_t rz_th_queue_size(RZ_NONNULL RzThreadQueue *queue); RZ_API void rz_th_queue_close_when_empty(RZ_NONNULL RzThreadQueue *queue); RZ_API void rz_th_queue_close(RZ_NONNULL RzThreadQueue *queue); +RZ_API void rz_th_queue_open(RZ_NONNULL RzThreadQueue *queue); RZ_API bool rz_th_queue_is_closed(RZ_NONNULL RzThreadQueue *queue); RZ_API RZ_OWN RzAtomicBool *rz_atomic_bool_new(bool value); diff --git a/librz/inquiry/absint/interpreter.c b/librz/inquiry/absint/interpreter.c index d141c0898a..046120f24e 100644 --- a/librz/inquiry/absint/interpreter.c +++ b/librz/inquiry/absint/interpreter.c @@ -639,6 +639,7 @@ static void report_yield_xref( if (!xref) { goto cleanup; } + xref->bb_addr = 0; // TODO? ctx->astate->bb_addr; xref->from = from; xref->to = to_addr; xref->type = type; diff --git a/librz/inquiry/absint/val_domain_constant.c b/librz/inquiry/absint/val_domain_constant.c index 1e72948f8a..e56a703725 100644 --- a/librz/inquiry/absint/val_domain_constant.c +++ b/librz/inquiry/absint/val_domain_constant.c @@ -8,7 +8,7 @@ * This is the abstract domain of either a single constant value or the universal set. */ -#include +#include typedef struct { /** @@ -251,3 +251,19 @@ RZ_IPI RzAbsIntValueDomain rz_absint_value_domain_const = { .eval_binop = eval_binop, .eval_unop = eval_unop }; + +RZ_API RzInquiryPlugin rz_inquiry_plugin_interpreter_prototype = { + .name = "abstr_int_prototype", + .author = "Rot127", + .version = "0.1p", + .desc = "A prototype interpreter for constant/top abstractions.", + .license = "LGPL-3.0-only", + .value_domain = &rz_absint_value_domain_const, +}; + +#ifndef RZ_PLUGIN_INCORE +RZ_API RzLibStruct rizin_plugin = { + .type = RZ_LIB_TYPE_INTERPRETER, + .data = &interpreter_prototype +}; +#endif diff --git a/librz/inquiry/algorithms/revng_fcn_detection.c b/librz/inquiry/algorithms/revng_fcn_detection.c new file mode 100644 index 0000000000..8d811bc02f --- /dev/null +++ b/librz/inquiry/algorithms/revng_fcn_detection.c @@ -0,0 +1,260 @@ +// SPDX-FileCopyrightText: 2026 RizinOrg +// SPDX-License-Identifier: LGPL-3.0-only + +/** + * \file This is the function detection algorithm inspired by + * paper: "REV.NG: A Unified Binary Analysis Framework to Recover CFGs and Function Boundaries" + * chapter: "4.2 Function boundaries recovery" + * doi: 10.1145/3033019.3033028 + * + * The actually algorithm is really simple. + * + * TERMS: + * + * Basic Block (BB): A sequence of instructions having exactly one entry point and + * one jump/call/exit at the end. + * Call candidate: RzAnalysisCallCandidate + * Candidate function entry points (CFEP): Addresses of possible function entry point. + * Return Addresses: Address after a call candidate BB, with an xref to it. + * Basic Block CFG: Control Flow Graph with basic blocks as nodes. + * + * IN: + * - Call candidates. + * - CFEPs + * - Return Addresses + * - Basic Block CFG (bcfg) + * + * ALGO: + * + * It simply iterates over all CFEPs. + * For each one it follows its edges in the bcfg. + * If an edge belongs to a call, it is NOT taken. + * + * Every walked edge and the basic blocks are added to the function. + * + * Tail calls are not modelled. + * + * OUT: + * - List of functions + * - Each functions starts with one CFEP and is a sub-graph in the bcfg. + */ + +#include "rz_analysis.h" +#include "rz_inquiry/rz_bcfg.h" +#include "rz_types_base.h" +#include "rz_util/ht_up.h" +#include "rz_util/rz_assert.h" +#include "rz_util/rz_graph.h" +#include "rz_util/rz_iterator.h" +#include "rz_util/rz_log.h" +#include "rz_util/rz_set.h" +#include "rz_vector.h" +#include +#include +#include + +static int cmp(const ut64 *a, const ut64 *b, void *user) { + if (*a > *b) { + return 1; + } else if (*a < *b) { + return -1; + } + return 0; +} + +static bool jumps_to_ignored_code(const RzVector *v, ut64 jump_target) { + void *it; + rz_vector_foreach (v, it) { + RzInterval *itv = it; + if (rz_itv_contain(*itv, jump_target)) { + return true; + } + } + return false; +} + +static void recurse_into_fcn_bbs( + RzInquiryFunction *fcn, + ut64 predecessor_bb_addr, + ut64 this_bb_addr, + RzInquiryBCFGEdgeType edge_type, + RzSetU *visited_fcn_bbs, + const RzVector /**/ *cfep_addresses, + const HtUP /**/ *call_candidates, + const RzInquiryBCFG *binary_bcfg, + RZ_NONNULL const RzVector /**/ *ignored_code) { + if (rz_set_u_contains(visited_fcn_bbs, this_bb_addr)) { + return; + } + rz_set_u_add(visited_fcn_bbs, this_bb_addr); + + // + // Add edge + // + RzInquiryBlock this_bb = { 0 }; + if (!rz_inquiry_bcfg_get_block(binary_bcfg, this_bb_addr, &this_bb)) { + rz_warn_if_reached(); + goto err_return; + } + if (!rz_inquiry_bcfg_add_block(fcn->bcfg, this_bb.addr, this_bb.size)) { + rz_warn_if_reached(); + goto err_return; + } + + if (edge_type != RZ_INQUIRY_BCFG_EDGE_TYPE_NONE) { + RzInquiryBlock from_bb = { 0 }; + if (!rz_inquiry_bcfg_get_block(binary_bcfg, predecessor_bb_addr, &from_bb)) { + rz_warn_if_reached(); + goto err_return; + } + if (!rz_inquiry_bcfg_add_block(fcn->bcfg, from_bb.addr, from_bb.size)) { + rz_warn_if_reached(); + goto err_return; + } + if (!rz_inquiry_bcfg_add_edge(fcn->bcfg, predecessor_bb_addr, this_bb_addr, edge_type)) { + rz_warn_if_reached(); + goto err_return; + } + } + + // + // Visit neighbors + // + RzIterator *successors = rz_inquiry_bcfg_get_outgoing_edges(binary_bcfg, this_bb_addr); + if (!successors) { + // Node has no successors. + goto err_return; + } + + const RzGraphEdge *e; + rz_iterator_foreach(successors, e) { + RzInquiryBCFGEdgeType etype = (RzInquiryBCFGEdgeType)(utptr)rz_graph_edge_get_data(e); + ut64 succ_addr = rz_graph_node_get_id(rz_graph_edge_get_to(e)); + switch (etype) { + case RZ_INQUIRY_BCFG_EDGE_TYPE_NONE: + case RZ_INQUIRY_BCFG_EDGE_TYPE_CF: + case RZ_INQUIRY_BCFG_EDGE_TYPE_JMP: + // Just an edge between two basic blocks. + break; + case RZ_INQUIRY_BCFG_EDGE_TYPE_CALL_RET: + // This is the next instruction packet after a call candidate. + // It might be an non-existing edge which was only added as a guess. + // In general all instructions after a call are added to the with + // an edge, to increase coverage. + // Even for call which never return (being a tail call or exit). + // + // The prototype doesn't handle tail calls. But to not loose too much + // precission we can check here if the NPC after the call (this_bb address), + // is a candidate function entry point. + // If so, we can assume that the NPC is in fact not a return point of a procedure. + // Hence, it shouldn't be added to the function CFG. + if (rz_vector_contains(cfep_addresses, &succ_addr)) { + continue; + } + break; + case RZ_INQUIRY_BCFG_EDGE_TYPE_CALL: + case RZ_INQUIRY_BCFG_EDGE_TYPE_RETURN: + continue; + } + if (jumps_to_ignored_code(ignored_code, succ_addr)) { + continue; + } + + recurse_into_fcn_bbs( + fcn, + this_bb_addr, + succ_addr, + etype, + visited_fcn_bbs, + cfep_addresses, + call_candidates, + binary_bcfg, + ignored_code); + } + rz_iterator_free(successors); + +err_return: + return; +} + +static void fill_candidate_fcn_entry_points( + const RzInquiryBCFG *binary_bcfg, + const HtUP /**/ *call_candidates, + RzVector *cfep_addresses) { + void **it; + RzIterator *iter = ht_up_as_iter(call_candidates); + rz_iterator_foreach(iter, it) { + RzAnalysisCallCandidate *cc = *it; + RzIterator *predecessor = rz_inquiry_bcfg_get_outgoing_edges(binary_bcfg, cc->bb_addr); + const RzGraphEdge *e; + rz_iterator_foreach(predecessor, e) { + RzInquiryBCFGEdgeType type = (RzInquiryBCFGEdgeType)(utptr)rz_graph_edge_get_data(e); + if (type == RZ_INQUIRY_BCFG_EDGE_TYPE_CALL_RET) { + // TODO: Remove this check + const RzGraphNode *nr = rz_graph_edge_get_to(e); + ut64 target = rz_graph_node_get_id(nr); + rz_warn_if_fail(target == cc->npc); + } else if (type == RZ_INQUIRY_BCFG_EDGE_TYPE_CALL) { + const RzGraphNode *nc = rz_graph_edge_get_to(e); + ut64 target = rz_graph_node_get_id(nc); + rz_vector_push(cfep_addresses, &target); + RZ_LOG_DEBUG("Add cfep at 0x%" PFMT64x " (call: 0x%" PFMT64x ") " + "based on BB 0x%" PFMT64x "\n", + cc->bb_addr, cc->candidate_addr, target); + } + } + rz_iterator_free(predecessor); + } + rz_iterator_free(iter); + rz_vector_sort(cfep_addresses, (RzVectorComparator)cmp, false, NULL); +} + +RZ_API bool rz_inquiry_algo_revng_fcn_detection( + RzSetU *symbol_addresses, + RZ_NONNULL const HtUP /**/ *call_candidates, + RZ_NONNULL const RzInquiryBCFG *binary_bcfg, + RZ_NONNULL RZ_OUT RzPVector /**/ *fcns, + RZ_NONNULL const RzVector /**/ *ignored_code) { + rz_return_val_if_fail(call_candidates && binary_bcfg && fcns, false); + + // Candidate function entry points + RzVector *cfep_addresses = rz_vector_new(sizeof(ut64), NULL, NULL); + RzIterator *iter = rz_set_u_as_iter(symbol_addresses); + ut64 *addr; + rz_iterator_foreach(iter, addr) { + rz_vector_push(cfep_addresses, addr); + } + rz_iterator_free(iter); + fill_candidate_fcn_entry_points(binary_bcfg, call_candidates, cfep_addresses); + + // Set of handled cfep + RzSetU *cfep_handled = rz_set_u_new(); + + ut64 *elem; + rz_vector_foreach (cfep_addresses, elem) { + ut64 cfep_addr = *elem; + if (rz_set_u_contains(cfep_handled, cfep_addr)) { + continue; + } + + RzSetU *visited_bbs = rz_set_u_new(); + RzInquiryFunction *fcn = rz_inquiry_function_new(); + rz_vector_push(fcn->entry_points, &cfep_addr); + recurse_into_fcn_bbs(fcn, + UT64_MAX, + cfep_addr, + RZ_INQUIRY_BCFG_EDGE_TYPE_NONE, + visited_bbs, + cfep_addresses, + call_candidates, + binary_bcfg, + ignored_code); + rz_set_u_free(visited_bbs); + rz_set_u_add(cfep_handled, cfep_addr); + rz_pvector_push(fcns, fcn); + } + + rz_set_u_free(cfep_handled); + rz_vector_free(cfep_addresses); + return true; +} diff --git a/librz/inquiry/bcfg.c b/librz/inquiry/bcfg.c new file mode 100644 index 0000000000..3be87c9b42 --- /dev/null +++ b/librz/inquiry/bcfg.c @@ -0,0 +1,349 @@ +// SPDX-FileCopyrightText: 2026 RizinOrg +// SPDX-License-Identifier: LGPL-3.0-only + +#include "rz_analysis.h" +#include "rz_types_base.h" +#include "rz_util/rz_assert.h" +#include "rz_util/rz_graph.h" +#include "rz_util/rz_iterator.h" +#include "rz_vector.h" +#include + +static ut64 hash_node(const void *data) { + const RzInquiryBlock *bb = data; + return bb->addr; +} + +static RZ_OWN char *node_formatter(const RzGraphNode *n) { + const RzInquiryBlock *bb = rz_graph_node_get_data(n); + return rz_str_newf("[label=\"0x%" PFMT64x ":%" PFMT64u "\"]", + bb->addr, bb->size); +} + +static RZ_OWN char *edge_formatter(const RzGraphEdge *e) { + RzInquiryBCFGEdgeType type = (utptr)rz_graph_edge_get_data(e); + switch (type) { + case RZ_INQUIRY_BCFG_EDGE_TYPE_NONE: + return rz_str_dup("[label=Unknown]"); + case RZ_INQUIRY_BCFG_EDGE_TYPE_JMP: + case RZ_INQUIRY_BCFG_EDGE_TYPE_CF: + return NULL; + case RZ_INQUIRY_BCFG_EDGE_TYPE_CALL_RET: + return rz_str_dup("[style=dotted label=npc]"); + case RZ_INQUIRY_BCFG_EDGE_TYPE_CALL: + return rz_str_dup("[style=dotted label=call]"); + case RZ_INQUIRY_BCFG_EDGE_TYPE_RETURN: + return rz_str_dup("[style=dotted label=ret]"); + } + rz_warn_if_reached(); + return NULL; +} + +RZ_IPI RZ_OWN char *rz_inquiry_bcfg_as_dot(const RzInquiryBCFG *bcfg, RZ_NULLABLE const char *name) { + rz_return_val_if_fail(bcfg, NULL); + return rz_graph_as_dot_str(bcfg->graph, name, node_formatter, edge_formatter); +} + +RZ_IPI RZ_OWN RzInquiryBCFG *rz_inquiry_bcfg_new(RzGraphImplType impl_type) { + RzInquiryBCFG *bcfg = RZ_NEW0(RzInquiryBCFG); + if (!bcfg) { + return NULL; + } + bcfg->graph = rz_graph_new(impl_type, hash_node, free, NULL); + if (!bcfg->graph) { + rz_inquiry_bcfg_free(bcfg); + return NULL; + } + return bcfg; +} + +RZ_IPI void rz_inquiry_bcfg_free(RZ_NULLABLE RZ_OWN RzInquiryBCFG *bcfg) { + if (!bcfg) { + return; + } + rz_graph_free(bcfg->graph); + free(bcfg); +} + +static bool edge_from(const RzGraphEdge *e, void *addr) { + ut64 from_addr = (utptr)addr; + return rz_graph_node_get_id(rz_graph_edge_get_from(e)) == from_addr; +} + +RZ_IPI bool rz_inquiry_bcfg_del_out_edges(RzInquiryBCFG *cfg, ut64 bb_addr) { + rz_return_val_if_fail(cfg, false); + return rz_graph_del_edges(cfg->graph, edge_from, RZ_GRAPH_INT_AS_DATA(bb_addr)) != RZ_GRAPH_STATUS_ERR; +} + +/** + * \brief Adds an edge to the block CFG. + * + * \param cfg The block CFG to edit. + * \param from_bb The address of the block with the branch. + * Not the address of the branch instruction! + * \param to_bb The address of the block the branch leads to. + * \param type The type of the edge. + * + * \return True if edge was added. False for error or if a node doesn't exist. + */ +RZ_IPI bool rz_inquiry_bcfg_add_edge(RzInquiryBCFG *cfg, ut64 from_bb, ut64 to_bb, RzInquiryBCFGEdgeType type) { + RzGraphStatus s = rz_graph_add_edge_by_id(cfg->graph, from_bb, to_bb, RZ_GRAPH_INT_AS_DATA(type)); + if (s == RZ_GRAPH_STATUS_OK || s == RZ_GRAPH_STATUS_EXISTED) { + return true; + } + return false; +} + +RZ_IPI bool rz_inquiry_bcfg_del_edge(RzInquiryBCFG *cfg, ut64 from_bb, ut64 to_bb) { + return rz_graph_del_edge_by_id(cfg->graph, from_bb, to_bb) != RZ_GRAPH_STATUS_ERR; +} + +static bool is_cf_edge(const RzGraphEdge *e, void *unused) { + RzInquiryBCFGEdgeType type = (utptr)rz_graph_edge_get_data(e); + return type == RZ_INQUIRY_BCFG_EDGE_TYPE_CF || type == RZ_INQUIRY_BCFG_EDGE_TYPE_NONE; +} + +/** + * \brief Updates or adds an edge to the block CFG. + * Only edges of type RZ_INQUIRY_BCFG_EDGE_TYPE_CF are updated to \p type. + * Otherwise the edge is not updated. + * + * \param cfg The block CFG to edit. + * \param from_bb The address of the block with the branch. + * Not the address of the branch instruction! + * \param to_bb The address of the block the branch leads to. + * \param type The type of the edge. + * + * \return True if edge was added. False in case of error. + */ +RZ_IPI bool rz_inquiry_bcfg_update_edge(RzInquiryBCFG *cfg, ut64 from_bb, ut64 to_bb, RzInquiryBCFGEdgeType type) { + return rz_graph_update_edge_by_id(cfg->graph, from_bb, to_bb, RZ_GRAPH_INT_AS_DATA(type), is_cf_edge, NULL) != RZ_GRAPH_STATUS_ERR; +} + +RZ_IPI bool rz_inquiry_bcfg_get_block(const RzInquiryBCFG *cfg, ut64 bb_addr, RZ_OUT RZ_NULLABLE RzInquiryBlock *bb) { + rz_return_val_if_fail(cfg, false); + const RzGraphNode *n = rz_graph_find_node(cfg->graph, bb_addr); + if (!n) { + RZ_LOG_WARN("Could not find BB at 0x%" PFMT64x "\n", bb_addr); + return false; + } + if (bb) { + const RzInquiryBlock *n_data = rz_graph_node_get_data(n); + bb->addr = n_data->addr; + bb->size = n_data->size; + } + return true; +} + +/** + * \brief Neighbors of outgoing edges. + */ +RZ_API RZ_OWN RzIterator /**/ *rz_inquiry_bcfg_get_outgoing_edges(const RzInquiryBCFG *cfg, ut64 bb_addr) { + rz_return_val_if_fail(cfg, NULL); + return rz_graph_out_edges_by_id(cfg->graph, bb_addr); +} + +/** + * \brief Neighbors of incoming edges. + */ +RZ_API RZ_OWN RzIterator /**/ *rz_inquiry_bcfg_get_incoming_edges(const RzInquiryBCFG *cfg, ut64 bb_addr) { + rz_return_val_if_fail(cfg, NULL); + return rz_graph_in_edges_by_id(cfg->graph, bb_addr); +} + +/** + * \brief Does not update the BB if it is already present. + * Returns false if it already exists. + */ +RZ_IPI bool rz_inquiry_bcfg_add_block(RzInquiryBCFG *cfg, ut64 addr, ut64 size) { + RzInquiryBlock *bb = RZ_NEW(RzInquiryBlock); + if (!bb) { + return false; + } + bb->addr = addr; + bb->size = size; + rz_graph_add_node(cfg->graph, bb, NULL); + // const RzInquiryBB *nbb = rz_graph_node_get_data(n); + // if (nbb->size != size) { + // rz_warn_if_reached(); + // } + return true; +} + +RZ_IPI bool rz_inquiry_bcfg_add_edge_xref(RzInquiryBCFG *cfg, const RzVector /**/ *xrefs) { + RzAnalysisXRef *xref; + rz_vector_foreach (xrefs, xref) { + switch (xref->type) { + case RZ_ANALYSIS_XREF_TYPE_CODE: + if (!rz_inquiry_bcfg_update_edge(cfg, xref->bb_addr, xref->to, RZ_INQUIRY_BCFG_EDGE_TYPE_JMP)) { + RZ_LOG_DEBUG("Did not add JMP edge: 0x%" PFMT64x " -> 0x%" PFMT64x "\n", xref->bb_addr, xref->to); + } + break; + case RZ_ANALYSIS_XREF_TYPE_CALL: + if (!rz_inquiry_bcfg_update_edge(cfg, xref->bb_addr, xref->to, RZ_INQUIRY_BCFG_EDGE_TYPE_CALL)) { + RZ_LOG_DEBUG("Did not add CALL edge: 0x%" PFMT64x " -> 0x%" PFMT64x "\n", xref->bb_addr, xref->to); + } + RzInquiryBlock bb = { 0 }; + if (!rz_inquiry_bcfg_get_block(cfg, xref->bb_addr, &bb)) { + rz_warn_if_reached(); + break; + } + ut64 ret_addr = bb.addr + bb.size; + if (!rz_inquiry_bcfg_update_edge(cfg, xref->bb_addr, ret_addr, RZ_INQUIRY_BCFG_EDGE_TYPE_CALL_RET)) { + RZ_LOG_DEBUG("Did not add CALL_RET edge: 0x%" PFMT64x " -> 0x%" PFMT64x "\n", xref->bb_addr, ret_addr); + } + break; + case RZ_ANALYSIS_XREF_TYPE_RETURN: + if (!rz_inquiry_bcfg_update_edge(cfg, xref->bb_addr, xref->to, RZ_INQUIRY_BCFG_EDGE_TYPE_RETURN)) { + RZ_LOG_DEBUG("Did not add RETURN edge: 0x%" PFMT64x " -> 0x%" PFMT64x "\n", xref->bb_addr, xref->to); + } + break; + case RZ_ANALYSIS_XREF_TYPE_NULL: + case RZ_ANALYSIS_XREF_TYPE_DATA: + case RZ_ANALYSIS_XREF_TYPE_STRING: + case RZ_ANALYSIS_XREF_TYPE_MEM_WRITE: + case RZ_ANALYSIS_XREF_TYPE_CALL_RET: + continue; + } + } + return true; +} + +static int cmp(const RzInquiryBlock *a, const RzInquiryBlock *b, void *user) { + if (a->addr < b->addr) { + return -1; + } else if (a->addr > b->addr) { + return 1; + } + return 0; +} + +/** + * \brief Reduces all blocks in the cfg to their minimum size. + * Removing duplicates and split overlapping blocks to basic blocks. + * + * This function makes each block just have a single entry point. + * So each becomes a basic block (one Entry/one Exit). + * + * Example: + * + * +- 0x1000 + * | + * | + * | +- 0x1058 + * | | + * | | +- 0x105c + * | | | + * | | | + * | | | + * +-+-+- 0x1080 + * | + * | JUMP + * v + * +-- 0x1084 + * | + * | + * | +- 0x108c + * | | + * | | + * +-+- 0x10a0 + * + * Gets converted to: + * + * +-- 0x1000 + * | + * +-- + * | CTRL FLOW + * v + * +-- 0x1058 + * | + * +-- + * | CTRL FLOW + * v + * +-- 0x105c + * | + * | + * | + * +-- 0x1080 + * | + * | JUMP + * v + * +-- 0x1084 + * | + * +-- + * | CTRL FLOW + * v + * +-- 0x108c + * | + * | + * +-- 0x10a0 + */ +RZ_IPI bool rz_inquiry_bcfg_reduce(RzInquiryBCFG *cfg) { + RzPVector blocks = { 0 }; + rz_pvector_init(&blocks, NULL); + rz_pvector_reserve(&blocks, rz_graph_get_n_nodes(cfg->graph)); + + RzGraphNode *n; + RzIterator *iter = rz_graph_get_nodes(cfg->graph); + rz_iterator_foreach(iter, n) { + RzInquiryBlock *b = rz_graph_node_get_data_mut(n); + rz_pvector_push(&blocks, b); // Cast because API is not constified. + } + rz_iterator_free(iter); + + // Sorting by starting address. + // Note that this means overlapping blocks (with the same end address) + // will lie sequentially in this vector. + rz_pvector_sort(&blocks, (RzPVectorComparator)cmp, NULL); + + RzVector outedges = { 0 }; + rz_vector_init(&outedges, sizeof(ut64), NULL, NULL); + // It is unlikely any node will have more than 8 outgoing edges. + rz_vector_reserve(&outedges, 8); + + size_t n_blocks = rz_pvector_len(&blocks); + for (size_t i = 0; n_blocks && i < n_blocks - 1; ++i) { + RzInquiryBlock *a = rz_pvector_at(&blocks, i); + + // Split of all blocks a overlaps with. + for (; i < n_blocks - 1; ++i) { + RzInquiryBlock *b = rz_pvector_at(&blocks, i + 1); + if ((a->addr + a->size) != (b->addr + b->size)) { + // End addresses don't match => b lies not within a. + break; + } + // b and a have the same end address => b lies within a. + // Shrink a. + a->size = b->addr - a->addr; + RzIterator *out_iter = rz_graph_out_edges_by_id(cfg->graph, a->addr); + if (!out_iter) { + // No edges to update. + // Check for blocks b overlaps with. + a = b; + continue; + } + + // Replace a's outgoing edges with a single edge to b, + RzGraphEdge *e; + rz_iterator_foreach(out_iter, e) { + const RzGraphNode *to_node = rz_graph_edge_get_to(e); + ut64 to = rz_graph_node_get_id(to_node); + rz_vector_push(&outedges, &to); + } + rz_iterator_free(out_iter); + + ut64 *to; + rz_vector_foreach (&outedges, to) { + rz_graph_del_edge_by_id(cfg->graph, a->addr, *to); + } + rz_vector_purge(&outedges); + rz_graph_add_edge_by_id(cfg->graph, a->addr, b->addr, RZ_GRAPH_INT_AS_DATA(RZ_INQUIRY_BCFG_EDGE_TYPE_CF)); + // Check for blocks b overlaps with. + a = b; + } + } + rz_vector_fini(&outedges); + rz_pvector_fini(&blocks); + + return true; +} diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c new file mode 100644 index 0000000000..3f72483424 --- /dev/null +++ b/librz/inquiry/inquiry.c @@ -0,0 +1,341 @@ +// SPDX-FileCopyrightText: 2025 RizinOrg +// SPDX-License-Identifier: LGPL-3.0-only + +#include +#include +#include + +#include "rz_analysis.h" +#include "rz_bin.h" +#include "rz_inquiry/rz_bcfg.h" +#include "rz_types.h" +#include "rz_util/ht_pp.h" +#include "rz_util/ht_sp.h" +#include "rz_util/ht_up.h" +#include "rz_util/rz_graph.h" +#include "rz_util/rz_iterator.h" +#include "rz_util/rz_log.h" +#include "rz_util/rz_set.h" +#include "rz_util/rz_str.h" +#include "rz_vector.h" +#include +#include +#include +#include + +#if 0 +RZ_LIB_VERSION(rz_inquiry); + +static RzInquiryPlugin *inquiry_static_plugins[] = { RZ_INQUIRY_STATIC_PLUGINS }; + +RZ_API const size_t rz_inquiry_get_n_plugins() { + return RZ_ARRAY_SIZE(inquiry_static_plugins); +} + +RZ_API RZ_BORROW RzInquiryPlugin *rz_inquiry_get_plugin(size_t index) { + if (index >= RZ_ARRAY_SIZE(inquiry_static_plugins)) { + return NULL; + } + return inquiry_static_plugins[index]; +} +#endif + +RZ_API bool rz_inquiry_plugin_add(RZ_BORROW RZ_NONNULL RzInquiry *inquiry, RZ_OWN RZ_NONNULL RzInquiryPlugin *plugin) { + rz_return_val_if_fail(inquiry && plugin, false); + if (!plugin->value_domain) { + rz_warn_if_reached(); + return false; + } + + if (!ht_sp_insert(inquiry->plugins, plugin->name, plugin)) { + RZ_LOG_WARN("Plugin '%s' was already added.\n", plugin->name); + return true; + } + + void **p_data = RZ_NEW0(void *); + if (!ht_sp_insert(inquiry->plugins_data, plugin->name, p_data)) { + rz_warn_if_reached(); + return false; + } + return true; +} + +RZ_API bool rz_inquiry_plugin_del(RZ_BORROW RZ_NONNULL RzInquiry *inquiry, RZ_OWN RZ_NONNULL RzInquiryPlugin *plugin) { + rz_return_val_if_fail(inquiry && plugin, false); + + void **p_data = ht_sp_find(inquiry->plugins_data, plugin->name, NULL); +#if 0 + if (plugin->p_interpreter->fini) { + plugin->p_interpreter->fini(p_data ? *p_data : NULL); + } +#endif + free(p_data); + if (plugin->value_domain) { + return ht_sp_delete(inquiry->plugins, plugin->name); + } + rz_warn_if_reached(); + return false; +} + +RZ_API void rz_inquiry_function_free(RZ_NULLABLE RZ_OWN RzInquiryFunction *fcn) { + if (!fcn) { + return; + } + rz_inquiry_bcfg_free(fcn->bcfg); + rz_vector_free(fcn->entry_points); + free(fcn); +} + +RZ_IPI RZ_OWN RzInquiryFunction *rz_inquiry_function_new() { + RzInquiryFunction *fcn = RZ_NEW0(RzInquiryFunction); + if (!fcn) { + return NULL; + } + fcn->bcfg = rz_inquiry_bcfg_new(RZ_GRAPH_IMPL_LIST); + fcn->entry_points = rz_vector_new(sizeof(ut64), NULL, NULL); + if (!fcn->bcfg || !fcn->entry_points) { + rz_inquiry_function_free(fcn); + return NULL; + } + return fcn; +} + +RZ_API RZ_OWN char *rz_inquiry_function_str(const RzInquiryFunction *fcn) { + RzStrBuf *buf = rz_strbuf_new(""); + rz_strbuf_append(buf, "ifcn: ["); + ut64 *it; + size_t i = 0; + rz_vector_foreach (fcn->entry_points, it) { + rz_strbuf_appendf(buf, "%s0x%" PFMT64x, (i++ > 0 ? ", " : " "), *it); + } + rz_strbuf_append(buf, " ]\n"); + RzIterator *iter = rz_graph_get_nodes(fcn->bcfg->graph); + RzGraphNode *n; + rz_iterator_foreach(iter, n) { + const RzInquiryBlock *bb = rz_graph_node_get_data(n); + rz_strbuf_appendf(buf, "\t0x%" PFMT64x ":0x%" PFMT64x "\n", bb->addr, bb->size); + } + rz_iterator_free(iter); + return rz_strbuf_drain(buf); +} + +RZ_API RZ_OWN RzInquiry *rz_inquiry_new(void) { + RzInquiry *iq = RZ_NEW0(RzInquiry); + if (!iq) { + return NULL; + } + iq->plugins = ht_sp_new(HT_STR_CONST, NULL, NULL); + iq->plugins_data = ht_sp_new(HT_STR_CONST, NULL, NULL); + iq->call_candidates = ht_up_new(NULL, free); + iq->dynamic_xrefs = rz_vector_new(sizeof(RzAnalysisXRef), NULL, NULL); + iq->bcfg = rz_inquiry_bcfg_new(RZ_GRAPH_IMPL_LIST); + if (!iq->plugins || !iq->plugins_data || !iq->bcfg) { + ht_sp_free(iq->plugins); + ht_sp_free(iq->plugins_data); + rz_inquiry_bcfg_free(iq->bcfg); + free(iq); + return NULL; + } + +#if 0 + for (size_t i = 0; i < RZ_ARRAY_SIZE(inquiry_static_plugins); ++i) { + rz_inquiry_plugin_add(iq, inquiry_static_plugins[i]); + } +#endif + return iq; +} + +RZ_API void rz_inquiry_free(RZ_OWN RZ_NULLABLE RzInquiry *iq) { + if (!iq) { + return; + } +#if 0 + for (size_t i = 0; i < RZ_ARRAY_SIZE(inquiry_static_plugins); ++i) { + rz_inquiry_plugin_del(iq, inquiry_static_plugins[i]); + } +#endif + ht_sp_free(iq->plugins); + ht_sp_free(iq->plugins_data); + ht_up_free(iq->call_candidates); + rz_inquiry_bcfg_free(iq->bcfg); + rz_vector_free(iq->dynamic_xrefs); + free(iq); +} + +RZ_IPI void rz_inquiry_add_xref(RzInquiry *iq, const RzAnalysisXRef *xref) { + rz_vector_push(iq->dynamic_xrefs, (void *)xref); +} + +RZ_API bool rz_inquiry_xref_interpreter_filter(RZ_NONNULL const RzAnalysisXRef *xref, RZ_NONNULL const RzPVector /**/ *allowed_segments) { + rz_return_val_if_fail(xref && allowed_segments, false); + void **it; + rz_pvector_foreach (allowed_segments, it) { + const RzBinSection *sec = *it; + + ut64 start = sec->vaddr; + ut64 end = start + sec->vsize; + if (RZ_BETWEEN_EXCL(start, xref->to, end)) { + switch (xref->type) { + case RZ_ANALYSIS_XREF_TYPE_CALL_RET: + case RZ_ANALYSIS_XREF_TYPE_CALL: + case RZ_ANALYSIS_XREF_TYPE_CODE: + case RZ_ANALYSIS_XREF_TYPE_RETURN: + return sec->perm & RZ_PERM_X; + case RZ_ANALYSIS_XREF_TYPE_MEM_WRITE: + return sec->perm & RZ_PERM_W; + case RZ_ANALYSIS_XREF_TYPE_MEM_READ: + case RZ_ANALYSIS_XREF_TYPE_STRING: + return sec->perm & RZ_PERM_R; + case RZ_ANALYSIS_XREF_TYPE_NULL: + rz_warn_if_reached(); + return false; + } + } + } + return false; +} + +RZ_API bool rz_inquiry_get_fcn_symbol_addr(RzCore *core, RZ_OUT RzSetU *symbol_targets) { + rz_return_val_if_fail(core && symbol_targets, false); + RzPVector /**/ *sections = rz_bin_object_get_sections(core->bin->cur->o); + if (!sections) { + rz_warn_if_reached(); + return false; + } + // Add function addresses of function symbols in the executable region. + void **it; + const RzPVector *symbols = rz_bin_object_get_symbols(core->bin->cur->o); + if (!symbols) { + rz_pvector_free(sections); + rz_warn_if_reached(); + return false; + } + rz_pvector_foreach (symbols, it) { + RzBinSymbol *sym = *it; + if (!RZ_STR_EQ(sym->type, RZ_BIN_TYPE_FUNC_STR)) { + continue; + } + void **it2; + rz_pvector_foreach (sections, it2) { + RzBinSection *sec = *it2; + if (sec->perm & RZ_PERM_X && + RZ_BETWEEN_EXCL(sec->vaddr, sym->vaddr, sec->vaddr + sec->vsize)) { + rz_set_u_add(symbol_targets, sym->vaddr); + break; + } + } + } + rz_pvector_free(sections); + return true; +} + +RZ_API bool rz_inquiry_convert_and_add_to_analysis( + RzAnalysis *analysis, + RzInquiry *inquiry, + const RzPVector /**/ *fcns, + const RzPVector /**/ *symbols) { + rz_return_val_if_fail(analysis && inquiry && fcns && symbols, false); + // Add all discovered binary blocks to analysis + + RzIterator *iter = rz_graph_get_nodes(inquiry->bcfg->graph); + RzGraphNode *n; + rz_iterator_foreach(iter, n) { + const RzInquiryBlock *bb = rz_graph_node_get_data(n); + rz_analysis_add_bb(analysis, bb->addr, bb->size); + RzAnalysisBlock *abb = rz_analysis_get_block_at(analysis, bb->addr); + RzIterator *out_edges = rz_inquiry_bcfg_get_outgoing_edges(inquiry->bcfg, bb->addr); + if (!out_edges) { + continue; + } + RzGraphEdge *e; + rz_iterator_foreach(out_edges, e) { + ut64 target = rz_graph_node_get_id(rz_graph_edge_get_to(e)); + RzInquiryBCFGEdgeType type = (RzInquiryBCFGEdgeType)(utptr)rz_graph_edge_get_data(e); + switch (type) { + default: + continue; + case RZ_INQUIRY_BCFG_EDGE_TYPE_CALL_RET: + case RZ_INQUIRY_BCFG_EDGE_TYPE_CF: + case RZ_INQUIRY_BCFG_EDGE_TYPE_JMP: { + if (abb->jump == UT64_MAX && abb->fail != target) { + abb->jump = target; + } else if (abb->fail == UT64_MAX && abb->jump != target) { + abb->fail = target; + } else if (abb->fail != target && abb->jump != target) { + RZ_LOG_WARN("The basic block at 0x%" PFMT64x " has more than two outgoing edges.\n" + "\t\tHas jump = 0x%" PFMT64x " fail = 0x%" PFMT64x ". Will miss = 0x%" PFMT64x " (%d)\n", + bb->addr, abb->jump, abb->fail, + target, type); + } + break; + } + } + } + rz_iterator_free(out_edges); + } + rz_iterator_free(iter); + + RzAnalysisXRef *xref; + rz_vector_foreach (inquiry->dynamic_xrefs, xref) { + rz_analysis_xrefs_set(analysis, xref->from, xref->to, xref->type); + } + + // Convert the Inquiry functions to analysis function. + void **it; + rz_pvector_foreach (fcns, it) { + RzInquiryFunction *fcn = *it; + + ut64 fcn_addr = *(ut64 *)rz_vector_head(fcn->entry_points); + char new_fcn_name[64] = { 0 }; + void **it; + rz_pvector_foreach (symbols, it) { + RzBinSymbol *s = *it; + if (s->vaddr == fcn_addr && RZ_STR_EQ(s->type, RZ_BIN_TYPE_FUNC_STR)) { + rz_strf(new_fcn_name, "sym.%s", s->name); + break; + } + } + if (new_fcn_name[0] == '\0') { + rz_strf(new_fcn_name, "fcn_0x%" PFMT64x, fcn_addr); + } + RzAnalysisFunction *afcn = rz_analysis_create_function(analysis, new_fcn_name, fcn_addr, RZ_ANALYSIS_FCN_TYPE_FCN); + if (!afcn) { + rz_warn_if_reached(); + continue; + } + + RzIterator *iter = rz_graph_get_nodes(fcn->bcfg->graph); + RzGraphNode *n; + rz_iterator_foreach(iter, n) { + const RzInquiryBlock *bb = rz_graph_node_get_data(n); + RzAnalysisBlock *abb = rz_analysis_get_block_at(analysis, bb->addr); + if (!abb && !(abb = rz_analysis_create_block(analysis, bb->addr, bb->size))) { + rz_warn_if_reached(); + continue; + } + rz_analysis_function_add_block(afcn, abb); + } + rz_iterator_free(iter); + } + return true; +} + +RZ_API bool rz_inquiry_function_deduction( + RZ_NONNULL RZ_BORROW RzAnalysis *analysis, + RZ_NONNULL RZ_BORROW RzInquiry *inquiry, + RZ_NONNULL RZ_BORROW RzSetU *symbol_addresses, + RZ_NONNULL const RzPVector /**/ *symbols, + RZ_NONNULL const RzVector /**/ *ignored_code, + RZ_NONNULL RZ_OUT RzPVector /**/ *inquiry_fcns) { + rz_return_val_if_fail(analysis && inquiry && symbol_addresses && symbols && ignored_code && inquiry_fcns, false); + if (!rz_inquiry_algo_revng_fcn_detection( + symbol_addresses, + inquiry->call_candidates, + inquiry->bcfg, + inquiry_fcns, + ignored_code)) { + rz_warn_if_reached(); + return false; + } + return true; +} diff --git a/librz/inquiry/meson.build b/librz/inquiry/meson.build index c38815ac58..a3882cd312 100644 --- a/librz/inquiry/meson.build +++ b/librz/inquiry/meson.build @@ -2,6 +2,9 @@ # SPDX-License-Identifier: LGPL-3.0-only rz_inquiry_sources = [ + 'algorithms/revng_fcn_detection.c', + 'bcfg.c', + 'inquiry.c', 'il_cache.c', 'absint/interpreter.c', 'absint/driver.c', diff --git a/librz/util/thread_queue.c b/librz/util/thread_queue.c index 8034e38c70..eeb6633dab 100644 --- a/librz/util/thread_queue.c +++ b/librz/util/thread_queue.c @@ -143,6 +143,19 @@ RZ_API void rz_th_queue_close_when_empty(RZ_NONNULL RzThreadQueue *queue) { rz_th_lock_leave(queue->data_lock); } +/** + * \brief Opens a RzThreadQueue (allows to read/write data). + * + * \param queue The RzThreadQueue to close + */ +RZ_API void rz_th_queue_open(RZ_NONNULL RzThreadQueue *queue) { + rz_return_if_fail(queue); + + rz_th_lock_enter(queue->data_lock); + queue->closed = false; + rz_th_lock_leave(queue->data_lock); +} + /** * \brief Closes a RzThreadQueue (once closed you cannot read/write data). * diff --git a/test/db/inquiry/interpreter/unmapped_fcn_loop b/test/db/inquiry/interpreter/unmapped_fcn_loop new file mode 100644 index 0000000000..041fdd6221 --- /dev/null +++ b/test/db/inquiry/interpreter/unmapped_fcn_loop @@ -0,0 +1,155 @@ +NAME=Indirect call x86 +FILE=bins/inquiry/interpreter/prototype/x86_unmapped_fcn_in_loop +CMDS=< CALL -> 0x8000050 reloc.target.run + reloc.target.run+26 0x800006a -> CODE -> 0x800009f reloc.recurse+27 + reloc.target.run+34 0x8000072 -> CALL -> 0x80000b0 sym.function_0 + reloc.target.run+34 0x8000072 -> DATA -> 0x80000d0 section..data + reloc.fcn_arr+14 0x8000083 -> CALL -> 0x8000040 section..text + reloc.recurse+25 0x800009d -> CODE -> 0x8000066 reloc.target.run+22 + addr size traced ninstr jump fail fcns calls xrefs +--------------------------------------------------------------------------------------- +0x8000040 9 0 3 0x08000049 0x08000040 0x08000050 0x08000083 +0x8000049 2 0 2 0x08000040 +0x800004b 5 0 1 0x08000050 +0x8000050 22 0 5 0x08000066 0x08000050 0x08000044 +0x8000066 6 0 2 0x0800009f 0x0800006c 0x08000050 0x0800009d +0x800006c 13 0 3 0x08000079 0x08000050 0x080000b0 +0x8000079 10 0 3 0x08000088 0x08000083 0x08000050 +0x8000083 5 0 1 0x08000088 0x08000050 0x08000040 +0x8000088 23 0 8 0x08000066 0x08000050 +0x800009f 9 0 4 0x08000050 0x0800006a +0x80000a8 8 0 1 0x080000b0 +0x80000b0 14 0 4 0x080000be 0x080000b0 0x08000440 0x08000072 +0x80000be 2 0 2 0x080000b0 +0x80000c0 14 0 4 0x080000ce 0x080000c0 0x08000440 +0x80000ce 2 0 2 0x080000c0 +0x8000440 1 0 0 0x08000440 +EOF +EXPECT_ERR=< 0x0800005d cmp dword [rbp-0x0c], 0x03 +| ,==< 0x08000061 jnb 0x800008d +| |: 0x08000063 mov eax, dword [rbp-0x0c] +| |: 0x08000066 mov ecx, eax +| |: 0x08000068 mov al, 0x00 +| |: 0x0800006a call qword [rcx*8+obj.fcn_arr] ; sym..data +| |: ; 0x80000e0; RELOC 32 .data @ 0x080000e0 +| |: ; RETURN XREF from sym.function_0 @ 0x80000aa +| |: 0x08000071 mov qword [rbp-0x18], rax +| |: 0x08000075 mov rax, qword [rbp-0x18] +| |: 0x08000079 movzx eax, byte [rax] +| |: 0x0800007c add eax, dword [rbp-0x08] +| |: 0x0800007f mov dword [rbp-0x08], eax +| |: 0x08000082 mov eax, dword [rbp-0x0c] +| |: 0x08000085 add eax, 0x01 +| |: 0x08000088 mov dword [rbp-0x0c], eax +| |`=< 0x0800008b jmp 0x800005d +| `--> 0x0800008d mov eax, dword [rbp-0x08] +| 0x08000090 add rsp, 0x20 +| 0x08000094 pop rbp +\ 0x08000095 ret + 0x08000096 nop word [rax+rax*1], ax + ; CALL XREF from sym.main @ 0x800006a +/ sym.function_0(); +| 0x080000a0 push rbp +| 0x080000a1 mov rbp, rsp +| 0x080000a4 call sym.some_ptr +| ; RETURN XREF from sym.some_ptr @ 0x80000bf +| 0x080000a9 pop rbp +\ 0x080000aa ret + 0x080000ab nop dword [rax+rax*1], eax + ; CALL XREF from sym.function_0 @ 0x80000a4 + ; CALL XREF from sym.function_1 @ 0x80000c4 + ; CALL XREF from sym.function_2 @ 0x80000d4 +/ sym.some_ptr(); +| 0x080000b0 push rbp +| 0x080000b1 mov rbp, rsp +| 0x080000b4 mov rax, obj.z ; sym..bss +| ; 0x80000f8; RELOC 64 .bss @ 0x080000f8 +| 0x080000be pop rbp +\ 0x080000bf ret +/ sym.function_1(); +| 0x080000c0 push rbp +| 0x080000c1 mov rbp, rsp +| 0x080000c4 call sym.some_ptr +| ; RETURN XREF from sym.some_ptr @ 0x80000bf +| 0x080000c9 pop rbp +\ 0x080000ca ret + 0x080000cb nop dword [rax+rax*1], eax +/ sym.function_2(); +| 0x080000d0 push rbp +| 0x080000d1 mov rbp, rsp +| 0x080000d4 call sym.some_ptr +| ; RETURN XREF from sym.some_ptr @ 0x80000bf +| 0x080000d9 pop rbp +\ 0x080000da ret + section..text+42 0x800006a -> CALL -> 0x80000a0 sym.function_0 + section..text+42 0x800006a -> DATA -> 0x80000e0 section..data + reloc..data+30 0x800008b -> CODE -> 0x800005d section..text+29 + sym.function_0+4 0x80000a4 -> CALL -> 0x80000b0 sym.some_ptr + sym.function_0+10 0x80000aa -> RETURN -> 0x8000071 reloc..data+4 + reloc..bss+9 0x80000bf -> RETURN -> 0x80000a9 sym.function_0+9 + reloc..bss+9 0x80000bf -> RETURN -> 0x80000c9 sym.function_1+9 + reloc..bss+9 0x80000bf -> RETURN -> 0x80000d9 sym.function_2+9 + sym.function_1+4 0x80000c4 -> CALL -> 0x80000b0 sym.some_ptr + sym.function_2+4 0x80000d4 -> CALL -> 0x80000b0 sym.some_ptr + addr size traced ninstr jump fail fcns calls xrefs +------------------------------------------------------------------------------------------------------------- +0x8000040 29 0 6 0x0800005d 0x08000040 +0x800005d 6 0 2 0x0800008d 0x08000063 0x08000040 0x0800008b +0x8000063 14 0 4 0x08000071 0x08000040 0x080000a0 +0x8000071 28 0 9 0x0800005d 0x08000040 0x080000aa +0x800008d 9 0 4 0x08000040 +0x8000096 10 0 1 0x080000a0 +0x80000a0 9 0 3 0x080000a9 0x080000a0 0x080000b0 0x0800006a +0x80000a9 2 0 2 0x080000a0 0x080000bf +0x80000ab 5 0 1 +0x80000b0 16 0 5 0x080000b0 0x080000a4 0x080000d4 0x080000c4 +0x80000c0 9 0 3 0x080000c9 0x080000c0 0x080000b0 +0x80000c9 2 0 2 0x080000c0 0x080000bf +0x80000cb 5 0 1 0x080000d0 +0x80000d0 9 0 3 0x080000d9 0x080000d0 0x080000b0 +0x80000d9 2 0 2 0x080000d0 0x080000bf +EOF +RUN + +NAME=Indirect call hexagon +FILE=bins/inquiry/interpreter/prototype/hexagon_icall_malloc +CMDS=< 0x08000060 [ R2 = memw(FP+##-0xc) +| : 0x08000064 [ P0 = cmp.gtu(R2,##0x2) +| :,=< 0x08000068 [ if (P0) jump:nt 0x80000ac +| ,===< 0x0800006c [ jump 0x8000070 +| `---> 0x08000070 [ R2 = memw(FP+##-0xc) +| :| ;-- .data: +| :| 0x08000074 / immext(##sym.some_ptr) ; 0x80000e8 +| :| ; sym..data; RELOC 32 .data @ 0x080000e8 +| :| ;-- .data: +| :| 0x08000078 \ R2 = memw(R2<<#0x2) ; RELOC 32 .data @ 0x080000e8 +| :| 0x0800007c [ callr R2 +| :| ; RETURN XREF from sym.function_0 @ 0x80000bc +| :| ; RETURN XREF from sym.function_1 @ 0x80000d8 +| :| ; RETURN XREF from sym.function_2 @ 0x80000e4 +| :| 0x08000080 [ memw(FP+##-0x10) = R0 +| :| 0x08000084 [ R2 = memw(FP+##-0x10) +| :| 0x08000088 [ R3 = memub(R2+##0x0) +| :| 0x0800008c [ R2 = memw(FP+##-0x8) +| :| 0x08000090 [ R2 = add(R2,R3) +| :| 0x08000094 [ memw(FP+##-0x8) = R2 +| ,===< 0x08000098 [ jump 0x800009c +| `---> 0x0800009c [ R2 = memw(FP+##-0xc) +| :| 0x080000a0 [ R2 = add(R2,##0x1) +| :| 0x080000a4 [ memw(FP+##-0xc) = R2 +| `==< 0x080000a8 [ jump 0x8000060 +| | ; CODE XREF from sym.main @ 0x8000068 +| `-> 0x080000ac [ R0 = memw(FP+##-0x8) +\ 0x080000b0 [ LR:FP = dealloc_return(FP):raw + ; CALL XREF from sym.main @ 0x800007c +/ sym.function_0(); +| 0x080000b4 [ allocframe(SP,#0x0):raw +| 0x080000b8 [ call sym.some_ptr +| ; RETURN XREF from sym.some_ptr @ 0x80000cc +\ 0x080000bc [ LR:FP = dealloc_return(FP):raw + ; CALL XREF from sym.function_0 @ 0x80000b8 + ; CALL XREF from sym.function_1 @ 0x80000d4 + ; CALL XREF from sym.function_2 @ 0x80000e0 +/ sym.some_ptr(); +| 0x080000c0 [ allocframe(SP,#0x0):raw +| ;-- .bss: +| 0x080000c4 / immext(##sym.some_ptr) ; RELOC 32 .bss @ 0x080000f8 +| ;-- .bss: +| 0x080000c8 \ R0 = ##obj.z ; RELOC 32 .bss @ 0x080000f8 +\ 0x080000cc [ LR:FP = dealloc_return(FP):raw + ; CALL XREF from sym.main @ 0x800007c +/ sym.function_1(); +| 0x080000d0 [ allocframe(SP,#0x0):raw +| 0x080000d4 [ call sym.some_ptr +| ; RETURN XREF from sym.some_ptr @ 0x80000cc +\ 0x080000d8 [ LR:FP = dealloc_return(FP):raw + ; CALL XREF from sym.main @ 0x800007c +/ sym.function_2(); +| 0x080000dc [ allocframe(SP,#0x0):raw +| 0x080000e0 [ call sym.some_ptr +| ; RETURN XREF from sym.some_ptr @ 0x80000cc +\ 0x080000e4 [ LR:FP = dealloc_return(FP):raw + ; DATA XREF from sym.main @ 0x8000074 + ;-- section..data: + ;-- .data: + ;-- .text: + ;-- fcn_arr: + 0x080000e8 .dword 0x080000b4 ; sym.function_0 ; RELOC 32 .text @ 0x08000040 + 0x74 ; [04] -rw- section size 12 named .data + ; DATA XREF from sym.main @ 0x8000074 + ;-- .text: + 0x080000ec .dword 0x080000d0 ; sym.function_1 ; RELOC 32 .text @ 0x08000040 + 0x90 + ; DATA XREF from sym.main @ 0x8000074 + ;-- .text: + 0x080000f0 .dword 0x080000dc ; sym.function_2 ; RELOC 32 .text @ 0x08000040 + 0x9c + 0x080000f4 + ;-- section..bss: + ;-- section..comment: + ;-- .bss: + ;-- z: + 0x080000f8 .dword 0xffffffff ; RELOC TARGET 32 .bss @ 0x080000f8 ; [07] ---- section size 41 named .comment + section..text+40 0x8000068 -> CODE -> 0x80000ac reloc..data.8000078+52 + reloc..data 0x8000074 -> DATA -> 0x80000e8 section..data + reloc..data 0x8000074 -> DATA -> 0x80000ec reloc..text.80000ec + reloc..data 0x8000074 -> DATA -> 0x80000f0 reloc..text.80000f0 + reloc..data.8000078+4 0x800007c -> CALL -> 0x80000b4 sym.function_0 + reloc..data.8000078+4 0x800007c -> CALL -> 0x80000d0 sym.function_1 + reloc..data.8000078+4 0x800007c -> CALL -> 0x80000dc sym.function_2 + reloc..data.8000078+48 0x80000a8 -> CODE -> 0x8000060 section..text+32 + sym.function_0+4 0x80000b8 -> CALL -> 0x80000c0 sym.some_ptr + sym.function_0+8 0x80000bc -> RETURN -> 0x8000080 reloc..data.8000078+8 + reloc..bss.80000c8+4 0x80000cc -> RETURN -> 0x80000bc sym.function_0+8 + reloc..bss.80000c8+4 0x80000cc -> RETURN -> 0x80000d8 sym.function_1+8 + reloc..bss.80000c8+4 0x80000cc -> RETURN -> 0x80000e4 sym.function_2+8 + sym.function_1+4 0x80000d4 -> CALL -> 0x80000c0 sym.some_ptr + sym.function_1+8 0x80000d8 -> RETURN -> 0x8000080 reloc..data.8000078+8 + sym.function_2+4 0x80000e0 -> CALL -> 0x80000c0 sym.some_ptr + sym.function_2+8 0x80000e4 -> RETURN -> 0x8000080 reloc..data.8000078+8 + addr size traced ninstr jump fail fcns calls xrefs +----------------------------------------------------------------------------------------------------------------------------------- +0x8000040 32 0 8 0x08000060 0x08000040 +0x8000060 12 0 3 0x0800006c 0x080000ac 0x08000040 0x080000a8 +0x800006c 4 0 1 0x08000070 0x08000040 +0x8000070 16 0 4 0x08000080 0x08000040 0x080000b4 0x080000d0 0x080000dc +0x8000080 28 0 7 0x0800009c 0x08000040 0x080000e4 0x080000d8 0x080000bc +0x800009c 16 0 4 0x08000060 0x08000040 +0x80000ac 8 0 2 0x08000040 0x08000068 +0x80000b4 8 0 2 0x080000bc 0x080000b4 0x080000c0 0x0800007c +0x80000bc 4 0 1 0x080000b4 0x080000cc +0x80000c0 16 0 4 0x080000c0 0x080000b8 0x080000e0 0x080000d4 +0x80000d0 8 0 2 0x080000d8 0x080000d0 0x080000c0 0x0800007c +0x80000d8 4 0 1 0x080000d0 0x080000cc +0x80000dc 8 0 2 0x080000e4 0x080000dc 0x080000c0 0x0800007c +0x80000e4 4 0 1 0x080000dc 0x080000cc +EOF +EXPECT_ERR=< 0x080000c0 sethi 0x20000, g1 ; RELOC 0 .data @ 0x08000130 +| :| ;-- .data: +| :| 0x080000c4 or g1, 0x130, g2 ; RELOC 0 .data @ 0x08000130 +| :| 0x080000c8 ld [fp+0x7f7], g1 +| :| 0x080000cc srl g1, 0, g1 +| :| 0x080000d0 sllx g1, 3, g1 +| :| 0x080000d4 ldx [g2+g1], g1 ; 0x8000130 +| :| ; obj.fcn_arr +| :| 0x080000d8 call g1 +| :| 0x080000dc nop +| :| ; RETURN XREF from sym.function_0 @ 0x8000070 +| :| ; RETURN XREF from sym.function_1 @ 0x800008c +| :| ; RETURN XREF from sym.function_2 @ 0x80000a8 +| :| 0x080000e0 stx o0, [fp+0x7ef] +| :| 0x080000e4 ldx [fp+0x7ef], g1 +| :| 0x080000e8 ldub [g1], g1 +| :| 0x080000ec and g1, 0xff, g1 +| :| 0x080000f0 ld [fp+0x7fb], g2 +| :| 0x080000f4 add g2, g1, g1 +| :| 0x080000f8 st g1, [fp+0x7fb] +| :| 0x080000fc ld [fp+0x7f7], g1 +| :| 0x08000100 add g1, 1, g1 +| :| 0x08000104 st g1, [fp+0x7f7] +| :| ; CODE XREF from sym.main @ 0x80000bc +| :`-> 0x08000108 ld [fp+0x7f7], g1 +| : 0x0800010c cmp g1, 2 +| `==< 0x08000110 bleu icc, reloc..data +| ; CODE XREF from sym.main @ 0x80000e0 +| 0x08000114 nop +| 0x08000118 ld [fp+0x7fb], g1 +| 0x0800011c sra g1, 0, g1 +| 0x08000120 mov g1, i0 +| 0x08000124 rett i7+8 +\ 0x08000128 nop + 0x0800012c invalid + reloc..bss.8000048+12 0x8000054 -> RETURN -> 0x8000064 sym.function_0+12 + reloc..bss.8000048+12 0x8000054 -> RETURN -> 0x8000080 sym.function_1+12 + reloc..bss.8000048+12 0x8000054 -> RETURN -> 0x800009c sym.function_2+12 + sym.function_0+8 0x8000060 -> CALL -> 0x8000040 section..text + sym.function_0+24 0x8000070 -> RETURN -> 0x80000e0 reloc..data.80000c4+28 + sym.function_1+8 0x800007c -> CALL -> 0x8000040 section..text + sym.function_1+24 0x800008c -> RETURN -> 0x80000e0 reloc..data.80000c4+28 + sym.function_2+8 0x8000098 -> CALL -> 0x8000040 section..text + sym.function_2+24 0x80000a8 -> RETURN -> 0x80000e0 reloc..data.80000c4+28 + sym.main+16 0x80000bc -> CODE -> 0x8000108 reloc..data.80000c4+68 + reloc..data.80000c4+16 0x80000d4 -> DATA -> 0x8000130 section..data + reloc..data.80000c4+16 0x80000d4 -> DATA -> 0x8000138 reloc..text.8000138 + reloc..data.80000c4+16 0x80000d4 -> DATA -> 0x8000140 reloc..text.8000140 + reloc..data.80000c4+24 0x80000dc -> CALL -> 0x8000058 sym.function_0 + reloc..data.80000c4+24 0x80000dc -> CALL -> 0x8000074 sym.function_1 + reloc..data.80000c4+24 0x80000dc -> CALL -> 0x8000090 sym.function_2 + reloc..data.80000c4+28 0x80000e0 -> CODE -> 0x8000114 reloc..data.80000c4+80 + reloc..data.80000c4+80 0x8000114 -> CODE -> 0x80000c0 reloc..data + addr size traced ninstr jump fail fcns calls xrefs +------------------------------------------------------------------------------------------------------------- +0x8000040 24 0 6 0x08000040 0x08000098 0x08000060 0x0800007c +0x8000058 12 0 3 0x08000064 0x08000058 0x08000040 0x080000dc +0x8000060 4 0 1 +0x8000064 16 0 4 0x08000058 0x08000054 +0x8000074 12 0 3 0x08000080 0x08000074 0x08000040 0x080000dc +0x800007c 4 0 1 +0x8000080 16 0 4 0x08000074 0x08000054 +0x8000090 12 0 3 0x0800009c 0x08000090 0x08000040 0x080000dc +0x8000098 4 0 1 +0x800009c 16 0 4 0x08000090 0x08000054 +0x80000ac 20 0 5 0x08000108 0x080000ac +0x80000c0 32 0 8 0x080000e0 0x080000ac 0x08000114 +0x80000dc 4 0 1 0x080000e0 +0x80000e0 40 0 10 0x08000108 0x080000ac 0x0800008c 0x080000a8 0x08000070 +0x8000108 16 0 4 0x080000c0 0x08000118 0x080000ac 0x080000e0 0x080000bc +0x8000114 4 0 1 0x080000e0 +0x8000118 20 0 5 0x080000ac +EOF +EXPECT_ERR=< 0x080000d4 ldr r2, obj.fcn_arr ; [reloc..data:4]=0x8000134 obj.fcn_arr ; "4\U00000001" +| :| 0x080000d8 ldr r3, [fp, -0xc] ; 12 +| :| 0x080000dc ldr r3, [r2, r3, lsl 2] ; 0x8000130 ; "4\U00000001" +| :| 0x080000e0 mov lr, pc +| :| 0x080000e4 bx r3 ; RELOC 0 +| :| ; RETURN XREF from sym.function_0 @ 0x8000070 +| :| ; RETURN XREF from sym.function_1 @ 0x8000090 +| :| 0x080000e8 str r0, [fp, -0x10] ; 16 +| :| 0x080000ec ldr r3, [fp, -0x10] ; 16 +| :| 0x080000f0 ldrb r3, [r3] +| :| 0x080000f4 mov r2, r3 +| :| 0x080000f8 ldr r3, [fp, -8] ; 8 +| :| 0x080000fc add r3, r3, r2 +| :| 0x08000100 str r3, [fp, -8] ; 8 +| :| 0x08000104 ldr r3, [fp, -0xc] ; 12 +| :| 0x08000108 add r3, r3, 1 +| :| 0x0800010c str r3, [fp, -0xc] ; 12 +| :| ; CODE XREF from sym.main @ 0x80000d0 +| :`-> 0x08000110 ldr r3, [fp, -0xc] ; 12 +| : 0x08000114 cmp r3, 2 ; 2 +| `==< 0x08000118 bls 0x80000d4 +| 0x0800011c ldr r3, [fp, -8] ; 8 +| 0x08000120 mov r0, r3 +| 0x08000124 sub sp, fp, 4 +| 0x08000128 pop {fp, lr} +\ 0x0800012c bx lr ; RELOC 0 + ; DATA XREFS from sym.main @ 0x80000d4, 0x80000dc + ;-- .data: + 0x08000130 stmdaeq r0, {r2, r4, r5, r8} ; RELOC 32 .data @ 0x08000134 + ; DATA XREF from sym.main @ 0x80000dc + ;-- section..data: + ;-- .data: + ;-- function_0: + ;-- fcn_arr: + 0x08000134 .dword 0x08000054 ; reloc.target.function_0 ; sym.function_0; RELOC 32 function_0 @ 0x08000054 ; [03] -rw- section size 12 named .data + ; DATA XREF from sym.main @ 0x80000dc + ;-- function_1: + 0x08000138 .dword 0x08000074 ; reloc.target.function_1 ; sym.function_1; RELOC 32 function_1 @ 0x08000074 + ;-- function_2: + 0x0800013c .dword 0x08000094 ; reloc.target.function_2 ; sym.function_2; RELOC 32 function_2 @ 0x08000094 + ;-- section..bss: + ;-- section..comment: + ;-- .bss: + ;-- z: + ;-- .comment: + 0x08000140 .dword 0xffffffff ; RELOC TARGET 32 .bss @ 0x08000140 ; [06] ---- section size 36 named .comment + 0x08000144 invalid + section..text+8 0x800003c -> DATA -> 0x8000050 reloc..bss + section..text+24 0x800004c -> RETURN -> 0x8000060 reloc.target.function_0+12 + section..text+24 0x800004c -> RETURN -> 0x8000080 reloc.target.function_1+12 + section..text+24 0x800004c -> RETURN -> 0x80000a0 reloc.target.function_2+12 + reloc.target.function_0+8 0x800005c -> CALL -> 0x8000034 section..text + reloc.target.function_0+28 0x8000070 -> RETURN -> 0x80000e8 sym.main+52 + reloc.target.function_1+8 0x800007c -> CALL -> 0x8000034 section..text + reloc.target.function_1+28 0x8000090 -> RETURN -> 0x80000e8 sym.main+52 + reloc.target.function_2+8 0x800009c -> CALL -> 0x8000034 section..text + sym.main+28 0x80000d0 -> CODE -> 0x8000110 sym.main+92 + sym.main+32 0x80000d4 -> DATA -> 0x8000130 reloc..data + sym.main+40 0x80000dc -> DATA -> 0x8000130 reloc..data + sym.main+40 0x80000dc -> DATA -> 0x8000134 section..data + sym.main+40 0x80000dc -> DATA -> 0x8000138 reloc.function_1 + sym.main+48 0x80000e4 -> CALL -> 0x8000054 reloc.target.function_0 + sym.main+48 0x80000e4 -> CALL -> 0x8000074 reloc.target.function_1 + sym.main+100 0x8000118 -> CODE -> 0x80000d4 sym.main+32 + addr size traced ninstr jump fail fcns calls xrefs +------------------------------------------------------------------------------------------------------------- +0x8000034 28 0 7 0x08000034 0x0800005c 0x0800009c 0x0800007c +0x8000050 4 0 1 0x08000054 0x0800003c +0x8000054 12 0 3 0x08000060 0x08000054 0x08000034 0x080000e4 +0x8000060 20 0 5 0x08000054 0x0800004c +0x8000074 12 0 3 0x08000080 0x08000074 0x08000034 0x080000e4 +0x8000080 20 0 5 0x08000074 0x0800004c +0x8000094 12 0 3 0x080000a0 0x08000094 0x08000034 +0x80000a0 20 0 5 0x08000094 0x0800004c +0x80000b4 32 0 8 0x08000110 0x080000b4 +0x80000d4 20 0 5 0x080000e8 0x080000b4 0x08000118 +0x80000e8 40 0 10 0x08000110 0x080000b4 0x08000090 0x08000070 +0x8000110 12 0 3 0x080000d4 0x0800011c 0x080000b4 0x080000d0 +0x800011c 20 0 5 0x080000b4 +0x8000134 1 0 1 0x080000dc +EOF +EXPECT_ERR=< 0x08000194 ld v0, 0(gp) ; RELOC 0 .data.rel.local @ 0x080002f0 +| :| 0x08000198 lwu v1, 4(fp) +| :| 0x0800019c dsll v1, v1, 3 +| :| ;-- .data.rel.local: +| :| 0x080001a0 daddiu v0, v0, 0 ; RELOC 0 .data.rel.local @ 0x080002f0 +| :| 0x080001a4 daddu v0, v1, v0 +| :| 0x080001a8 ld v0, 0(v0) +| :| 0x080001ac move t9, v0 +| :| 0x080001b0 jalr t9 + :| 0x080001b4 nop + :| 0x080001b8 sd v0, 8(fp) + :| 0x080001bc ld v0, 8(fp) + :| 0x080001c0 lbu v0, 0(v0) + :| 0x080001c4 lw v1, 0(fp) + :| 0x080001c8 addu v0, v1, v0 + :| 0x080001cc sw v0, 0(fp) + :| 0x080001d0 lw v0, 4(fp) + :| 0x080001d4 addiu v0, v0, 1 + :| 0x080001d8 sw v0, 4(fp) +| :| ; CODE XREF from sym.main @ 0x800018c +| :`-> 0x080001dc lw v0, 4(fp) +| : 0x080001e0 sltiu v0, v0, 3 +| `==< 0x080001e4 bnez v0, reloc..data.rel.local +| 0x080001e8 nop +| 0x080001ec lw v0, 0(fp) +| 0x080001f0 move sp, fp +| 0x080001f4 ld ra, 0x28(sp) +| 0x080001f8 ld fp, 0x20(sp) +| 0x080001fc ld gp, 0x18(sp) +| 0x08000200 daddiu sp, sp, 0x30 +\ 0x08000204 jr ra + reloc.main.8000180+12 0x800018c -> CODE -> 0x80001dc reloc..data.rel.local.80001a0+60 + reloc..data.rel.local.80001a0+68 0x80001e4 -> CODE -> 0x8000194 reloc..data.rel.local + addr size traced ninstr jump fail fcns calls xrefs +--------------------------------------------------------------------------------- +0x8000040 48 0 12 0x08000040 +0x8000070 4 0 1 +0x8000074 48 0 12 0x08000074 +0x80000a4 28 0 7 +0x80000c0 4 0 1 +0x80000c4 48 0 12 0x080000c4 +0x80000f4 28 0 7 +0x8000110 4 0 1 +0x8000114 48 0 12 0x08000114 +0x8000144 28 0 7 +0x8000160 4 0 1 0x08000164 +0x8000164 44 0 11 0x080001dc 0x08000164 +0x8000190 4 0 1 +0x8000194 32 0 8 0x08000164 0x080001e4 +0x80001b4 40 0 10 0x080001dc +0x80001dc 12 0 3 0x08000194 0x080001e8 0x08000164 0x0800018c +0x80001e8 32 0 8 0x08000164 +EOF +EXPECT_ERR=<