rizin/librz/il/theory_effect.c
Florian Märkl 5d93ca3330
[RzIL] Flatten out all RzILOpArgs* structs (#2151)
This reduces heap usage and indirection.
2021-12-28 20:47:10 +00:00

185 lines
5.3 KiB
C

// SPDX-FileCopyrightText: 2021 Florian Märkl <info@florianmaerkl.de>
// SPDX-FileCopyrightText: 2021 heersin <teablearcher@gmail.com>
// SPDX-License-Identifier: LGPL-3.0-only
#include <rz_il/rzil_opcodes.h>
#include <rz_il/vm_layer.h>
#include <rz_il/rzil_vm.h>
static RzILEvent *il_event_new_write_from_var(RzILVM *vm, RzILVar *var, RzILVal *new_val) {
rz_return_val_if_fail(vm && var && new_val, NULL);
RzILVal *old_val = NULL;
RzILEvent *evt = NULL;
RzBitVector *oldnum = NULL;
RzBitVector *newnum = NULL;
if (new_val->type == RZIL_VAR_TYPE_BOOL) {
newnum = rz_bv_new_from_ut64(1, new_val->data.b->b);
} else {
newnum = new_val->data.bv;
}
old_val = rz_il_hash_find_val_by_var(vm, var);
if (old_val) {
if (old_val->type == RZIL_VAR_TYPE_BOOL) {
oldnum = rz_bv_new_from_ut64(1, old_val->data.b->b);
} else {
oldnum = old_val->data.bv;
}
}
evt = rz_il_event_var_write_new(var->var_name, oldnum, newnum);
if (old_val && old_val->type == RZIL_VAR_TYPE_BOOL) {
rz_bv_free(oldnum);
}
if (new_val->type == RZIL_VAR_TYPE_BOOL) {
rz_bv_free(newnum);
}
return evt;
}
static void rz_il_set(RzILVM *vm, const char *var_name, bool is_local, bool is_mutable, RZ_OWN RzILVal *val) {
RzILVar *var = NULL;
RzILEvent *evt = NULL;
if (is_local) {
var = rz_il_find_local_var_by_name(vm, var_name);
} else {
var = rz_il_find_var_by_name(vm, var_name);
}
if (!var && !is_local) {
// it's a set to a global variable which has not been defined.
char *message = rz_str_newf("unknown global variable '%s'.", var_name);
evt = rz_il_event_exception_new(message);
free(message);
rz_il_value_free(val);
rz_il_vm_event_add(vm, evt);
return;
} else if (var && !var->is_mutable) {
// forbid changing an immutable type
char *message = rz_str_newf("cannot change %s variable '%s' because is not mutable.", is_local ? "local" : "global", var_name);
evt = rz_il_event_exception_new(message);
free(message);
rz_il_value_free(val);
rz_il_vm_event_add(vm, evt);
return;
}
// enforce var type to val except if var is unk, because unk type can be set to any type.
if (var && var->type == RZIL_VAR_TYPE_BV && val->type == RZIL_VAR_TYPE_BOOL) {
RzBitVector *bv = rz_bv_new_from_ut64(1, val->data.b->b);
RzILVal *cast = rz_il_value_new_bitv(bv);
rz_il_value_free(val);
val = cast;
} else if (var && var->type == RZIL_VAR_TYPE_BOOL && val->type == RZIL_VAR_TYPE_BV) {
RzILBool *b = rz_il_bool_new(!rz_bv_is_zero_vector(val->data.bv));
RzILVal *cast = rz_il_value_new_bool(b);
rz_il_value_free(val);
val = cast;
}
if (is_local) {
// add/update local variable
if (var) {
// update mutable local variable
rz_il_hash_cancel_local_binding(vm, var);
} else {
// first set of an mutable/immutable local variable
var = rz_il_vm_create_local_variable(vm, var_name, val->type, is_mutable);
}
rz_il_hash_local_bind(vm, var, val);
} else {
// update global variable
evt = il_event_new_write_from_var(vm, var, val);
rz_il_hash_cancel_binding(vm, var);
rz_il_hash_bind(vm, var, val);
rz_il_vm_fortify_val(vm, val);
rz_il_vm_event_add(vm, evt);
}
}
bool rz_il_handler_nop(RzILVM *vm, RzILOpEffect *op) {
rz_return_val_if_fail(vm && op, false);
return true;
}
bool rz_il_handler_set(RzILVM *vm, RzILOpEffect *op) {
rz_return_val_if_fail(vm && op, false);
RzILOpArgsSet *set_op = &op->op.set;
RzILVal *val = rz_il_evaluate_val(vm, set_op->x);
if (!val) {
return false;
}
rz_il_set(vm, set_op->v, false, true, val);
return true;
}
bool rz_il_handler_let(RzILVM *vm, RzILOpEffect *op) {
rz_return_val_if_fail(vm && op, false);
RzILOpArgsLet *let_op = &op->op.let;
RzILVal *val = rz_il_evaluate_val(vm, let_op->x);
if (!val) {
return false;
}
rz_il_set(vm, let_op->v, true, let_op->mut, val);
return false;
}
static void perform_jump(RzILVM *vm, RZ_OWN RzBitVector *dst) {
rz_il_vm_event_add(vm, rz_il_event_pc_write_new(vm->pc, dst));
rz_bv_free(vm->pc);
vm->pc = dst;
}
bool rz_il_handler_jmp(RzILVM *vm, RzILOpEffect *op) {
rz_return_val_if_fail(vm && op, false);
RzBitVector *dst = rz_il_evaluate_bitv(vm, op->op.jmp.dst);
if (!dst) {
return false;
}
perform_jump(vm, dst);
return true;
}
bool rz_il_handler_goto(RzILVM *vm, RzILOpEffect *op) {
rz_return_val_if_fail(vm && op, false);
RzILOpArgsGoto *op_goto = &op->op.goto_;
const char *lname = op_goto->lbl;
RzILEffectLabel *label = rz_il_vm_find_label_by_name(vm, lname);
if (!label) {
return false;
}
if (label->type == EFFECT_LABEL_SYSCALL || label->type == EFFECT_LABEL_HOOK) {
RzILVmHook internal_hook = (RzILVmHook)label->hook;
internal_hook(vm, op);
} else {
perform_jump(vm, rz_bv_dup(label->addr));
}
return true;
}
bool rz_il_handler_seq(RzILVM *vm, RzILOpEffect *op) {
rz_return_val_if_fail(vm && op, false);
RzILOpArgsSeq *op_seq = &op->op.seq;
return rz_il_evaluate_effect(vm, op_seq->x) && rz_il_evaluate_effect(vm, op_seq->y);
}
bool rz_il_handler_branch(RzILVM *vm, RzILOpEffect *op) {
rz_return_val_if_fail(vm && op, false);
RzILOpArgsBranch *op_branch = &op->op.branch;
RzILBool *condition = rz_il_evaluate_bool(vm, op_branch->condition);
if (!condition) {
return false;
}
bool ret;
if (condition->b) {
ret = rz_il_evaluate_effect(vm, op_branch->true_eff);
} else {
ret = rz_il_evaluate_effect(vm, op_branch->false_eff);
}
rz_il_bool_free(condition);
return ret;
}