Split off RzAnalysisILContext from VM (Fix #6455) (#6574)

Information about the final register binding and memories is needed
independently of a stateful vm, specifically for analysis.
This is a pure refactor.
This commit is contained in:
Florian Märkl 2026-06-29 13:45:05 +02:00 committed by GitHub
parent 5fa8210504
commit 1306a965ec
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 175 additions and 76 deletions

View file

@ -4,7 +4,7 @@
#include "analysis_private.h"
/**
* \name Config and Init State
* \name Config, Init State, and Context
* @{
*/
@ -121,63 +121,11 @@ RZ_API void rz_analysis_il_config_add_label(RZ_NONNULL RzAnalysisILConfig *cfg,
rz_pvector_push(&cfg->labels, label);
}
/// @}
/////////////////////////////////////////////////////////
/**
* \name Analysis IL VM
* @{
*/
static void setup_vm_from_config(RzAnalysis *analysis, RzAnalysisILVM *vm, RzAnalysisILConfig *cfg);
static void setup_vm_init_state(RzAnalysisILVM *vm, RZ_NULLABLE RzAnalysisILInitState *is, RZ_NULLABLE RzReg *reg);
/**
* Create and initialize an RzAnalysisILVM with the current arch/cpu/bits configuration and plugin
* \p init_state_reg optional RzReg to take variable values from, unless the plugin overrides them using RzAnalysisILInitState
* \return RzAnalysisRzil* a pointer to RzAnalysisILVM instance
*/
RZ_API RZ_OWN RzAnalysisILVM *rz_analysis_il_vm_new(RzAnalysis *a, RZ_NULLABLE RzReg *init_state_reg) {
rz_return_val_if_fail(a && a->cur && a->cur->il_config, NULL);
RzAnalysisILConfig *config = a->cur->il_config(a);
if (!config) {
return false;
}
RzAnalysisILVM *r = RZ_NEW0(RzAnalysisILVM);
if (!r) {
goto ruby_pool;
}
r->io_buf = rz_buf_new_with_io(&a->iob);
setup_vm_from_config(a, r, config);
if (!r->vm) {
rz_buf_free(r->io_buf);
free(r);
r = NULL;
goto ruby_pool;
}
setup_vm_init_state(r, config->init_state, init_state_reg);
ruby_pool:
rz_analysis_il_config_free(config);
return r;
}
/**
* Frees an RzAnalysisILVM instance
*/
RZ_API void rz_analysis_il_vm_free(RZ_NULLABLE RzAnalysisILVM *vm) {
if (!vm) {
return;
}
rz_il_vm_free(vm->vm);
rz_il_reg_binding_free(vm->reg_binding);
rz_buf_free(vm->io_buf);
free(vm);
}
static bool setup_regs(RzAnalysis *a, RzAnalysisILVM *vm, RzAnalysisILConfig *cfg) {
static RzILRegBinding *setup_reg_binding(RzAnalysis *a, RzAnalysisILConfig *cfg) {
if (!a->cur->get_reg_profile) {
return false;
}
RzILRegBinding *reg_binding = NULL;
// Explicitly use a new reg here!
// The a->reg might be changed by the user, but plugins expect exactly
// the register profile they supplied. Syncing will later adjust the register
@ -189,7 +137,6 @@ static bool setup_regs(RzAnalysis *a, RzAnalysisILVM *vm, RzAnalysisILConfig *cf
char *profile = a->cur->get_reg_profile(a);
bool succ;
if (!profile) {
succ = false;
goto new_real;
}
succ = rz_reg_set_profile_string(reg, profile);
@ -202,33 +149,145 @@ static bool setup_regs(RzAnalysis *a, RzAnalysisILVM *vm, RzAnalysisILConfig *cf
while (cfg->reg_bindings[count]) {
count++;
}
vm->reg_binding = rz_il_reg_binding_exactly(reg, count, cfg->reg_bindings);
reg_binding = rz_il_reg_binding_exactly(reg, count, cfg->reg_bindings);
} else {
vm->reg_binding = rz_il_reg_binding_derive(reg);
reg_binding = rz_il_reg_binding_derive(reg);
}
if (!vm->reg_binding) {
succ = false;
if (!reg_binding) {
goto new_real;
}
rz_il_vm_setup_reg_binding(vm->vm, vm->reg_binding);
new_real:
rz_reg_free(reg);
return succ;
return reg_binding;
}
static void setup_vm_from_config(RzAnalysis *analysis, RzAnalysisILVM *vm, RzAnalysisILConfig *cfg) {
vm->vm = rz_il_vm_new(0, cfg->pc_size, cfg->big_endian, get_halt_exc_options(&analysis->coreb, analysis->core));
static void analysis_il_mem_free(void *e, void *user) {
RzAnalysisILMem *mem = e;
rz_buf_free(mem->base_buf);
}
/**
* \brief Resolve a concrete IL context from the current plugin's IL config
*/
RZ_API RZ_OWN RzAnalysisILContext *rz_analysis_il_context_resolve(RzAnalysis *a) {
rz_return_val_if_fail(a && a->cur && a->cur->il_config, NULL);
RzAnalysisILContext *ctx = RZ_NEW0(RzAnalysisILContext);
if (!ctx) {
return NULL;
}
ctx->config = a->cur->il_config(a);
if (!ctx->config) {
goto err_ctx;
}
ctx->reg_binding = setup_reg_binding(a, ctx->config);
if (!ctx->reg_binding) {
goto err_config;
}
rz_vector_init(&ctx->memory, sizeof(RzAnalysisILMem), analysis_il_mem_free, NULL);
// Currently only a single memory bound to io is supported. More could be added here.
RzAnalysisILMem *io_mem = rz_vector_push(&ctx->memory, NULL);
if (!io_mem) {
goto err_reg_binding;
}
io_mem->type = RZ_ANALYSIS_IL_MEM_TYPE_IO;
io_mem->key_size = ctx->config->mem_key_size;
io_mem->base_buf = rz_buf_new_with_io(&a->iob);
if (!io_mem->base_buf) {
goto err_memory;
}
return ctx;
err_memory:
rz_vector_fini(&ctx->memory);
err_reg_binding:
rz_il_reg_binding_free(ctx->reg_binding);
err_config:
rz_analysis_il_config_free(ctx->config);
err_ctx:
free(ctx);
return NULL;
}
RZ_API void rz_analysis_il_context_free(RzAnalysisILContext *ctx) {
if (!ctx) {
return;
}
rz_analysis_il_config_free(ctx->config);
rz_il_reg_binding_free(ctx->reg_binding);
rz_vector_fini(&ctx->memory);
free(ctx);
}
/// @}
/////////////////////////////////////////////////////////
/**
* \name Analysis IL VM
* @{
*/
static void setup_vm_from_context(RzAnalysis *analysis, RzAnalysisILVM *vm, RZ_OWN RzAnalysisILContext *ctx);
static void setup_vm_init_state(RzAnalysisILVM *vm, RZ_NULLABLE RzAnalysisILInitState *is, RZ_NULLABLE RzReg *reg);
/**
* Create and initialize an RzAnalysisILVM with the current arch/cpu/bits configuration and plugin
* \p init_state_reg optional RzReg to take variable values from, unless the plugin overrides them using RzAnalysisILInitState
* \return RzAnalysisRzil* a pointer to RzAnalysisILVM instance
*/
RZ_API RZ_OWN RzAnalysisILVM *rz_analysis_il_vm_new(RzAnalysis *a, RZ_NULLABLE RzReg *init_state_reg) {
rz_return_val_if_fail(a && a->cur && a->cur->il_config, NULL);
RzAnalysisILContext *context = rz_analysis_il_context_resolve(a);
if (!context) {
return false;
}
RzAnalysisILVM *r = RZ_NEW0(RzAnalysisILVM);
if (!r) {
goto ruby_pool;
}
setup_vm_from_context(a, r, context);
if (!r->vm) {
free(r);
r = NULL;
goto ruby_pool;
}
setup_vm_init_state(r, context->config->init_state, init_state_reg);
return r;
ruby_pool:
rz_analysis_il_context_free(context);
return NULL;
}
/**
* Frees an RzAnalysisILVM instance
*/
RZ_API void rz_analysis_il_vm_free(RZ_NULLABLE RzAnalysisILVM *vm) {
if (!vm) {
return;
}
rz_il_vm_free(vm->vm);
rz_analysis_il_context_free(vm->ctx);
free(vm);
}
static void setup_vm_from_context(RzAnalysis *analysis, RzAnalysisILVM *vm, RZ_OWN RzAnalysisILContext *ctx) {
vm->vm = rz_il_vm_new(0, ctx->config->pc_size, ctx->config->big_endian, get_halt_exc_options(&analysis->coreb, analysis->core));
if (!vm->vm) {
return;
}
if (!setup_regs(analysis, vm, cfg)) {
rz_il_vm_free(vm->vm);
vm->vm = NULL;
return;
vm->ctx = ctx;
rz_il_vm_setup_reg_binding(vm->vm, ctx->reg_binding);
for (RzILMemIndex i = 0; i < (RzILMemIndex)rz_vector_len(&ctx->memory); i++) {
RzAnalysisILMem *analysis_mem = rz_vector_index_ptr(&ctx->memory, i);
if (!analysis_mem->base_buf) {
// Memories without a backing buffer are supported by the API, but not yet the implementation
rz_warn_if_reached();
continue;
}
rz_il_vm_add_mem(vm->vm, 0, rz_il_mem_new_borrowed(analysis_mem->base_buf, analysis_mem->key_size));
}
rz_il_vm_add_mem(vm->vm, 0, rz_il_mem_new_borrowed(vm->io_buf, cfg->mem_key_size));
void **it;
rz_pvector_foreach (&cfg->labels, it) {
rz_pvector_foreach (&ctx->config->labels, it) {
RzILEffectLabel *lbl = *it;
rz_il_vm_add_label(vm->vm, rz_il_effect_label_dup(lbl));
}
@ -236,7 +295,7 @@ static void setup_vm_from_config(RzAnalysis *analysis, RzAnalysisILVM *vm, RzAna
static void setup_vm_init_state(RzAnalysisILVM *vm, RZ_NULLABLE RzAnalysisILInitState *is, RZ_NULLABLE RzReg *reg) {
if (reg) {
rz_il_vm_sync_from_reg(vm->vm, vm->reg_binding, reg);
rz_il_vm_sync_from_reg(vm->vm, vm->ctx->reg_binding, reg);
}
if (is) {
RzAnalysisILInitStateVar *v;
@ -257,7 +316,7 @@ static void setup_vm_init_state(RzAnalysisILVM *vm, RZ_NULLABLE RzAnalysisILInit
*/
RZ_API void rz_analysis_il_vm_sync_from_reg(RzAnalysisILVM *vm, RZ_NONNULL RzReg *reg) {
rz_return_if_fail(vm && reg);
rz_il_vm_sync_from_reg(vm->vm, vm->reg_binding, reg);
rz_il_vm_sync_from_reg(vm->vm, vm->ctx->reg_binding, reg);
}
/**
@ -270,7 +329,7 @@ RZ_API void rz_analysis_il_vm_sync_from_reg(RzAnalysisILVM *vm, RZ_NONNULL RzReg
*/
RZ_API bool rz_analysis_il_vm_sync_to_reg(RzAnalysisILVM *vm, RZ_NONNULL RzReg *reg) {
rz_return_val_if_fail(vm && reg, false);
return rz_il_vm_sync_to_reg(vm->vm, vm->reg_binding, reg);
return rz_il_vm_sync_to_reg(vm->vm, vm->ctx->reg_binding, reg);
}
static void il_events(RzILVM *vm, RzStrBuf *sb) {
@ -435,7 +494,7 @@ RZ_API bool rz_analysis_il_vm_setup(RzAnalysis *analysis) {
if (analysis->il_vm) {
// rz_analysis_il_vm_new merges the contents of analysis->reg with the plugin's optional RzAnalysisILInitState
// Now sync the merged state back:
rz_il_vm_sync_to_reg(analysis->il_vm->vm, analysis->il_vm->reg_binding, analysis->reg);
rz_il_vm_sync_to_reg(analysis->il_vm->vm, analysis->il_vm->ctx->reg_binding, analysis->reg);
}
return !!analysis->il_vm;
}

View file

@ -1058,6 +1058,45 @@ typedef struct rz_analysis_il_config_t {
// more information might go in here, for example additional memories, etc.
} RzAnalysisILConfig;
/**
* \brief Defines the kind of memory attached to the VM, which may alter its behaviour.
*
* At the moment only IO is supported.
*/
typedef enum rz_analysis_il_mem_type_t {
RZ_ANALYSIS_IL_MEM_TYPE_IO
} RzAnalysisILMemType;
typedef struct rz_analysis_il_mem_t {
RzAnalysisILMemType type;
ut32 key_size; ///< address size
/**
* \brief Buffer that backs this memory.
*
* This is purely a pointer to the outside world, e.g. io.
* It should not by itself contain any state that is considered local to a single VM instance.
* For example if a sparse overlay on top of io is to be used locally during emulation, this
* should still point to a direct io buffer and the overlay.
* That is because RzAnalysisILContext is independent of any state.
*
* This is nullable for future memory types that may not have a backing buffer.
*/
RZ_NULLABLE RZ_OWN RzBuffer *base_buf;
} RzAnalysisILMem;
/**
* \brief Global context of an RzAnalysisILVM
*
* This holds information resolved from the RzAnalysisILConfig about the bindings between
* IL and memory/regs/...
*/
typedef struct rz_analysis_il_context_t {
RZ_NONNULL RzAnalysisILConfig *config; ///< config this context is resolved from, defined by arch plugin
RZ_NONNULL RzILRegBinding *reg_binding; ///< specifies which (global) variables are bound to registers
RzVector /*<RzAnalysisILMem>*/ memory; ///< specifies the available IL memories
} RzAnalysisILContext;
/**
* \brief High-level RzIL vm to emulate disassembled code
*
@ -1066,9 +1105,8 @@ typedef struct rz_analysis_il_config_t {
* and lifting with analysis plugins.
*/
struct rz_analysis_il_vm_t {
RZ_NONNULL RZ_OWN RzAnalysisILContext *ctx;
RZ_NONNULL RzILVM *vm; ///< low-level vm to execute IL code
RZ_NONNULL RzBuffer *io_buf; ///< buffer to use for memory 0 (io)
RZ_NONNULL RzILRegBinding *reg_binding; ///< specifies which (global) variables are bound to registers
} /* RzAnalysisILVM */;
typedef enum {
@ -1485,6 +1523,8 @@ RZ_API void rz_analysis_il_init_state_set_var(RZ_NONNULL RzAnalysisILInitState *
RZ_API RZ_OWN RzAnalysisILConfig *rz_analysis_il_config_new(ut32 pc_size, bool big_endian, ut32 mem_key_size);
RZ_API void rz_analysis_il_config_free(RzAnalysisILConfig *cfg);
RZ_API void rz_analysis_il_config_add_label(RZ_NONNULL RzAnalysisILConfig *cfg, RZ_NONNULL RZ_OWN RzILEffectLabel *label);
RZ_API RZ_OWN RzAnalysisILContext *rz_analysis_il_context_resolve(RzAnalysis *a);
RZ_API void rz_analysis_il_context_free(RzAnalysisILContext *ctx);
typedef bool (*RzAnalysisILVMCondCallback)(RzAnalysisILVM *vm, void *user);