rizin/librz/core/seek.c
Giovanni b5fee3471c
Move RzAnalysis as private and force usage of C API (#6123)
* Hide RzAnalysis structure to force the usage of the C APIs.

* Fix rop code & test

* Fix linter

* Fix rz_analysis_free to return void
2026-04-06 16:07:53 +00:00

473 lines
14 KiB
C

// SPDX-FileCopyrightText: 2009-2020 pancake <pancake@nopcode.org>
// SPDX-FileCopyrightText: 2021 ret2libc <sirmy15@gmail.com>
// SPDX-License-Identifier: LGPL-3.0-only
#include <rz_core.h>
RZ_API void rz_core_seek_item_free(RzCoreSeekItem *item) {
free(item);
}
static void get_current_seek_state(RzCore *core, RzCoreSeekItem *elem) {
elem->offset = core->offset;
elem->cursor = core->print->cur_enabled ? rz_print_get_cursor(core->print) : 0;
elem->is_current = false;
}
static void set_current_seek_state(RzCore *core, RzCoreSeekItem *elem) {
rz_core_seek(core, elem->offset, true);
core->print->cur = elem->cursor;
}
static void add_seek_history(RzCore *core) {
RzVector *vundo = &core->seek_history.undos;
RzVector *vredo = &core->seek_history.redos;
RzCoreSeekItem *item = &core->seek_history.saved_item;
ut64 histsize = rz_config_get_i(core->config, "cfg.seek.histsize");
if (!rz_vector_empty(vundo)) {
RzCoreSeekItem *last = rz_vector_index_ptr(vundo, rz_vector_len(vundo) - 1);
if (item->offset == last->offset && item->cursor == last->cursor) {
return;
}
}
if (histsize != 0 && rz_vector_len(vundo) >= histsize) {
rz_vector_remove_at(vundo, 0, NULL);
}
rz_vector_push(vundo, item);
rz_vector_clear(vredo);
}
static bool seek_check_save(RzCore *core, ut64 addr, bool rb, bool save) {
if (save) {
return rz_core_seek_and_save(core, addr, rb);
} else {
return rz_core_seek(core, addr, rb);
}
}
/**
* \brief Mark current state (offset+cursor) as the next state to save in history
*
* The saving can be disabled if eval var cfg.seek.silent is set to true. The
* state saved here is actually saved in history once \p rz_core_seek_and_save
* is called (or other functions with the \p save argument set to true).
*
* \param core RzCore reference
*/
RZ_API bool rz_core_seek_mark(RzCore *core) {
if (!rz_config_get_i(core->config, "cfg.seek.silent")) {
get_current_seek_state(core, &core->seek_history.saved_item);
core->seek_history.saved_set = true;
return true;
}
core->seek_history.saved_set = false;
return false;
}
static bool need_add2history(RzCore *core, ut64 addr) {
RzCoreSeekHistory *hist = &core->seek_history;
return hist->saved_set && (addr != hist->saved_item.offset || hist->saved_item.cursor != 0);
}
static bool seek_save(RzCore *core, ut64 addr) {
if (need_add2history(core, addr)) {
add_seek_history(core);
core->seek_history.saved_set = false;
return true;
}
core->seek_history.saved_set = false;
return false;
}
/**
* \brief Save last marked position, if any, in the seek history.
*
* \param core RzCore reference
*/
RZ_API bool rz_core_seek_save(RzCore *core) {
return seek_save(core, core->offset);
}
/**
* \brief Save currently marked state in seek history and seek to \p addr .
*
* If \p rz_core_seek_mark is used to mark a position, that position will be
* saved in the history, otherwise the current state is used.
*
* \param core RzCore reference
* \param addr Address where to move to
* \param rb If true read the block
*/
RZ_API bool rz_core_seek_and_save(RzCore *core, ut64 addr, bool rb) {
if (!core->seek_history.saved_set) {
rz_core_seek_mark(core);
}
seek_save(core, addr);
return rz_core_seek(core, addr, rb);
}
/**
* \brief Seek to \p addr.
*
* \param core RzCore reference
* \param addr Address where to move to
* \param rb If true read the block
*/
RZ_API bool rz_core_seek(RzCore *core, ut64 addr, bool rb) {
core->offset = rz_io_seek(core->io, addr, RZ_IO_SEEK_SET);
if (rb) {
rz_core_block_read(core);
}
if (core->binat) {
RzBinFile *bf = rz_bin_file_at(core->bin, core->offset);
if (bf) {
core->bin->cur = bf;
rz_bin_select_bfid(core->bin, bf->id);
} else {
core->bin->cur = NULL;
}
}
return core->offset == addr;
}
/**
* \brief Seek to \p addr and optionally save the current offset in seek history.
*
* \param core RzCore reference
* \param addr Address where to move to
* \param rb If true read the block
* \param save If true save the current state in seek history before seeking
*/
RZ_API bool rz_core_seek_opt(RzCore *core, ut64 addr, bool rb, bool save) {
return seek_check_save(core, addr, rb, save);
}
/**
* \brief Seek relative to current offset and optionally save the current offset in seek history.
*
* \param core RzCore reference
* \param delta Delta address added to the current offset
* \param save If true save the current state in seek history before seeking
*/
RZ_API bool rz_core_seek_delta(RzCore *core, st64 delta, bool save) {
ut64 newaddr;
if (delta > 0 && UT64_ADD_OVFCHK(core->offset, (ut64)(delta))) {
newaddr = UT64_MAX;
} else if (delta < 0 && core->offset < (ut64)RZ_ABS(delta)) {
newaddr = 0;
} else {
newaddr = core->offset + delta;
}
return seek_check_save(core, newaddr, true, save);
}
/**
* \brief Seek to a new address composed of current offset with last hex digits replaced with those of \p hex
*
* \param core RzCore reference
* \param hex New final part of the address (in hex)
* \param save If true save the current state in seek history before seeking
*/
RZ_API int rz_core_seek_base(RzCore *core, const char *hex, bool save) {
ut64 addr = rz_num_tail(core->num, core->offset, hex);
return seek_check_save(core, addr, true, save);
}
struct seek_flag_offset_t {
ut64 offset;
ut64 *next;
bool is_next;
};
static bool seek_flag_offset(RzFlagItem *fi, void *user) {
struct seek_flag_offset_t *u = (struct seek_flag_offset_t *)user;
if (u->is_next) {
if (fi->offset < *u->next && fi->offset > u->offset) {
*u->next = fi->offset;
}
} else {
if (fi->offset > *u->next && fi->offset < u->offset) {
*u->next = fi->offset;
}
}
return true;
}
/**
* \brief Seek to the next \p type of item from current offset
*
* \param core RzCore reference
* \param type Type of next "item" to seek to (could be "opc", "fun", "hit", "flag")
* \param save If true save the current state in seek history before seeking
*/
RZ_API bool rz_core_seek_next(RzCore *core, const char *type, bool save) {
RzListIter *iter;
ut64 next = UT64_MAX;
if (strstr(type, "opc")) {
RzAnalysisOp aop = { 0 };
rz_analysis_op_init(&aop);
if (rz_analysis_op(core->analysis, &aop, core->offset, core->block, core->blocksize, RZ_ANALYSIS_OP_MASK_BASIC) > 0) {
next = core->offset + aop.size;
} else {
RZ_LOG_ERROR("core: invalid opcode\n");
}
rz_analysis_op_fini(&aop);
} else if (strstr(type, "fun")) {
RzAnalysisFunction *fcni;
RzList *fcns = rz_analysis_function_list(core->analysis);
rz_list_foreach (fcns, iter, fcni) {
if (fcni->addr < next && fcni->addr > core->offset) {
next = fcni->addr;
}
}
} else if (strstr(type, "hit")) {
const char *pfx = rz_config_get(core->config, "search.prefix");
struct seek_flag_offset_t u = { .offset = core->offset, .next = &next, .is_next = true };
rz_flag_foreach_prefix(core->flags, pfx, -1, seek_flag_offset, &u);
} else { // flags
struct seek_flag_offset_t u = { .offset = core->offset, .next = &next, .is_next = true };
rz_flag_foreach(core->flags, seek_flag_offset, &u);
}
if (next == UT64_MAX) {
return false;
}
return seek_check_save(core, next, true, save);
}
/**
* \brief Seek to the previous \p type of item from current offset
*
* \param core RzCore reference
* \param type Type of previous "item" to seek to (could be "opc", "fun", "hit", "flag")
* \param save If true save the current state in seek history before seeking
*/
RZ_API bool rz_core_seek_prev(RzCore *core, const char *type, bool save) {
RzListIter *iter;
ut64 next = 0;
if (strstr(type, "opc")) {
RZ_LOG_WARN("core: TODO: rz_core_seek_prev (opc)\n");
} else if (strstr(type, "fun")) {
RzAnalysisFunction *fcni;
RzList *fcns = rz_analysis_function_list(core->analysis);
rz_list_foreach (fcns, iter, fcni) {
if (fcni->addr > next && fcni->addr < core->offset) {
next = fcni->addr;
}
}
} else if (strstr(type, "hit")) {
const char *pfx = rz_config_get(core->config, "search.prefix");
struct seek_flag_offset_t u = { .offset = core->offset, .next = &next, .is_next = false };
rz_flag_foreach_prefix(core->flags, pfx, -1, seek_flag_offset, &u);
} else { // flags
struct seek_flag_offset_t u = { .offset = core->offset, .next = &next, .is_next = false };
rz_flag_foreach(core->flags, seek_flag_offset, &u);
}
if (next == 0) {
return false;
}
return seek_check_save(core, next, true, save);
}
/**
* \brief Seek to current offset aligned to \p align
*
* \param core RzCore reference
* \param align Value to align the current offset
* \param save If true save the current state in seek history before seeking
*/
RZ_API bool rz_core_seek_align(RzCore *core, ut64 align, bool save) {
if (align < 2) {
return false;
}
int diff = core->offset % align;
return seek_check_save(core, core->offset - diff, true, save);
}
/**
* \brief Seek to basic block that contains address \p addr
*
* \param core RzCore reference
* \param addr Address that needs to be in the basicblock
* \param save If true save the current state in seek history before seeking
*/
RZ_API bool rz_core_seek_analysis_bb(RzCore *core, ut64 addr, bool save) {
RzAnalysisBlock *block = rz_analysis_find_most_relevant_block_in(core->analysis, addr);
if (block) {
seek_check_save(core, block->addr, false, save);
return true;
}
return false;
}
/**
* Undo the last entry in the seek history
*/
RZ_API bool rz_core_seek_undo(RzCore *core) {
if (rz_vector_empty(&core->seek_history.undos)) {
return false;
}
RzCoreSeekItem elem;
get_current_seek_state(core, &elem);
rz_vector_push(&core->seek_history.redos, &elem);
rz_vector_pop(&core->seek_history.undos, &elem);
set_current_seek_state(core, &elem);
return true;
}
/**
* Redo the last undone entry in the seek history
*/
RZ_API bool rz_core_seek_redo(RzCore *core) {
if (rz_vector_empty(&core->seek_history.redos)) {
return false;
}
RzCoreSeekItem elem;
get_current_seek_state(core, &elem);
rz_vector_push(&core->seek_history.undos, &elem);
rz_vector_pop(&core->seek_history.redos, &elem);
set_current_seek_state(core, &elem);
return true;
}
static RzCoreSeekItem *get_current_item(RzCore *core) {
RzCoreSeekItem *res = RZ_NEW0(RzCoreSeekItem);
if (!res) {
return NULL;
}
get_current_seek_state(core, res);
res->is_current = true;
res->idx = 0;
return res;
}
static RzCoreSeekItem *dup_seek_history_item(RzCoreSeekItem *item, int i) {
RzCoreSeekItem *res = RZ_NEW0(RzCoreSeekItem);
if (!res) {
return NULL;
}
res->offset = item->offset;
res->cursor = item->cursor;
res->is_current = item->is_current;
res->idx = i;
return res;
}
/**
* \brief Return a element in the undo/redo list.
*
* The element is not removed from the list nor it is restored as the current
* state. Useful if you want to inspect the undo history. The object shall be
* freed by the caller.
*
* \param core Reference to RzCore
* \param idx Index of the element. 0 references the current seek, <0 are undos, >0 redos
*/
RZ_API RzCoreSeekItem *rz_core_seek_peek(RzCore *core, int idx) {
if (idx == 0) {
return get_current_item(core);
} else if (idx < 0) {
RzVector *vundo = &core->seek_history.undos;
size_t i = RZ_ABS(idx) - 1;
size_t len = rz_vector_len(vundo);
if (i >= len) {
return NULL;
}
RzCoreSeekItem *vel = (RzCoreSeekItem *)rz_vector_index_ptr(vundo, len - i - 1);
return dup_seek_history_item(vel, idx);
} else {
RzVector *vredo = &core->seek_history.redos;
size_t i = RZ_ABS(idx) - 1;
size_t len = rz_vector_len(vredo);
if (i >= len) {
return NULL;
}
RzCoreSeekItem *vel = (RzCoreSeekItem *)rz_vector_index_ptr(vredo, len - i - 1);
return dup_seek_history_item(vel, idx);
}
}
/**
* Remove all seek history entries
*/
RZ_API void rz_core_seek_reset(RzCore *core) {
rz_vector_fini(&core->seek_history.undos);
rz_vector_fini(&core->seek_history.redos);
rz_vector_init(&core->seek_history.undos, sizeof(RzCoreSeekItem), NULL, NULL);
rz_vector_init(&core->seek_history.redos, sizeof(RzCoreSeekItem), NULL, NULL);
}
/**
* Free seek history data
*/
RZ_API void rz_core_seek_free(RzCore *core) {
rz_vector_fini(&core->seek_history.undos);
rz_vector_fini(&core->seek_history.redos);
}
/**
* \brief Return the seek history.
*
* The list is composed of some items with negative idx which are Undos items
* (potentially none), then there is an item with is_current=true that is the
* current state, followed by some items with positive idx which are Redos
* items.
*/
RZ_API RzList /*<RzCoreSeekItem *>*/ *rz_core_seek_list(RzCore *core) {
RzList *res = rz_list_newf((RzListFree)rz_core_seek_item_free);
if (!res) {
return NULL;
}
RzCoreSeekItem *it;
int i = -rz_vector_len(&core->seek_history.undos);
rz_vector_foreach (&core->seek_history.undos, it) {
RzCoreSeekItem *dup = dup_seek_history_item(it, i++);
if (!dup) {
goto err;
}
rz_list_append(res, dup);
}
RzCoreSeekItem *cur = get_current_item(core);
if (!cur) {
goto err;
}
rz_list_append(res, cur);
i = 1;
rz_vector_foreach_prev (&core->seek_history.redos, it) {
RzCoreSeekItem *dup = dup_seek_history_item(it, i++);
if (!dup) {
goto err;
}
rz_list_append(res, dup);
}
return res;
err:
rz_list_free(res);
return NULL;
}
/* \brief Seek to the \p index instruction in the current basic block
*
* Allows \p index to be negative, in this case it will count
* the instructions from the end of the block
* */
RZ_IPI bool rz_core_seek_bb_instruction(RzCore *core, int index) {
RzAnalysisBlock *bb = rz_analysis_find_most_relevant_block_in(core->analysis, core->offset);
if (!bb) {
RZ_LOG_ERROR("Can't find a basic block for 0x%08" PFMT64x "\n", core->offset);
return false;
}
// handle negative indices
if (index < 0) {
index = bb->ninstr + index;
}
if (!(index >= 0 && index < bb->ninstr)) {
RZ_LOG_ERROR("The current basic block has %d instructions\n", bb->ninstr);
return false;
}
ut64 inst_addr = rz_analysis_block_get_op_addr(bb, index);
return rz_core_seek(core, inst_addr, true);
}