[RzIL] Remove vector-oplists in favor of seq

The root type of a lifted op is now simply a single RzILOpEffect, which
can be for example a chain of seq ops. This is in line with what BAP
uses.
For convenient creation of sequences, `rz_il_op_new_seqn(ut32 n, ...)`
is a drop-in replacement for `rz_il_make_oplist(ut32 n, ...)`.
This commit is contained in:
Florian Märkl 2021-12-28 10:39:51 +01:00
parent 20ed5b924f
commit 6a09385746
14 changed files with 179 additions and 148 deletions

View file

@ -54,8 +54,7 @@ RZ_API bool rz_analysis_op_fini(RzAnalysisOp *op) {
op->switch_op = NULL;
RZ_FREE(op->mnemonic);
if (op->rzil_op) {
// TODO free root_node once it is implemented
rz_pvector_free(op->rzil_op->ops);
rz_il_op_effect_free(op->rzil_op->op);
RZ_FREE(op->rzil_op);
}
return true;

View file

@ -77,49 +77,45 @@ ut64 parse_label_id(char *lbl_name) {
return addr;
}
RzPVector *bf_right_arrow(RzILVM *vm, ut64 id) {
RzILOpEffect *bf_right_arrow(RzILVM *vm, ut64 id) {
// (set ptr (+ (val ptr) (int 1)))
RzILOpBitVector *add = rz_il_op_new_add(bf_il_ptr(), bf_il_one(BF_ADDR_SIZE));
return rz_il_make_oplist(1, bf_il_set_ptr(add));
return bf_il_set_ptr(add);
}
RzPVector *bf_left_arrow(RzILVM *vm, ut64 id) {
RzILOpEffect *bf_left_arrow(RzILVM *vm, ut64 id) {
// (set ptr (- (val ptr) (int 1)))
RzILOpBitVector *sub = rz_il_op_new_sub(bf_il_ptr(), bf_il_one(BF_ADDR_SIZE));
return rz_il_make_oplist(1, bf_il_set_ptr(sub));
return bf_il_set_ptr(sub);
}
RzPVector *bf_inc(RzILVM *vm, ut64 id) {
RzILOpEffect *bf_inc(RzILVM *vm, ut64 id) {
// (store mem (var ptr) (+ (load (var ptr)) (int 1)))
// mem == 0 because is the only mem in bf
RzILOpBitVector *load = rz_il_op_new_load(0, bf_il_ptr());
RzILOpBitVector *add = rz_il_op_new_add(load, bf_il_one(BF_BYTE_SIZE));
RzILOpEffect *store = rz_il_op_new_store(0, bf_il_ptr(), add);
return rz_il_make_oplist(1, store);
return rz_il_op_new_store(0, bf_il_ptr(), add);
}
RzPVector *bf_dec(RzILVM *vm, ut64 id) {
RzILOpEffect *bf_dec(RzILVM *vm, ut64 id) {
// (store mem (var ptr) (- (load (var ptr)) (int 1)))
// mem == 0 because is the only mem in bf
RzILOpBitVector *load = rz_il_op_new_load(0, bf_il_ptr());
RzILOpBitVector *sub = rz_il_op_new_sub(load, bf_il_one(BF_BYTE_SIZE));
RzILOpEffect *store = rz_il_op_new_store(0, bf_il_ptr(), sub);
return rz_il_make_oplist(1, store);
return rz_il_op_new_store(0, bf_il_ptr(), sub);
}
RzPVector *bf_out(RzILVM *vm, ut64 id) {
RzILOpEffect *bf_out(RzILVM *vm, ut64 id) {
// (goto write)
RzILOpEffect *goto_ = rz_il_op_new_goto("write");
return rz_il_make_oplist(1, goto_);
return rz_il_op_new_goto("write");
}
RzPVector *bf_in(RzILVM *vm, ut64 id) {
RzILOpEffect *bf_in(RzILVM *vm, ut64 id) {
// (goto hook_read)
RzILOpEffect *goto_ = rz_il_op_new_goto("read");
return rz_il_make_oplist(1, goto_);
return rz_il_op_new_goto("read");
}
RzPVector *bf_llimit(RzILVM *vm, BfContext *ctx, ut64 id, ut64 addr) {
RzILOpEffect *bf_llimit(RzILVM *vm, BfContext *ctx, ut64 id, ut64 addr) {
// (perform (branch (load mem (var ptr))
// (do nothing)
// (goto ]))
@ -159,13 +155,10 @@ RzPVector *bf_llimit(RzILVM *vm, BfContext *ctx, ut64 id, ut64 addr) {
RzILOpEffect *goto_ = rz_il_op_new_goto(dst_label->label_id);
// branch if (load mem (var ptr)) is false then goto ]
RzILOpEffect *branch = rz_il_op_new_branch(cond, NULL, goto_);
// perform
return rz_il_make_oplist(1, branch);
return rz_il_op_new_branch(cond, NULL, goto_);
}
RzPVector *bf_rlimit(RzILVM *vm, BfContext *ctx, ut64 id, ut64 addr) {
RzILOpEffect *bf_rlimit(RzILVM *vm, BfContext *ctx, ut64 id, ut64 addr) {
// (perform (branch (load mem (var ptr))
// (goto [)
// (do nothing))
@ -202,7 +195,7 @@ RzPVector *bf_rlimit(RzILVM *vm, BfContext *ctx, ut64 id, ut64 addr) {
RzILOpEffect *branch = rz_il_op_new_branch(cond, goto_, NULL);
free(to_free);
return rz_il_make_oplist(1, branch);
return branch;
}
static bool bf_specific_init(RzAnalysisRzil *rzil) {
@ -320,7 +313,7 @@ static int bf_op(RzAnalysis *analysis, RzAnalysisOp *op, ut64 addr, const ut8 *b
BfContext *ctx = analysis->rzil->user;
RzILVM *vm = analysis->rzil->vm;
RzPVector *oplist = NULL;
RzILOpEffect *ilop = NULL;
op->rzil_op = RZ_NEW0(RzAnalysisRzilOp);
if (!op->rzil_op) {
RZ_LOG_ERROR("Fail to init rzil op\n");
@ -330,7 +323,7 @@ static int bf_op(RzAnalysis *analysis, RzAnalysisOp *op, ut64 addr, const ut8 *b
switch (buf[0]) {
case '[':
oplist = bf_llimit(vm, ctx, op->id, addr);
ilop = bf_llimit(vm, ctx, op->id, addr);
op->type = RZ_ANALYSIS_OP_TYPE_CJMP;
op->fail = addr + 1;
buf = rz_mem_dup((void *)buf, len);
@ -377,31 +370,31 @@ static int bf_op(RzAnalysis *analysis, RzAnalysisOp *op, ut64 addr, const ut8 *b
free((ut8 *)buf);
break;
case ']':
oplist = bf_rlimit(vm, ctx, op->id, addr);
ilop = bf_rlimit(vm, ctx, op->id, addr);
op->type = RZ_ANALYSIS_OP_TYPE_UJMP;
break;
case '>':
op->type = RZ_ANALYSIS_OP_TYPE_ADD;
oplist = bf_right_arrow(vm, op->id);
ilop = bf_right_arrow(vm, op->id);
break;
case '<':
op->type = RZ_ANALYSIS_OP_TYPE_SUB;
oplist = bf_left_arrow(vm, op->id);
ilop = bf_left_arrow(vm, op->id);
break;
case '+':
op->type = RZ_ANALYSIS_OP_TYPE_ADD;
oplist = bf_inc(vm, op->id);
ilop = bf_inc(vm, op->id);
break;
case '-':
op->type = RZ_ANALYSIS_OP_TYPE_SUB;
oplist = bf_dec(vm, op->id);
ilop = bf_dec(vm, op->id);
break;
case '.':
oplist = bf_out(vm, op->id);
ilop = bf_out(vm, op->id);
op->type = RZ_ANALYSIS_OP_TYPE_STORE;
break;
case ',':
oplist = bf_in(vm, op->id);
ilop = bf_in(vm, op->id);
op->type = RZ_ANALYSIS_OP_TYPE_LOAD;
break;
case 0x00:
@ -410,10 +403,11 @@ static int bf_op(RzAnalysis *analysis, RzAnalysisOp *op, ut64 addr, const ut8 *b
break;
default:
op->type = RZ_ANALYSIS_OP_TYPE_NOP;
ilop = rz_il_op_new_nop();
break;
}
if (oplist) {
op->rzil_op->ops = oplist;
if (ilop) {
op->rzil_op->op = ilop;
}
ctx->op_count++;
return op->size;

View file

@ -636,8 +636,6 @@ RZ_IPI void rz_core_analysis_rzil_vm_status(RzCore *core, const char *var_name,
// step a list of ct_opcode at a given address
RZ_IPI void rz_core_rzil_step(RzCore *core) {
RzPVector *oplist;
if (!core->analysis || !core->analysis->rzil) {
RZ_LOG_ERROR("RzIL: Run 'aezi' first to initialize the VM\n");
return;
@ -662,10 +660,10 @@ RZ_IPI void rz_core_rzil_step(RzCore *core) {
// analysis current data to trigger rzil_set_op_code
(void)rz_io_read_at_mapped(core->io, addr, code, sizeof(code));
int size = rz_analysis_op(analysis, &op, addr, code, sizeof(code), RZ_ANALYSIS_OP_MASK_ESIL | RZ_ANALYSIS_OP_MASK_HINT);
oplist = op.rzil_op ? op.rzil_op->ops : NULL;
RzILOpEffect *ilop = op.rzil_op ? op.rzil_op->op : NULL;
if (oplist) {
rz_il_vm_list_step(vm, oplist, size > 0 ? size : 1);
if (ilop) {
rz_il_vm_list_step(vm, ilop, size > 0 ? size : 1);
} else {
RZ_LOG_ERROR("RzIL: invalid instruction detected or reach the end of code at address 0x%08" PFMT64x "\n", addr);
}

View file

@ -1052,9 +1052,9 @@ static void core_analysis_bytes(RzCore *core, const ut8 *buf, int len, int nops,
pj_ks(pj, "esil", jesil);
}
if (op.rzil_op) {
if (op.rzil_op->ops) {
if (op.rzil_op->op) {
pj_k(pj, "rzil");
rz_il_oplist_json(op.rzil_op->ops, pj);
rz_il_op_effect_json(op.rzil_op->op, pj);
} else {
pj_knull(pj, "rzil");
}
@ -1235,15 +1235,11 @@ static void core_analysis_bytes(RzCore *core, const ut8 *buf, int len, int nops,
} else if (RZ_STR_ISNOTEMPTY(esilstr)) {
printline("esil", "%s\n", esilstr);
}
if (op.rzil_op) {
if (op.rzil_op->ops) {
RzStrBuf *sbil = rz_strbuf_new("");
rz_il_oplist_stringify(op.rzil_op->ops, sbil);
printline("rzil", "%s\n", rz_strbuf_get(sbil));
rz_strbuf_free(sbil);
} else {
printline_noarg("rzil", "[]");
}
if (op.rzil_op && op.rzil_op->op) {
RzStrBuf *sbil = rz_strbuf_new("");
rz_il_op_effect_stringify(op.rzil_op->op, sbil);
printline("rzil", "%s\n", rz_strbuf_get(sbil));
rz_strbuf_free(sbil);
}
if (hint && hint->jump != UT64_MAX) {
op.jump = hint->jump;

View file

@ -665,45 +665,6 @@ RZ_API void rz_il_op_effect_json(RZ_NONNULL RzILOpEffect *op, RZ_NONNULL PJ *pj)
il_op_effect_resolve(op, NULL, pj);
}
/**
* Generates the string representation of the IL statements
* \param op_list RzPVector*, array of IL statements
* \param sb RzStrBuf*, a pointer to the string buffer
*/
RZ_API void rz_il_oplist_stringify(RZ_NONNULL RzPVector *op_list, RZ_NONNULL RzStrBuf *sb) {
rz_return_if_fail(op_list && sb);
ut32 i = 0;
void **iter;
rz_strbuf_append(sb, "[");
rz_pvector_foreach (op_list, iter) {
RzILOpEffect *ilop = *iter;
if (i > 0) {
rz_strbuf_append(sb, ", ");
}
rz_il_op_effect_stringify(ilop, sb);
i++;
}
rz_strbuf_append(sb, "]");
}
/**
* Generates the JSON representation of the IL statements
* \param code RzPVector*, array of IL statements
* \param pj PJ*, a pointer to the JSON buffer
*/
RZ_API void rz_il_oplist_json(RZ_NONNULL RzPVector *op_list, RZ_NONNULL PJ *pj) {
rz_return_if_fail(op_list && pj);
pj_a(pj);
void **iter;
rz_pvector_foreach (op_list, iter) {
RzILOpEffect *ilop = *iter;
rz_il_op_effect_json(ilop, pj);
}
pj_end(pj);
}
RZ_API void rz_il_event_stringify(RZ_NONNULL RzILEvent *evt, RZ_NONNULL RzStrBuf *sb) {
rz_return_if_fail(evt && sb);
char *tmp0 = NULL, *tmp1 = NULL, *tmp2 = NULL;

View file

@ -560,6 +560,64 @@ RZ_API RZ_OWN RzILOpEffect *rz_il_op_new_seq(RZ_NONNULL RzILOpEffect *x, RZ_NONN
return ret;
}
/**
* Chain \p n opcodes given as varargs in sequence using seq if necessary
*
* It works exactly like this seq helper from BAP:
* let rec seq = function
* | [] -> CT.perform Theory.Effect.Sort.bot
* | [x] -> x
* | x :: xs -> CT.seq x @@ seq xs
*
* \param n number of total opcodes given
* \param ... \p num RzILOpEffect * ops to be executed in sequence
*/
RZ_API RZ_OWN RzILOpEffect *rz_il_op_new_seqn(ut32 n, ...) {
if (!n) {
return rz_il_op_new_nop();
}
RzILOpEffect *root = NULL;
RzILOpEffect *prev_seq = NULL;
va_list args;
va_start(args, n);
for (ut32 i = 0; i < n; ++i) {
RzILOpEffect *cur_op = va_arg(args, RzILOpEffect *);
if (i == n - 1) {
// last one
if (prev_seq) {
prev_seq->op.seq->y = cur_op;
} else {
// n == 1, no need for seq at all
root = cur_op;
}
break;
}
RzILOpEffect *seq = RZ_NEW0(RzILOpEffect);
if (!seq) {
break;
}
seq->op.seq = RZ_NEW0(RzILOpArgsSeq);
if (!seq->op.seq) {
free(seq);
break;
}
seq->code = RZIL_OP_SEQ;
seq->op.seq->x = cur_op;
if (prev_seq) {
// not the first one
// We let the seq recurse in the second op because that
// can enable tail call elimination in the evaluation.
prev_seq->op.seq->y = seq;
} else {
// first one
root = seq;
}
prev_seq = seq;
}
va_end(args);
return root;
}
/**
* \brief op structure for `blk` (label -> data eff -> ctrl eff -> unit eff)
*

View file

@ -386,27 +386,6 @@ RZ_API RZ_BORROW RzILEffectLabel *rz_il_vm_update_label(RZ_NONNULL RzILVM *vm, R
return lbl;
}
/**
* Make a core theory opcode vector
* \param num int, number of total opcodes
* \param ... op RzILOp, op will be pushed to vector one by one
* \return oplist RzPVector*, pointer to the opcode
*/
RZ_API RZ_OWN RzPVector *rz_il_make_oplist(ut32 num, ...) {
va_list args;
RzILOpEffect *cur_op;
RzPVector *oplist = rz_pvector_new((RzPVectorFree)rz_il_op_effect_free);
va_start(args, num);
for (ut32 i = 0; i < num; ++i) {
cur_op = va_arg(args, RzILOpEffect *);
rz_pvector_push(oplist, cur_op);
}
va_end(args);
return oplist;
}
static void *eval_pure(RZ_NONNULL RzILVM *vm, RZ_NONNULL RzILOpPure *op, RZ_NONNULL RzILPureType *type) {
rz_return_val_if_fail(vm && op && type, NULL);
RzILOpPureHandler handler = vm->op_handler_pure_table[op->code];

View file

@ -400,20 +400,12 @@ RZ_API void rz_il_vm_event_add(RzILVM *vm, RzILEvent *evt) {
* \param op_list, a list of op roots.
* \param op_size, how much the pc value has to increate of.
*/
RZ_API bool rz_il_vm_list_step(RzILVM *vm, RzPVector *op_list, ut32 op_size) {
rz_return_val_if_fail(vm && op_list, false);
RZ_API bool rz_il_vm_list_step(RzILVM *vm, RzILOpEffect *op, ut32 op_size) {
rz_return_val_if_fail(vm && op, false);
rz_list_purge(vm->events);
bool succ = true;
void **iter;
rz_pvector_foreach (op_list, iter) {
RzILOpEffect *root = *iter;
if (!rz_il_evaluate_effect(vm, root)) {
succ = false;
break;
}
}
bool succ = rz_il_evaluate_effect(vm, op);
RzBitVector *step = rz_bv_new_from_ut64(vm->pc->len, op_size);
RzBitVector *next_pc = rz_bv_add(vm->pc, step, NULL);

View file

@ -807,9 +807,8 @@ typedef enum rz_analysis_data_type_t {
RZ_ANALYSIS_DATATYPE_FLOAT,
} RzAnalysisDataType;
// For switchting to AST-like approach in the future
typedef struct rz_analysis_rzil_op_t {
RzPVector *ops;
RzILOpEffect *op;
} RzAnalysisRzilOp;
typedef struct rz_analysis_op_t {

View file

@ -524,6 +524,7 @@ RZ_API RZ_OWN RzILOpEffect *rz_il_op_new_let(RZ_NONNULL const char *var, RZ_NONN
RZ_API RZ_OWN RzILOpEffect *rz_il_op_new_jmp(RZ_NONNULL RzILOpBitVector *dst);
RZ_API RZ_OWN RzILOpEffect *rz_il_op_new_goto(RZ_NONNULL const char *label);
RZ_API RZ_OWN RzILOpEffect *rz_il_op_new_seq(RZ_NONNULL RzILOpEffect *x, RZ_NONNULL RzILOpEffect *y);
RZ_API RZ_OWN RzILOpEffect *rz_il_op_new_seqn(ut32 n, ...);
RZ_API RZ_OWN RzILOpEffect *rz_il_op_new_blk(RZ_NONNULL RzILOpEffect *data_effect, RZ_NONNULL RzILOpEffect *ctrl_effect);
RZ_API RZ_OWN RzILOpEffect *rz_il_op_new_repeat(RZ_NONNULL RzILOpBool *condition, RZ_NONNULL RzILOpEffect *data_effect);
RZ_API RZ_OWN RzILOpEffect *rz_il_op_new_branch(RZ_NONNULL RzILOpBool *condition, RZ_NULLABLE RzILOpEffect *true_effect, RZ_NULLABLE RzILOpEffect *false_effect);

View file

@ -94,17 +94,11 @@ RZ_API RZ_BORROW RzILVal *rz_il_vm_fortify_bool(RZ_NONNULL RzILVM *vm, bool val)
RZ_API void rz_il_vm_add_reg(RZ_NONNULL RzILVM *vm, RZ_NONNULL const char *name, ut32 length);
RZ_API void rz_il_vm_add_bit_reg(RZ_NONNULL RzILVM *vm, RZ_NONNULL const char *name, bool value);
// VM store and load core theory opcodes
RZ_API RZ_OWN RzPVector *rz_il_make_oplist(ut32 num, ...);
#define rz_il_make_nop_list() rz_il_make_oplist(0, NULL)
RZ_API void rz_il_op_pure_stringify(RZ_NONNULL RzILOpPure *op, RZ_NONNULL RzStrBuf *sb);
RZ_API void rz_il_op_effect_stringify(RZ_NONNULL RzILOpEffect *op, RZ_NONNULL RzStrBuf *sb);
RZ_API void rz_il_oplist_stringify(RZ_NONNULL RzPVector *oplist, RZ_NONNULL RzStrBuf *sb);
RZ_API void rz_il_op_pure_json(RZ_NONNULL RzILOpPure *op, RZ_NONNULL PJ *pj);
RZ_API void rz_il_op_effect_json(RZ_NONNULL RzILOpEffect *op, RZ_NONNULL PJ *pj);
RZ_API void rz_il_oplist_json(RZ_NONNULL RzPVector *oplist, RZ_NONNULL PJ *pj);
RZ_API void rz_il_event_stringify(RZ_NONNULL RzILEvent *evt, RZ_NONNULL RzStrBuf *sb);
RZ_API void rz_il_event_json(RZ_NONNULL RzILEvent *evt, RZ_NONNULL PJ *pj);

View file

@ -17,7 +17,7 @@ RZ_API bool rz_il_vm_init(RzILVM *vm, ut64 start_addr, ut32 addr_size, bool big_
RZ_API void rz_il_vm_fini(RzILVM *vm);
RZ_API void rz_il_vm_add_mem(RzILVM *vm, RzILMemIndex index, RZ_OWN RzILMem *mem);
RZ_API RzILMem *rz_il_vm_get_mem(RzILVM *vm, RzILMemIndex index);
RZ_API bool rz_il_vm_list_step(RzILVM *vm, RzPVector *op_list, ut32 op_size);
RZ_API bool rz_il_vm_list_step(RzILVM *vm, RzILOpEffect *op, ut32 op_size);
// VM Event operations
RZ_API void rz_il_vm_event_add(RzILVM *vm, RzILEvent *evt);

View file

@ -78,29 +78,29 @@ aoj @ 0x6a ~{.rzil}
EOF
EXPECT=<<EOF
opcode: inc [ptr]
rzil: [store(mem:0, key:var(v:ptr), value:add(x:load(mem:0, key:var(v:ptr)), y:bitv(bits:0x01, len:8)))]
[{"opcode":"store","mem":0,"key":{"opcode":"var","value":"ptr"},"value":{"opcode":"add","x":{"opcode":"load","mem":0,"key":{"opcode":"var","value":"ptr"}},"y":{"opcode":"bitv","bits":"0x01","len":8}}}]
rzil: store(mem:0, key:var(v:ptr), value:add(x:load(mem:0, key:var(v:ptr)), y:bitv(bits:0x01, len:8)))
{"opcode":"store","mem":0,"key":{"opcode":"var","value":"ptr"},"value":{"opcode":"add","x":{"opcode":"load","mem":0,"key":{"opcode":"var","value":"ptr"}},"y":{"opcode":"bitv","bits":"0x01","len":8}}}
opcode: while [ptr]
rzil: [branch(condition:inv(x:is_zero(bv:load(mem:0, key:var(v:ptr)))), true_eff:nop(), false_eff:goto(lbl:]8))]
[{"opcode":"branch","condition":{"opcode":"inv","x":{"opcode":"is_zero","bv":{"opcode":"load","mem":0,"key":{"opcode":"var","value":"ptr"}}}},"true_eff":{"opcode":"nop"},"false_eff":{"opcode":"goto","label":"]8"}}]
rzil: branch(condition:inv(x:is_zero(bv:load(mem:0, key:var(v:ptr)))), true_eff:nop(), false_eff:goto(lbl:]8))
{"opcode":"branch","condition":{"opcode":"inv","x":{"opcode":"is_zero","bv":{"opcode":"load","mem":0,"key":{"opcode":"var","value":"ptr"}}}},"true_eff":{"opcode":"nop"},"false_eff":{"opcode":"goto","label":"]8"}}
opcode: inc ptr
rzil: [set(v:ptr, x:add(x:var(v:ptr), y:bitv(bits:0x0000000000000001, len:64)))]
[{"opcode":"set","dst":"ptr","src":{"opcode":"add","x":{"opcode":"var","value":"ptr"},"y":{"opcode":"bitv","bits":"0x0000000000000001","len":64}}}]
rzil: set(v:ptr, x:add(x:var(v:ptr), y:bitv(bits:0x0000000000000001, len:64)))
{"opcode":"set","dst":"ptr","src":{"opcode":"add","x":{"opcode":"var","value":"ptr"},"y":{"opcode":"bitv","bits":"0x0000000000000001","len":64}}}
opcode: dec ptr
rzil: [set(v:ptr, x:sub(x:var(v:ptr), y:bitv(bits:0x0000000000000001, len:64)))]
[{"opcode":"set","dst":"ptr","src":{"opcode":"sub","x":{"opcode":"var","value":"ptr"},"y":{"opcode":"bitv","bits":"0x0000000000000001","len":64}}}]
rzil: set(v:ptr, x:sub(x:var(v:ptr), y:bitv(bits:0x0000000000000001, len:64)))
{"opcode":"set","dst":"ptr","src":{"opcode":"sub","x":{"opcode":"var","value":"ptr"},"y":{"opcode":"bitv","bits":"0x0000000000000001","len":64}}}
opcode: dec [ptr]
rzil: [store(mem:0, key:var(v:ptr), value:sub(x:load(mem:0, key:var(v:ptr)), y:bitv(bits:0x01, len:8)))]
[{"opcode":"store","mem":0,"key":{"opcode":"var","value":"ptr"},"value":{"opcode":"sub","x":{"opcode":"load","mem":0,"key":{"opcode":"var","value":"ptr"}},"y":{"opcode":"bitv","bits":"0x01","len":8}}}]
rzil: store(mem:0, key:var(v:ptr), value:sub(x:load(mem:0, key:var(v:ptr)), y:bitv(bits:0x01, len:8)))
{"opcode":"store","mem":0,"key":{"opcode":"var","value":"ptr"},"value":{"opcode":"sub","x":{"opcode":"load","mem":0,"key":{"opcode":"var","value":"ptr"}},"y":{"opcode":"bitv","bits":"0x01","len":8}}}
opcode: loop
rzil: [branch(condition:inv(x:is_zero(bv:load(mem:0, key:var(v:ptr)))), true_eff:goto(lbl:[14), false_eff:nop())]
[{"opcode":"branch","condition":{"opcode":"inv","x":{"opcode":"is_zero","bv":{"opcode":"load","mem":0,"key":{"opcode":"var","value":"ptr"}}}},"true_eff":{"opcode":"goto","label":"[14"},"false_eff":{"opcode":"nop"}}]
rzil: branch(condition:inv(x:is_zero(bv:load(mem:0, key:var(v:ptr)))), true_eff:goto(lbl:[14), false_eff:nop())
{"opcode":"branch","condition":{"opcode":"inv","x":{"opcode":"is_zero","bv":{"opcode":"load","mem":0,"key":{"opcode":"var","value":"ptr"}}}},"true_eff":{"opcode":"goto","label":"[14"},"false_eff":{"opcode":"nop"}}
opcode: out [ptr]
rzil: [goto(lbl:write)]
[{"opcode":"goto","label":"write"}]
rzil: goto(lbl:write)
{"opcode":"goto","label":"write"}
opcode: nop
rzil: []
null
rzil: nop()
{"opcode":"nop"}
EOF
RUN

View file

@ -234,6 +234,65 @@ static bool test_rzil_mem_storew() {
mu_end;
}
static bool test_rzil_seqn() {
// n = 0 ==> just a nop
RzILOpEffect *s = rz_il_op_new_seqn(0);
mu_assert_notnull(s, "seqn 0");
mu_assert_eq(s->code, RZIL_OP_NOP, "seqn 0 nop");
rz_il_op_effect_free(s);
// n = 1 ==> just the op
RzILOpEffect *e0 = rz_il_op_new_goto("beach");
s = rz_il_op_new_seqn(1, e0);
mu_assert_notnull(s, "seqn 1");
mu_assert_ptreq(s, e0, "seqn 1 op");
rz_il_op_effect_free(s);
// n = 2 ==> single seq
// (seq e0 e1)
e0 = rz_il_op_new_goto("beach");
RzILOpEffect *e1 = rz_il_op_new_goto("beach2");
s = rz_il_op_new_seqn(2, e0, e1);
mu_assert_notnull(s, "seqn 2");
mu_assert_eq(s->code, RZIL_OP_SEQ, "seqn 2 seq");
mu_assert_ptreq(s->op.seq->x, e0, "seqn 2 first");
mu_assert_ptreq(s->op.seq->y, e1, "seqn 2 second");
rz_il_op_effect_free(s);
// n = 3 ==> nested seq with recursion in the second op:
// (seq e0 (seq e1 e2))
e0 = rz_il_op_new_goto("beach");
e1 = rz_il_op_new_goto("beach2");
RzILOpEffect *e2 = rz_il_op_new_goto("beach3");
s = rz_il_op_new_seqn(3, e0, e1, e2);
mu_assert_notnull(s, "seqn 3");
mu_assert_eq(s->code, RZIL_OP_SEQ, "seqn 3 seq");
mu_assert_ptreq(s->op.seq->x, e0, "seqn 3 first");
mu_assert_eq(s->op.seq->y->code, RZIL_OP_SEQ, "seqn 3 second seq");
mu_assert_ptreq(s->op.seq->y->op.seq->x, e1, "seqn 3 second");
mu_assert_ptreq(s->op.seq->y->op.seq->y, e2, "seqn 3 third");
rz_il_op_effect_free(s);
// n = 4 ==> nested seq with recursion in the second op and no confusion:
// (seq e0 (seq e1 (seq e2 e3)))
e0 = rz_il_op_new_goto("beach");
e1 = rz_il_op_new_goto("beach2");
e2 = rz_il_op_new_goto("beach3");
RzILOpEffect *e3 = rz_il_op_new_goto("beach3");
s = rz_il_op_new_seqn(4, e0, e1, e2, e3);
mu_assert_notnull(s, "seqn 4");
mu_assert_eq(s->code, RZIL_OP_SEQ, "seqn 4 seq");
mu_assert_ptreq(s->op.seq->x, e0, "seqn 4 first");
mu_assert_eq(s->op.seq->y->code, RZIL_OP_SEQ, "seqn 4 second seq");
mu_assert_ptreq(s->op.seq->y->op.seq->x, e1, "seqn 4 second");
mu_assert_eq(s->op.seq->y->op.seq->y->code, RZIL_OP_SEQ, "seqn 4 third seq");
mu_assert_ptreq(s->op.seq->y->op.seq->y->op.seq->x, e2, "seqn 4 third");
mu_assert_ptreq(s->op.seq->y->op.seq->y->op.seq->y, e3, "seqn 4 fourth");
rz_il_op_effect_free(s);
mu_end;
}
bool all_tests() {
mu_run_test(test_rzil_bool_init);
mu_run_test(test_rzil_bool_logic);
@ -241,6 +300,7 @@ bool all_tests() {
mu_run_test(test_rzil_mem_store);
mu_run_test(test_rzil_mem_loadw);
mu_run_test(test_rzil_mem_storew);
mu_run_test(test_rzil_seqn);
return tests_passed != tests_run;
}