diff --git a/librz/anal/Makefile b/librz/anal/Makefile index b87e9ea9c4..519a5224c9 100644 --- a/librz/anal/Makefile +++ b/librz/anal/Makefile @@ -36,6 +36,7 @@ OBJLIBS+=esil_sources.o esil_interrupt.o esil_cfg.o OBJLIBS+=esil_stats.o esil_trace.o flirt.o labels.o OBJLIBS+=esil2reil.o pin.o vtable.o rtti.o OBJLIBS+=rtti_msvc.o rtti_itanium.o jmptbl.o function.o +OBJLIBS+=serialize_anal.o ASMOBJS+=$(LTOP)/asm/arch/xtensa/gnu/xtensa-modules.o ASMOBJS+=$(LTOP)/asm/arch/xtensa/gnu/xtensa-isa.o ASMOBJS+=$(LTOP)/asm/arch/xtensa/gnu/elf32-xtensa.o diff --git a/librz/anal/meson.build b/librz/anal/meson.build index d005eece32..ccec615fb0 100644 --- a/librz/anal/meson.build +++ b/librz/anal/meson.build @@ -38,6 +38,7 @@ rz_anal_sources = [ 'var.c', 'vtable.c', 'xrefs.c', + 'serialize_anal.c', 'p/anal_6502.c', 'p/anal_6502_cs.c', 'p/anal_8051.c', diff --git a/librz/anal/serialize_anal.c b/librz/anal/serialize_anal.c new file mode 100644 index 0000000000..0aa4a0b66f --- /dev/null +++ b/librz/anal/serialize_anal.c @@ -0,0 +1,2079 @@ +/* radare - LGPL - Copyright 2020 - thestr4ng3r */ + +#include +#include + +#include "../util/serialize_helper.h" + +#include + +/* + * + * SDB Format: + * + * / + * /blocks + * 0x={size:, jump?:, fail?:, traced?:true, folded?:true, colorize?:, + * fingerprint?:"", diff?: , switch_op?:, + * ninstr:, op_pos?:[], stackptr:, parent_stackptr:, + * cmpval:, cmpreg?:} + * /functions + * 0x={name:, bits?:, type:, cc?:, stack:, maxstack:, + * ninstr:, folded?:, pure?:, bp_frame?:, bp_off?:, noreturn?:, + * fingerprint?:"", diff?:, bbs:[], imports?:[], vars?:[], + * labels?: {:}} + * /xrefs + * 0x=[{to:, type?:"c"|"C"|"d"|"s"}] + * + * /meta + * 0x=[{size?:, type:, subtype?:, str?:, space?:}] + * /spaces + * see spaces.c + * + * /hints + * 0x={arch?:,bits?:,toff?:,nword?:,jump?:,fail?:,newbits?:, + * immbase?:,ptr?:,ret?:,syntax?:,opcode?:,esil?:,optype?:, + * size?:,frame?:,val?:,high?:} + * /classes + * + * /attrs + * + * + * /zigns + * + * /spaces + * see spaces.c + * + * /imports + * =i + * + * RzAnalDiff JSON: + * {type?:"m"|"u", addr:, dist:, name?:, size:} + * + * RzAnalSwitchOp JSON: + * {addr:, min:, max:, def:, cases:[]} + * + * RzAnalCaseOp JSON: + * {addr:, jump:, value:} + * + * RzAnalVar JSON: + * {name:, type:, kind:"s|b|r", arg?:, delta?:, reg?:, cmt?:, + * accs?: [{off:, type:"r|w|rw", reg:, sp?:}], constrs?:[,,...]} + * + */ + +RZ_API void rz_serialize_anal_diff_save(RZ_NONNULL PJ *j, RZ_NONNULL RzAnalDiff *diff) { + pj_o (j); + switch (diff->type) { + case RZ_ANAL_DIFF_TYPE_MATCH: + pj_ks (j, "type", "m"); + break; + case RZ_ANAL_DIFF_TYPE_UNMATCH: + pj_ks (j, "type", "u"); + break; + } + if (diff->addr != UT64_MAX) { + pj_kn (j, "addr", diff->addr); + } + if (diff->dist != 0.0) { + pj_kd (j, "dist", diff->dist); + } + if (diff->name) { + pj_ks (j, "name", diff->name); + } + if (diff->size) { + pj_kn (j, "size", (ut64)diff->size); + } + pj_end (j); +} + +enum { + DIFF_FIELD_TYPE, + DIFF_FIELD_ADDR, + DIFF_FIELD_DIST, + DIFF_FIELD_NAME, + DIFF_FIELD_SIZE +}; + +RZ_API RSerializeAnalDiffParser rz_serialize_anal_diff_parser_new(void) { + RSerializeAnalDiffParser parser = key_parser_new (); + if (!parser) { + return NULL; + } + key_parser_add (parser, "type", DIFF_FIELD_TYPE); + key_parser_add (parser, "addr", DIFF_FIELD_ADDR); + key_parser_add (parser, "dist", DIFF_FIELD_DIST); + key_parser_add (parser, "name", DIFF_FIELD_NAME); + key_parser_add (parser, "size", DIFF_FIELD_SIZE); + return parser; +} + +RZ_API void rz_serialize_anal_diff_parser_free(RSerializeAnalDiffParser parser) { + key_parser_free (parser); +} + +RZ_API RZ_NULLABLE RzAnalDiff *rz_serialize_anal_diff_load(RZ_NONNULL RSerializeAnalDiffParser parser, RZ_NONNULL const RJson *json) { + if (json->type != RZ_JSON_OBJECT) { + return NULL; + } + RzAnalDiff *diff = rz_anal_diff_new (); + if (!diff) { + return NULL; + } + KEY_PARSER_JSON (parser, json, child, { + case DIFF_FIELD_TYPE: + if (child->type != RZ_JSON_STRING) { + break; + } + if (strcmp (child->str_value, "m") == 0) { + diff->type = RZ_ANAL_DIFF_TYPE_MATCH; + } else if (strcmp (child->str_value, "u") == 0) { + diff->type = RZ_ANAL_DIFF_TYPE_UNMATCH; + } + break; + case DIFF_FIELD_ADDR: + if (child->type != RZ_JSON_INTEGER) { + break; + } + diff->addr = child->num.u_value; + break; + case DIFF_FIELD_DIST: + if (child->type == RZ_JSON_INTEGER) { + diff->dist = child->num.u_value; + } else if (child->type == RZ_JSON_DOUBLE) { + diff->dist = child->num.dbl_value; + } + break; + case DIFF_FIELD_NAME: + if (child->type != RZ_JSON_STRING) { + break; + } + free (diff->name); + diff->name = strdup (child->str_value); + break; + case DIFF_FIELD_SIZE: + if (child->type != RZ_JSON_INTEGER) { + break; + } + diff->size = child->num.u_value; + break; + default: + break; + }) + return diff; +} + +RZ_API void rz_serialize_anal_case_op_save(RZ_NONNULL PJ *j, RZ_NONNULL RzAnalCaseOp *op) { + pj_o (j); + pj_kn (j, "addr", op->addr); + pj_kn (j, "jump", op->jump); + pj_kn (j, "value", op->value); + pj_end (j); +} + +RZ_API void rz_serialize_anal_switch_op_save(RZ_NONNULL PJ *j, RZ_NONNULL RzAnalSwitchOp *op) { + pj_o (j); + pj_kn (j, "addr", op->addr); + pj_kn (j, "min", op->min_val); + pj_kn (j, "max", op->max_val); + pj_kn (j, "def", op->def_val); + pj_k (j, "cases"); + pj_a (j); + RzListIter *it; + RzAnalCaseOp *cop; + rz_list_foreach (op->cases, it, cop) { + rz_serialize_anal_case_op_save (j, cop); + } + pj_end (j); + pj_end (j); +} + +RZ_API RzAnalSwitchOp *rz_serialize_anal_switch_op_load(RZ_NONNULL const RJson *json) { + if (json->type != RZ_JSON_OBJECT) { + return NULL; + } + RzAnalSwitchOp *sop = rz_anal_switch_op_new (0, 0, 0, 0); + if (!sop) { + return NULL; + } + RJson *child; + for (child = json->children.first; child; child = child->next) { + if (child->type == RZ_JSON_INTEGER) { + if (strcmp (child->key, "addr") == 0) { + sop->addr = child->num.u_value; + } else if (strcmp (child->key, "min") == 0) { + sop->min_val = child->num.u_value; + } else if (strcmp (child->key, "max") == 0) { + sop->max_val = child->num.u_value; + } else if (strcmp (child->key, "def") == 0) { + sop->def_val = child->num.u_value; + } + } else if (child->type == RZ_JSON_ARRAY && strcmp (child->key, "cases") == 0) { + RJson *baby; + for (baby = child->children.first; baby; baby = baby->next) { + if (baby->type != RZ_JSON_OBJECT) { + continue; + } + ut64 addr = UT64_MAX; + ut64 jump = UT64_MAX; + ut64 value = UT64_MAX; + RJson *semen; + for (semen = baby->children.first; semen; semen = semen->next) { + if (semen->type != RZ_JSON_INTEGER) { + continue; + } + if (strcmp (semen->key, "addr") == 0) { + addr = semen->num.u_value; + } else if (strcmp (semen->key, "jump") == 0) { + jump = semen->num.u_value; + } else if (strcmp (semen->key, "value") == 0) { + value = semen->num.u_value; + } + } + rz_anal_switch_op_add_case (sop, addr, value, jump); + } + } + } + return sop; +} + +static void block_store(RZ_NONNULL Sdb *db, const char *key, RzAnalBlock *block) { + PJ *j = pj_new (); + if (!j) { + return; + } + pj_o (j); + + pj_kn (j, "size", block->size); + if (block->jump != UT64_MAX) { + pj_kn (j, "jump", block->jump); + } + if (block->fail != UT64_MAX) { + pj_kn (j, "fail", block->fail); + } + if (block->traced) { + pj_kb (j, "traced", true); + } + if (block->folded) { + pj_kb (j, "folded", true); + } + if (block->colorize) { + pj_kn (j, "colorize", (ut64)block->colorize); + } + if (block->fingerprint) { + char *b64 = rz_base64_encode_dyn ((const char *)block->fingerprint, block->size); + if (b64) { + pj_ks (j, "fingerprint", b64); + free (b64); + } + } + if (block->diff) { + pj_k (j, "diff"); + rz_serialize_anal_diff_save (j, block->diff); + } + + // TODO: cond? It's used nowhere... + + if (block->switch_op) { + pj_k (j, "switch_op"); + rz_serialize_anal_switch_op_save (j, block->switch_op); + } + + if (block->ninstr) { + pj_ki (j, "ninstr", block->ninstr); + } + if (block->op_pos && block->ninstr > 1) { + pj_k (j, "op_pos"); + pj_a (j); + size_t i; + for (i = 0; i < block->ninstr - 1; i++) { + pj_n (j, block->op_pos[i]); + } + pj_end (j); + } + + // op_bytes is only java, never set + // parent_reg_arena is never set + + if (block->stackptr) { + pj_ki (j, "stackptr", block->stackptr); + } + if (block->parent_stackptr != INT_MAX) { + pj_ki (j, "parent_stackptr", block->parent_stackptr); + } + if (block->cmpval != UT64_MAX) { + pj_kn (j, "cmpval", block->cmpval); + } + if (block->cmpreg) { + pj_ks (j, "cmpreg", block->cmpreg); + } + + pj_end (j); + sdb_set (db, key, pj_string (j), 0); + pj_free (j); +} + +RZ_API void rz_serialize_anal_blocks_save(RZ_NONNULL Sdb *db, RZ_NONNULL RzAnal *anal) { + RBIter iter; + RzAnalBlock *block; + RzStrBuf key = { 0 }; + rz_rbtree_foreach (anal->bb_tree, iter, block, RzAnalBlock, _rb) { + rz_strbuf_setf (&key, "0x%"PFMT64x, block->addr); + block_store (db, rz_strbuf_get (&key), block); + } + rz_strbuf_fini (&key); +} + +enum { + BLOCK_FIELD_SIZE, + BLOCK_FIELD_JUMP, + BLOCK_FIELD_FAIL, + BLOCK_FIELD_TRACED, + BLOCK_FIELD_FOLDED, + BLOCK_FIELD_COLORIZE, + BLOCK_FIELD_FINGERPRINT, + BLOCK_FIELD_DIFF, + BLOCK_FIELD_SWITCH_OP, + BLOCK_FIELD_NINSTR, + BLOCK_FIELD_OP_POS, + BLOCK_FIELD_STACKPTR, + BLOCK_FIELD_PARENT_STACKPTR, + BLOCK_FIELD_CMPVAL, + BLOCK_FIELD_CMPREG +}; + +typedef struct { + RzAnal *anal; + KeyParser *parser; + RSerializeAnalDiffParser diff_parser; +} BlockLoadCtx; + +static bool block_load_cb(void *user, const char *k, const char *v) { + BlockLoadCtx *ctx = user; + + char *json_str = strdup (v); + if (!json_str) { + return true; + } + RJson *json = rz_json_parse (json_str); + if (!json || json->type != RZ_JSON_OBJECT) { + free (json_str); + return false; + } + + RzAnalBlock proto = { 0 }; + proto.jump = UT64_MAX; + proto.fail = UT64_MAX; + proto.size = UT64_MAX; + proto.parent_stackptr = INT_MAX; + proto.cmpval = UT64_MAX; + size_t fingerprint_size = SIZE_MAX; + KEY_PARSER_JSON (ctx->parser, json, child, { + case BLOCK_FIELD_SIZE: + if (child->type != RZ_JSON_INTEGER) { + break; + } + proto.size = child->num.u_value; + break; + case BLOCK_FIELD_JUMP: + if (child->type != RZ_JSON_INTEGER) { + break; + } + proto.jump = child->num.u_value; + break; + case BLOCK_FIELD_FAIL: + if (child->type != RZ_JSON_INTEGER) { + break; + } + proto.fail = child->num.u_value; + break; + case BLOCK_FIELD_TRACED: + if (child->type != RZ_JSON_BOOLEAN) { + break; + } + proto.traced = child->num.u_value; + break; + case BLOCK_FIELD_FOLDED: + if (child->type != RZ_JSON_BOOLEAN) { + break; + } + proto.folded = child->num.u_value; + break; + case BLOCK_FIELD_COLORIZE: + if (child->type != RZ_JSON_INTEGER) { + break; + } + proto.colorize = (ut32)child->num.u_value; + break; + case BLOCK_FIELD_FINGERPRINT: { + if (child->type != RZ_JSON_STRING) { + break; + } + if (proto.fingerprint) { + free (proto.fingerprint); + proto.fingerprint = NULL; + } + fingerprint_size = strlen (child->str_value); + if (!fingerprint_size) { + break; + } + proto.fingerprint = malloc (fingerprint_size); + if (!proto.fingerprint) { + break; + } + int decsz = rz_base64_decode (proto.fingerprint, child->str_value, fingerprint_size); + if (decsz <= 0) { + free (proto.fingerprint); + proto.fingerprint = NULL; + fingerprint_size = 0; + } else if (decsz < fingerprint_size) { + ut8 *n = realloc (proto.fingerprint, (size_t)decsz); + if (n) { + proto.fingerprint = n; + fingerprint_size = decsz; + } + } + break; + } + case BLOCK_FIELD_DIFF: + rz_anal_diff_free (proto.diff); + proto.diff = rz_serialize_anal_diff_load (ctx->diff_parser, child); + break; + case BLOCK_FIELD_SWITCH_OP: + rz_anal_switch_op_free (proto.switch_op); + proto.switch_op = rz_serialize_anal_switch_op_load (child); + break; + case BLOCK_FIELD_NINSTR: + if (child->type != RZ_JSON_INTEGER) { + break; + } + proto.ninstr = (int)child->num.s_value; + break; + case BLOCK_FIELD_OP_POS: { + if (child->type != RZ_JSON_ARRAY) { + break; + } + if (proto.op_pos) { + free (proto.op_pos); + proto.op_pos = NULL; + } + proto.op_pos = calloc (child->children.count, sizeof (ut16)); + proto.op_pos_size = 0; + RJson *baby; + for (baby = child->children.first; baby; baby = baby->next) { + if (baby->type != RZ_JSON_INTEGER) { + free (proto.op_pos); + proto.op_pos = NULL; + proto.op_pos_size = 0; + break; + } + proto.op_pos[proto.op_pos_size++] = (ut16)baby->num.u_value; + } + break; + } + case BLOCK_FIELD_STACKPTR: + if (child->type != RZ_JSON_INTEGER) { + break; + } + proto.stackptr = (int)child->num.s_value; + break; + case BLOCK_FIELD_PARENT_STACKPTR: + if (child->type != RZ_JSON_INTEGER) { + break; + } + proto.parent_stackptr = (int)child->num.s_value; + break; + case BLOCK_FIELD_CMPVAL: + if (child->type != RZ_JSON_INTEGER) { + break; + } + proto.cmpval = child->num.u_value; + break; + case BLOCK_FIELD_CMPREG: + if (child->type != RZ_JSON_STRING) { + break; + } + proto.cmpreg = rz_str_constpool_get (&ctx->anal->constpool, child->str_value); + break; + default: + break; + }) + rz_json_free (json); + free (json_str); + + errno = 0; + ut64 addr = strtoull (k, NULL, 0); + if (errno || proto.size == UT64_MAX + || (fingerprint_size != SIZE_MAX && fingerprint_size != proto.size) + || (proto.op_pos && proto.op_pos_size != proto.ninstr - 1)) { // op_pos_size > ninstr - 1 is legal but we require the format to be like this. + goto error; + } + + RzAnalBlock *block = rz_anal_create_block (ctx->anal, addr, proto.size); + if (!block) { + goto error; + } + block->jump = proto.jump; + block->fail = proto.fail; + block->traced = proto.traced; + block->folded = proto.folded; + block->colorize = proto.colorize; + block->fingerprint = proto.fingerprint; + block->diff = proto.diff; + block->switch_op = proto.switch_op; + block->ninstr = proto.ninstr; + if (proto.op_pos) { + free (block->op_pos); + block->op_pos = proto.op_pos; + block->op_pos_size = proto.op_pos_size; + } + block->stackptr = proto.stackptr; + block->parent_stackptr = proto.parent_stackptr; + block->cmpval = proto.cmpval; + block->cmpreg = proto.cmpreg; + + return true; +error: + free (proto.fingerprint); + rz_anal_diff_free (proto.diff); + rz_anal_switch_op_free (proto.switch_op); + free (proto.op_pos); + return false; +} + +RZ_API bool rz_serialize_anal_blocks_load(RZ_NONNULL Sdb *db, RZ_NONNULL RzAnal *anal, RSerializeAnalDiffParser diff_parser, RZ_NULLABLE RzSerializeResultInfo *res) { + BlockLoadCtx ctx = { anal, key_parser_new (), diff_parser }; + if (!ctx.parser) { + SERIALIZE_ERR ("parser init failed"); + return false; + } + key_parser_add (ctx.parser, "size", BLOCK_FIELD_SIZE); + key_parser_add (ctx.parser, "jump", BLOCK_FIELD_JUMP); + key_parser_add (ctx.parser, "fail", BLOCK_FIELD_FAIL); + key_parser_add (ctx.parser, "traced", BLOCK_FIELD_TRACED); + key_parser_add (ctx.parser, "folded", BLOCK_FIELD_FOLDED); + key_parser_add (ctx.parser, "colorize", BLOCK_FIELD_COLORIZE); + key_parser_add (ctx.parser, "fingerprint", BLOCK_FIELD_FINGERPRINT); + key_parser_add (ctx.parser, "diff", BLOCK_FIELD_DIFF); + key_parser_add (ctx.parser, "switch_op", BLOCK_FIELD_SWITCH_OP); + key_parser_add (ctx.parser, "ninstr", BLOCK_FIELD_NINSTR); + key_parser_add (ctx.parser, "op_pos", BLOCK_FIELD_OP_POS); + key_parser_add (ctx.parser, "stackptr", BLOCK_FIELD_STACKPTR); + key_parser_add (ctx.parser, "parent_stackptr", BLOCK_FIELD_PARENT_STACKPTR); + key_parser_add (ctx.parser, "cmpval", BLOCK_FIELD_CMPVAL); + key_parser_add (ctx.parser, "cmpreg", BLOCK_FIELD_CMPREG); + bool ret = sdb_foreach (db, block_load_cb, &ctx); + key_parser_free (ctx.parser); + if (!ret) { + SERIALIZE_ERR ("basic blocks parsing failed"); + } + return ret; +} + +RZ_API void rz_serialize_anal_var_save(RZ_NONNULL PJ *j, RZ_NONNULL RzAnalVar *var) { + pj_o (j); + pj_ks (j, "name", var->name); + pj_ks (j, "type", var->type); + switch (var->kind) { + case RZ_ANAL_VAR_KIND_REG: + pj_ks (j, "kind", "r"); + break; + case RZ_ANAL_VAR_KIND_SPV: + pj_ks (j, "kind", "s"); + break; + case RZ_ANAL_VAR_KIND_BPV: + pj_ks (j, "kind", "b"); + break; + } + if (var->kind != RZ_ANAL_VAR_KIND_REG) { + pj_kN (j, "delta", var->delta); + } + if (var->regname) { + pj_ks (j, "reg", var->regname); + } + if (var->isarg) { + pj_kb (j, "arg", true); + } + if (var->comment) { + pj_ks (j, "cmt", var->comment); + } + if (!rz_vector_empty (&var->accesses)) { + pj_ka (j, "accs"); + RzAnalVarAccess *acc; + rz_vector_foreach (&var->accesses, acc) { + pj_o (j); + pj_kn (j, "off", acc->offset); + switch (acc->type) { + case RZ_ANAL_VAR_ACCESS_TYPE_READ: + pj_ks (j, "type", "r"); + break; + case RZ_ANAL_VAR_ACCESS_TYPE_WRITE: + pj_ks (j, "type", "w"); + break; + case RZ_ANAL_VAR_ACCESS_TYPE_READ | RZ_ANAL_VAR_ACCESS_TYPE_WRITE: + pj_ks (j, "type", "rw"); + break; + } + if (acc->stackptr) { + pj_kn (j, "sp", acc->stackptr); + } + if (acc->reg) { + pj_ks (j, "reg", acc->reg); + } else { + rz_warn_if_reached (); + } + pj_end (j); + } + pj_end (j); + } + if (!rz_vector_empty (&var->constraints)) { + pj_ka (j, "constrs"); + RzAnalVarConstraint *constr; + rz_vector_foreach (&var->constraints, constr) { + pj_i (j, (int)constr->cond); + pj_n (j, constr->val); + } + pj_end (j); + } + pj_end (j); +} + +enum { + VAR_FIELD_NAME, + VAR_FIELD_TYPE, + VAR_FIELD_KIND, + VAR_FIELD_ARG, + VAR_FIELD_DELTA, + VAR_FIELD_REG, + VAR_FIELD_COMMENT, + VAR_FIELD_ACCS, + VAR_FIELD_CONSTRS +}; + +RZ_API RSerializeAnalVarParser rz_serialize_anal_var_parser_new(void) { + RSerializeAnalDiffParser parser = key_parser_new (); + if (!parser) { + return NULL; + } + key_parser_add (parser, "name", VAR_FIELD_NAME); + key_parser_add (parser, "type", VAR_FIELD_TYPE); + key_parser_add (parser, "kind", VAR_FIELD_KIND); + key_parser_add (parser, "arg", VAR_FIELD_ARG); + key_parser_add (parser, "delta", VAR_FIELD_DELTA); + key_parser_add (parser, "reg", VAR_FIELD_REG); + key_parser_add (parser, "cmt", VAR_FIELD_COMMENT); + key_parser_add (parser, "accs", VAR_FIELD_ACCS); + key_parser_add (parser, "constrs", VAR_FIELD_CONSTRS); + return parser; +} + +RZ_API void rz_serialize_anal_var_parser_free(RSerializeAnalVarParser parser) { + key_parser_free (parser); +} + +RZ_API RZ_NULLABLE RzAnalVar *rz_serialize_anal_var_load(RZ_NONNULL RzAnalFunction *fcn, RZ_NONNULL RSerializeAnalVarParser parser, RZ_NONNULL const RJson *json) { + if (json->type != RZ_JSON_OBJECT) { + return NULL; + } + const char *name = NULL; + const char *type = NULL; + RzAnalVarKind kind = -1; + bool arg = false; + st64 delta = ST64_MAX; + const char *regname = NULL; + const char *comment = NULL; + RzVector accesses; + rz_vector_init (&accesses, sizeof (RzAnalVarAccess), NULL, NULL); + RzVector constraints; + rz_vector_init (&constraints, sizeof (RzAnalVarConstraint), NULL, NULL); + + RzAnalVar *ret = NULL; + + KEY_PARSER_JSON (parser, json, child, { + case VAR_FIELD_NAME: + if (child->type != RZ_JSON_STRING) { + break; + } + name = child->str_value; + break; + case VAR_FIELD_TYPE: + if (child->type != RZ_JSON_STRING) { + break; + } + type = child->str_value; + break; + case VAR_FIELD_KIND: + if (child->type != RZ_JSON_STRING || !*child->str_value || child->str_value[1]) { + // must be a string of exactly 1 char + break; + } + switch (*child->str_value) { + case 'r': + kind = RZ_ANAL_VAR_KIND_REG; + break; + case 's': + kind = RZ_ANAL_VAR_KIND_SPV; + break; + case 'b': + kind = RZ_ANAL_VAR_KIND_BPV; + break; + default: + break; + } + break; + case VAR_FIELD_ARG: + if (child->type != RZ_JSON_BOOLEAN) { + break; + } + arg = child->num.u_value ? true : false; + break; + case VAR_FIELD_DELTA: + if (child->type != RZ_JSON_INTEGER) { + eprintf ("delta nop\n"); + break; + } + delta = child->num.s_value; + break; + case VAR_FIELD_REG: + if (child->type != RZ_JSON_STRING) { + break; + } + regname = child->str_value; + break; + case VAR_FIELD_COMMENT: + if (child->type != RZ_JSON_STRING) { + break; + } + comment = child->str_value; + break; + case VAR_FIELD_ACCS: { + if (child->type != RZ_JSON_ARRAY) { + break; + } + RJson *baby; + for (baby = child->children.first; baby; baby = baby->next) { + if (baby->type != RZ_JSON_OBJECT) { + continue; + } + // {off:, type:"r|w|rw", sp?:} + const RJson *offv = rz_json_get (baby, "off"); + if (!offv || offv->type != RZ_JSON_INTEGER) { + continue; + } + const RJson *typev = rz_json_get (baby, "type"); + if (!typev || typev->type != RZ_JSON_STRING) { + continue; + } + const char *acctype_str = typev->str_value; + const RJson *spv = rz_json_get (baby, "sp"); + if (spv && spv->type != RZ_JSON_INTEGER) { + continue; + } + const RJson *regv = rz_json_get (baby, "reg"); + if (!regv || regv->type != RZ_JSON_STRING) { + continue; + } + + ut64 acctype; + // parse "r", "w" or "rw" and reject everything else + if (acctype_str[0] == 'r') { + if (acctype_str[1] == 'w') { + acctype = RZ_ANAL_VAR_ACCESS_TYPE_READ | RZ_ANAL_VAR_ACCESS_TYPE_WRITE; + } else if (!acctype_str[1]) { + acctype = RZ_ANAL_VAR_ACCESS_TYPE_READ; + } else { + continue; + } + } else if (acctype_str[0] == 'w' && !acctype_str[1]) { + acctype = RZ_ANAL_VAR_ACCESS_TYPE_WRITE; + } else { + continue; + } + + RzAnalVarAccess *acc = rz_vector_push (&accesses, NULL); + acc->offset = offv->num.s_value; + acc->type = acctype; + acc->stackptr = spv ? spv->num.s_value : 0; + acc->reg = regv->str_value; + } + break; + } + case VAR_FIELD_CONSTRS: { + if (child->type != RZ_JSON_ARRAY) { + break; + } + RJson *baby; + for (baby = child->children.first; baby; baby = baby->next) { + if (baby->type != RZ_JSON_INTEGER) { + break; + } + RJson *sibling = baby->next; + if (!sibling || sibling->type != RZ_JSON_INTEGER) { + break; + } + RzAnalVarConstraint constr; + constr.cond = (_RzAnalCond)baby->num.s_value; + constr.val = sibling->num.u_value; + if (constr.cond < RZ_ANAL_COND_AL || constr.cond > RZ_ANAL_COND_LS) { + baby = sibling; + continue; + } + rz_vector_push (&constraints, &constr); + baby = sibling; + } + break; + } + default: + break; + }) + + if (kind == RZ_ANAL_VAR_KIND_REG) { + if (!regname) { + goto beach; + } + RzRegItem *reg = rz_reg_get (fcn->anal->reg, regname, -1); + if (!reg) { + goto beach; + } + delta = reg->index; + } + if (!name || !type || kind == -1 || delta == ST64_MAX) { + goto beach; + } + ret = rz_anal_function_set_var (fcn, delta, kind, type, 0, arg, name); + if (!ret) { + goto beach; + } + if (comment) { + free (ret->comment); + ret->comment = strdup (comment); + } + RzAnalVarAccess *acc; + rz_vector_foreach (&accesses, acc) { + rz_anal_var_set_access (ret, acc->reg, fcn->addr + acc->offset, acc->type, acc->stackptr); + } + RzAnalVarConstraint *constr; + rz_vector_foreach (&constraints, constr) { + rz_anal_var_add_constraint (ret, constr); + } + +beach: + rz_vector_fini (&accesses); + rz_vector_fini (&constraints); + return ret; +} + +static bool store_label_cb(void *j, const ut64 k, const void *v) { + pj_kn (j, v, k); + return true; +} + +static void function_store(RZ_NONNULL Sdb *db, const char *key, RzAnalFunction *function) { + PJ *j = pj_new (); + if (!j) { + return; + } + pj_o (j); + + pj_ks (j, "name", function->name); + if (function->bits) { + pj_ki (j, "bits", function->bits); + } + pj_ki (j, "type", function->type); + if (function->cc) { + pj_ks (j, "cc", function->cc); + } + pj_ki (j, "stack", function->stack); + pj_ki (j, "maxstack", function->maxstack); + pj_ki (j, "ninstr", function->ninstr); + if (function->folded) { + pj_kb (j, "folded", true); + } + if (function->bp_frame) { + pj_kb (j, "bp_frame", true); + } + if (function->bp_off) { + pj_kN (j, "bp_off", function->bp_off); + } + if (function->is_pure) { + pj_kb (j, "pure", true); + } + if (function->is_noreturn) { + pj_kb (j, "noreturn", true); + } + if (function->fingerprint) { + char *b64 = rz_base64_encode_dyn ((const char *)function->fingerprint, function->fingerprint_size); + if (b64) { + pj_ks (j, "fingerprint", b64); + free (b64); + } + } + if (function->diff) { + pj_k (j, "diff"); + rz_serialize_anal_diff_save (j, function->diff); + } + + pj_ka (j, "bbs"); + RzListIter *it; + RzAnalBlock *block; + rz_list_foreach (function->bbs, it, block) { + pj_n (j, block->addr); + } + pj_end (j); + + if (!rz_list_empty (function->imports)) { + pj_ka (j, "imports"); + const char *import; + rz_list_foreach (function->imports, it, import) { + pj_s (j, import); + } + pj_end (j); + } + + if (!rz_pvector_empty (&function->vars)) { + pj_ka (j, "vars"); + void **vit; + rz_pvector_foreach (&function->vars, vit) { + RzAnalVar *var = *vit; + rz_serialize_anal_var_save (j, var); + } + pj_end (j); + } + + if (function->labels->count) { + pj_ko (j, "labels"); + ht_up_foreach (function->labels, store_label_cb, j); + pj_end (j); + } + + pj_end (j); + sdb_set (db, key, pj_string (j), 0); + pj_free (j); +} + +RZ_API void rz_serialize_anal_functions_save(RZ_NONNULL Sdb *db, RZ_NONNULL RzAnal *anal) { + RzListIter *it; + RzAnalFunction *function; + RzStrBuf key; + rz_strbuf_init (&key); + rz_list_foreach (anal->fcns, it, function) { + rz_strbuf_setf (&key, "0x%"PFMT64x, function->addr); + function_store (db, rz_strbuf_get (&key), function); + } + rz_strbuf_fini (&key); +} + +enum { + FUNCTION_FIELD_NAME, + FUNCTION_FIELD_BITS, + FUNCTION_FIELD_TYPE, + FUNCTION_FIELD_CC, + FUNCTION_FIELD_STACK, + FUNCTION_FIELD_MAXSTACK, + FUNCTION_FIELD_NINSTR, + FUNCTION_FIELD_FOLDED, + FUNCTION_FIELD_PURE, + FUNCTION_FIELD_BP_FRAME, + FUNCTION_FIELD_BP_OFF, + FUNCTION_FIELD_NORETURN, + FUNCTION_FIELD_FINGERPRINT, + FUNCTION_FIELD_DIFF, + FUNCTION_FIELD_BBS, + FUNCTION_FIELD_IMPORTS, + FUNCTION_FIELD_VARS, + FUNCTION_FIELD_LABELS +}; + +typedef struct { + RzAnal *anal; + KeyParser *parser; + RSerializeAnalDiffParser diff_parser; + RSerializeAnalVarParser var_parser; +} FunctionLoadCtx; + +static bool function_load_cb(void *user, const char *k, const char *v) { + FunctionLoadCtx *ctx = user; + + char *json_str = strdup (v); + if (!json_str) { + return true; + } + RJson *json = rz_json_parse (json_str); + if (!json || json->type != RZ_JSON_OBJECT) { + free (json_str); + return false; + } + + RzAnalFunction *function = rz_anal_function_new (ctx->anal); + function->bits = 0; // should be 0 if not specified + function->bp_frame = false; // should be false if not specified + function->bp_off = 0; // 0 if not specified + bool noreturn = false; + RJson *vars_json = NULL; + KEY_PARSER_JSON (ctx->parser, json, child, { + case FUNCTION_FIELD_NAME: + if (child->type != RZ_JSON_STRING) { + break; + } + if (function->name) { + free (function->name); + } + function->name = strdup (child->str_value); + break; + case FUNCTION_FIELD_BITS: + if (child->type != RZ_JSON_INTEGER) { + break; + } + function->bits = (int)child->num.s_value; + break; + case FUNCTION_FIELD_TYPE: + if (child->type != RZ_JSON_INTEGER) { + break; + } + function->type = (int)child->num.s_value; + break; + case FUNCTION_FIELD_CC: + if (child->type != RZ_JSON_STRING) { + break; + } + function->cc = rz_str_constpool_get (&ctx->anal->constpool, child->str_value); + break; + case FUNCTION_FIELD_STACK: + if (child->type != RZ_JSON_INTEGER) { + break; + } + function->stack = (int)child->num.s_value; + break; + case FUNCTION_FIELD_MAXSTACK: + if (child->type != RZ_JSON_INTEGER) { + break; + } + function->maxstack = (int)child->num.s_value; + break; + case FUNCTION_FIELD_NINSTR: + if (child->type != RZ_JSON_INTEGER) { + break; + } + function->ninstr = (int)child->num.s_value; + break; + case FUNCTION_FIELD_FOLDED: + if (child->type != RZ_JSON_BOOLEAN) { + break; + } + function->folded = child->num.u_value ? true : false; + break; + case FUNCTION_FIELD_PURE: + if (child->type != RZ_JSON_BOOLEAN) { + break; + } + function->is_pure = child->num.u_value ? true : false; + break; + case FUNCTION_FIELD_BP_FRAME: + if (child->type != RZ_JSON_BOOLEAN) { + break; + } + function->bp_frame = child->num.u_value ? true : false; + break; + case FUNCTION_FIELD_BP_OFF: + if (child->type != RZ_JSON_INTEGER) { + break; + } + function->bp_off = child->num.s_value; + break; + case FUNCTION_FIELD_NORETURN: + if (child->type != RZ_JSON_BOOLEAN) { + break; + } + noreturn = child->num.u_value ? true : false; + break; + case FUNCTION_FIELD_FINGERPRINT: + if (child->type != RZ_JSON_STRING) { + break; + } + if (function->fingerprint) { + free (function->fingerprint); + function->fingerprint = NULL; + } + function->fingerprint_size = strlen (child->str_value); + if (!function->fingerprint_size) { + break; + } + function->fingerprint = malloc (function->fingerprint_size); + if (!function->fingerprint) { + function->fingerprint_size = 0; + break; + } + int decsz = rz_base64_decode (function->fingerprint, child->str_value, function->fingerprint_size); + if (decsz <= 0) { + free (function->fingerprint); + function->fingerprint = NULL; + function->fingerprint_size = 0; + } else if (decsz < function->fingerprint_size) { + ut8 *n = realloc (function->fingerprint, (size_t)decsz); + if (!n) { + free (function->fingerprint); + function->fingerprint = NULL; + function->fingerprint_size = 0; + } + function->fingerprint = n; + function->fingerprint_size = (size_t)decsz; + } + break; + case FUNCTION_FIELD_DIFF: + rz_anal_diff_free (function->diff); + function->diff = rz_serialize_anal_diff_load (ctx->diff_parser, child); + break; + case FUNCTION_FIELD_BBS: { + if (child->type != RZ_JSON_ARRAY) { + break; + } + RJson *baby; + for (baby = child->children.first; baby; baby = baby->next) { + if (baby->type != RZ_JSON_INTEGER) { + continue; + } + RzAnalBlock *block = rz_anal_get_block_at (ctx->anal, baby->num.u_value); + if (!block) { + continue; + } + rz_anal_function_add_block (function, block); + } + break; + } + case FUNCTION_FIELD_IMPORTS: { + if (child->type != RZ_JSON_ARRAY) { + break; + } + RJson *baby; + for (baby = child->children.first; baby; baby = baby->next) { + if (baby->type != RZ_JSON_STRING) { + continue; + } + char *import = strdup (baby->str_value); + if (!import) { + break; + } + if (!function->imports) { + function->imports = rz_list_newf ((RzListFree)free); + if (!function->imports) { + free (import); + break; + } + } + rz_list_push (function->imports, import); + } + break; + } + case FUNCTION_FIELD_VARS: { + if (child->type != RZ_JSON_ARRAY) { + break; + } + vars_json = child; + break; + } + case FUNCTION_FIELD_LABELS: { + if (child->type != RZ_JSON_OBJECT) { + break; + } + RJson *baby; + for (baby = child->children.first; baby; baby = baby->next) { + if (baby->type != RZ_JSON_INTEGER) { + continue; + } + rz_anal_function_set_label (function, baby->key, baby->num.u_value); + } + break; + } + default: + break; + }) + + bool ret = true; + errno = 0; + function->addr = strtoull (k, NULL, 0); + if (errno || !function->name || !rz_anal_add_function (ctx->anal, function)) { + rz_anal_function_free (function); + ret = false; + goto beach; + } + function->is_noreturn = noreturn; // Can't set directly, rz_anal_add_function() overwrites it + + if (vars_json) { + RJson *baby; + for (baby = vars_json->children.first; baby; baby = baby->next) { + rz_serialize_anal_var_load (function, ctx->var_parser, baby); + } + } + +beach: + rz_json_free (json); + free (json_str); + return ret; +} + +RZ_API bool rz_serialize_anal_functions_load(RZ_NONNULL Sdb *db, RZ_NONNULL RzAnal *anal, RSerializeAnalDiffParser diff_parser, RZ_NULLABLE RzSerializeResultInfo *res) { + FunctionLoadCtx ctx = { + .anal = anal, + .parser = key_parser_new (), + .diff_parser = diff_parser, + .var_parser = rz_serialize_anal_var_parser_new () + }; + bool ret; + if (!ctx.parser || !ctx.var_parser) { + SERIALIZE_ERR ("parser init failed"); + ret = false; + goto beach; + } + key_parser_add (ctx.parser, "name", FUNCTION_FIELD_NAME); + key_parser_add (ctx.parser, "bits", FUNCTION_FIELD_BITS); + key_parser_add (ctx.parser, "type", FUNCTION_FIELD_TYPE); + key_parser_add (ctx.parser, "cc", FUNCTION_FIELD_CC); + key_parser_add (ctx.parser, "stack", FUNCTION_FIELD_STACK); + key_parser_add (ctx.parser, "maxstack", FUNCTION_FIELD_MAXSTACK); + key_parser_add (ctx.parser, "ninstr", FUNCTION_FIELD_NINSTR); + key_parser_add (ctx.parser, "folded", FUNCTION_FIELD_FOLDED); + key_parser_add (ctx.parser, "pure", FUNCTION_FIELD_PURE); + key_parser_add (ctx.parser, "bp_frame", FUNCTION_FIELD_BP_FRAME); + key_parser_add (ctx.parser, "bp_off", FUNCTION_FIELD_BP_OFF); + key_parser_add (ctx.parser, "noreturn", FUNCTION_FIELD_NORETURN); + key_parser_add (ctx.parser, "fingerprint", FUNCTION_FIELD_FINGERPRINT); + key_parser_add (ctx.parser, "diff", FUNCTION_FIELD_DIFF); + key_parser_add (ctx.parser, "bbs", FUNCTION_FIELD_BBS); + key_parser_add (ctx.parser, "imports", FUNCTION_FIELD_IMPORTS); + key_parser_add (ctx.parser, "vars", FUNCTION_FIELD_VARS); + key_parser_add (ctx.parser, "labels", FUNCTION_FIELD_LABELS); + ret = sdb_foreach (db, function_load_cb, &ctx); + if (!ret) { + SERIALIZE_ERR ("functions parsing failed"); + } +beach: + key_parser_free (ctx.parser); + rz_serialize_anal_var_parser_free (ctx.var_parser); + return ret; +} + +static bool store_xref_cb(void *j, const ut64 k, const void *v) { + const RzAnalRef *xref = v; + pj_o (j); + pj_kn (j, "to", k); + if (xref->type != RZ_ANAL_REF_TYPE_NULL) { + char type[2] = { xref->type, '\0' }; + pj_ks (j, "type", type); + } + pj_end (j); + return true; +} + +static bool store_xrefs_list_cb(void *db, const ut64 k, const void *v) { + char key[0x20]; + if (snprintf (key, sizeof (key), "0x%"PFMT64x, k) < 0) { + return false; + } + PJ *j = pj_new (); + if (!j) { + return false; + } + pj_a (j); + HtUP *ht = (HtUP *)v; + ht_up_foreach (ht, store_xref_cb, j); + pj_end (j); + sdb_set (db, key, pj_string (j), 0); + pj_free (j); + return true; +} + +RZ_API void rz_serialize_anal_xrefs_save(RZ_NONNULL Sdb *db, RZ_NONNULL RzAnal *anal) { + ht_up_foreach (anal->dict_refs, store_xrefs_list_cb, db); +} + +static bool xrefs_load_cb(void *user, const char *k, const char *v) { + RzAnal *anal = user; + + errno = 0; + ut64 from = strtoull (k, NULL, 0); + if (errno) { + return false; + } + + char *json_str = strdup (v); + if (!json_str) { + return true; + } + RJson *json = rz_json_parse (json_str); + if (!json || json->type != RZ_JSON_ARRAY) { + free (json_str); + return false; + } + + const RJson *child; + for (child = json->children.first; child; child = child->next) { + if (child->type != RZ_JSON_OBJECT) { + goto error; + } + const RJson *baby = rz_json_get (child, "to"); + if (!baby || baby->type != RZ_JSON_INTEGER) { + goto error; + } + ut64 to = baby->num.u_value; + + RzAnalRefType type = RZ_ANAL_REF_TYPE_NULL; + baby = rz_json_get (child, "type"); + if (baby) { + // must be a 1-char string + if (baby->type != RZ_JSON_STRING || !baby->str_value[0] || baby->str_value[1]) { + goto error; + } + switch (baby->str_value[0]) { + case RZ_ANAL_REF_TYPE_CODE: + case RZ_ANAL_REF_TYPE_CALL: + case RZ_ANAL_REF_TYPE_DATA: + case RZ_ANAL_REF_TYPE_STRING: + type = baby->str_value[0]; + break; + default: + goto error; + } + } + + rz_anal_xrefs_set (anal, from, to, type); + } + + rz_json_free (json); + free (json_str); + + return true; +error: + rz_json_free (json); + free (json_str); + return false; +} + +RZ_API bool rz_serialize_anal_xrefs_load(RZ_NONNULL Sdb *db, RZ_NONNULL RzAnal *anal, RZ_NULLABLE RzSerializeResultInfo *res) { + bool ret = sdb_foreach (db, xrefs_load_cb, anal); + if (!ret) { + SERIALIZE_ERR ("xrefs parsing failed"); + } + return ret; +} + +RZ_API void rz_serialize_anal_meta_save(RZ_NONNULL Sdb *db, RZ_NONNULL RzAnal *anal) { + rz_serialize_spaces_save (sdb_ns (db, "spaces", true), &anal->meta_spaces); + + PJ *j = pj_new (); + if (!j) { + return; + } + char key[0x20]; + RIntervalTreeIter it; + RzAnalMetaItem *meta; + ut64 addr = 0; + size_t count = 0; +#define FLUSH pj_end (j); \ + if (snprintf (key, sizeof (key), "0x%"PFMT64x, addr) >= 0) { \ + sdb_set (db, key, pj_string (j), 0); \ + } + rz_interval_tree_foreach (&anal->meta, it, meta) { + RIntervalNode *node = rz_interval_tree_iter_get (&it); + if (count && node->start != addr) { + // new address + FLUSH + pj_reset (j); + pj_a (j); + count = 0; + } else if (!count) { + // first address + pj_a (j); + } + count++; + addr = node->start; + pj_o (j); + ut64 size = rz_meta_node_size (node); + if (size != 1) { + pj_kn (j, "size", size); + } + char type_str[2] = { 0 }; + switch (meta->type) { + case RZ_META_TYPE_DATA: + type_str[0] = 'd'; + break; + case RZ_META_TYPE_CODE: + type_str[0] = 'c'; + break; + case RZ_META_TYPE_STRING: + type_str[0] = 's'; + break; + case RZ_META_TYPE_FORMAT: + type_str[0] = 'f'; + break; + case RZ_META_TYPE_MAGIC: + type_str[0] = 'm'; + break; + case RZ_META_TYPE_HIDE: + type_str[0] = 'h'; + break; + case RZ_META_TYPE_COMMENT: + type_str[0] = 'C'; + break; + case RZ_META_TYPE_RUN: + type_str[0] = 'r'; + break; + case RZ_META_TYPE_HIGHLIGHT: + type_str[0] = 'H'; + break; + case RZ_META_TYPE_VARTYPE: + type_str[0] = 't'; + break; + default: + break; + } + pj_ks (j, "type", type_str); + if (meta->subtype) { + pj_ki (j, "subtype", meta->subtype); + } + if (meta->str) { + pj_ks (j, "str", meta->str); + } + if (meta->space) { + pj_ks (j, "space", meta->space->name); + } + pj_end (j); + } + if (count) { + FLUSH + } +#undef FLUSH + pj_free (j); +} + +static bool meta_load_cb(void *user, const char *k, const char *v) { + RzAnal *anal = user; + + errno = 0; + ut64 addr = strtoull (k, NULL, 0); + if (errno) { + return false; + } + + char *json_str = strdup (v); + if (!json_str) { + return true; + } + RJson *json = rz_json_parse (json_str); + if (!json || json->type != RZ_JSON_ARRAY) { + free (json_str); + return false; + } + + const RJson *child; + for (child = json->children.first; child; child = child->next) { + if (child->type != RZ_JSON_OBJECT) { + goto error; + } + + ut64 size = 1; + RzAnalMetaType type = RZ_META_TYPE_ANY; + const char *str = NULL; + int subtype = 0; + const char *space_name = NULL; + + const RJson *baby; + for (baby = child->children.first; baby; baby = baby->next) { + if (!strcmp (baby->key, "size")) { + if (baby->type == RZ_JSON_INTEGER) { + size = baby->num.u_value; + } + continue; + } + if (!strcmp (baby->key, "type")) { + // only single-char strings accepted + if (baby->type == RZ_JSON_STRING && baby->str_value[0] && !baby->str_value[1]) { + switch (baby->str_value[0]) { + case 'd': + type = RZ_META_TYPE_DATA; + break; + case 'c': + type = RZ_META_TYPE_CODE; + break; + case 's': + type = RZ_META_TYPE_STRING; + break; + case 'f': + type = RZ_META_TYPE_FORMAT; + break; + case 'm': + type = RZ_META_TYPE_MAGIC; + break; + case 'h': + type = RZ_META_TYPE_HIDE; + break; + case 'C': + type = RZ_META_TYPE_COMMENT; + break; + case 'r': + type = RZ_META_TYPE_RUN; + break; + case 'H': + type = RZ_META_TYPE_HIGHLIGHT; + break; + case 't': + type = RZ_META_TYPE_VARTYPE; + break; + default: + break; + } + } + continue; + } + if (!strcmp (baby->key, "str")) { + if (baby->type == RZ_JSON_STRING) { + str = baby->str_value; + } + continue; + } + if (!strcmp (baby->key, "subtype")) { + if (baby->type == RZ_JSON_INTEGER) { + subtype = (int)baby->num.s_value; + } + continue; + } + if (!strcmp (baby->key, "space")) { + if (baby->type == RZ_JSON_STRING) { + space_name = baby->str_value; + } + continue; + } + } + + if (type == RZ_META_TYPE_ANY || (type == RZ_META_TYPE_COMMENT && !str)) { + continue; + } + + RzAnalMetaItem *item = RZ_NEW0 (RzAnalMetaItem); + if (!item) { + break; + } + item->type = type; + item->subtype = subtype; + item->space = space_name ? rz_spaces_get (&anal->meta_spaces, space_name) : NULL; + item->str = str ? strdup (str) : NULL; + if (str && !item->str) { + free (item); + continue; + } + ut64 end = addr + size - 1; + if (end < addr) { + end = UT64_MAX; + } + rz_interval_tree_insert (&anal->meta, addr, end, item); + } + + rz_json_free (json); + free (json_str); + + return true; +error: + rz_json_free (json); + free (json_str); + return false; +} + +RZ_API bool rz_serialize_anal_meta_load(RZ_NONNULL Sdb *db, RZ_NONNULL RzAnal *anal, RZ_NULLABLE RzSerializeResultInfo *res) { + Sdb *spaces_db = sdb_ns (db, "spaces", false); + if (!spaces_db) { + SERIALIZE_ERR ("missing meta spaces namespace"); + return false; + } + if (!rz_serialize_spaces_load (spaces_db, &anal->meta_spaces, false, res)) { + return false; + } + bool ret = sdb_foreach (db, meta_load_cb, anal); + if (!ret) { + SERIALIZE_ERR ("meta parsing failed"); + } + return ret; +} + +typedef struct { + const RzVector/**/ *addr_hints; + const char *arch; + int bits; + bool arch_set; + bool bits_set; +} HintsAtAddr; + +static void hints_at_addr_kv_free(HtUPKv *kv) { + free (kv->value); +} + +static HintsAtAddr *hints_at_addr(HtUP *acc, ut64 addr) { + HintsAtAddr *h = ht_up_find (acc, addr, NULL); + if (h) { + return h; + } + h = RZ_NEW0 (HintsAtAddr); + if (!h) { + return NULL; + } + ht_up_insert (acc, addr, h); + return h; +} + +static bool addr_hint_acc_cb(ut64 addr, const RzVector/**/ *records, void *user) { + HintsAtAddr *h = hints_at_addr (user, addr); + if (!h) { + return false; + } + h->addr_hints = records; + return true; +} + +static bool arch_hint_acc_cb(ut64 addr, RZ_NULLABLE const char *arch, void *user) { + HintsAtAddr *h = hints_at_addr (user, addr); + if (!h) { + return false; + } + h->arch = arch; + h->arch_set = true; + return true; +} + +static bool bits_hint_acc_cb(ut64 addr, int bits, void *user) { + HintsAtAddr *h = hints_at_addr (user, addr); + if (!h) { + return false; + } + h->bits = bits; + h->bits_set = true; + return true; +} + +static bool hints_acc_store_cb(void *user, const ut64 addr, const void *v) { + const HintsAtAddr *h = v; + char key[0x20]; + if (snprintf (key, sizeof (key), "0x%"PFMT64x, addr) < 0) { + return false; + } + Sdb *db = user; + PJ *j = pj_new (); + if (!j) { + return false; + } + pj_o (j); + if (h->arch_set) { + pj_k (j, "arch"); + if (h->arch) { + pj_s (j, h->arch); + } else { + pj_null (j); + } + } + if (h->bits_set) { + pj_ki (j, "bits", h->bits); + } + if (h->addr_hints) { + RzAnalAddrHintRecord *record; + rz_vector_foreach (h->addr_hints, record) { + switch (record->type) { + case RZ_ANAL_ADDR_HINT_TYPE_IMMBASE: + pj_ki (j, "immbase", record->immbase); + break; + case RZ_ANAL_ADDR_HINT_TYPE_JUMP: + pj_kn (j, "jump", record->jump); + break; + case RZ_ANAL_ADDR_HINT_TYPE_FAIL: + pj_kn (j, "fail", record->fail); + break; + case RZ_ANAL_ADDR_HINT_TYPE_STACKFRAME: + pj_kn (j, "frame", record->stackframe); + break; + case RZ_ANAL_ADDR_HINT_TYPE_PTR: + pj_kn (j, "ptr", record->ptr); + break; + case RZ_ANAL_ADDR_HINT_TYPE_NWORD: + pj_ki (j, "nword", record->nword); + break; + case RZ_ANAL_ADDR_HINT_TYPE_RET: + pj_kn (j, "ret", record->retval); + break; + case RZ_ANAL_ADDR_HINT_TYPE_NEW_BITS: + pj_ki (j, "newbits", record->newbits); + break; + case RZ_ANAL_ADDR_HINT_TYPE_SIZE: + pj_kn (j, "size", record->size); + break; + case RZ_ANAL_ADDR_HINT_TYPE_SYNTAX: + pj_ks (j, "syntax", record->syntax); + break; + case RZ_ANAL_ADDR_HINT_TYPE_OPTYPE: + pj_ki (j, "optype", record->optype); + break; + case RZ_ANAL_ADDR_HINT_TYPE_OPCODE: + pj_ks (j, "opcode", record->opcode); + break; + case RZ_ANAL_ADDR_HINT_TYPE_TYPE_OFFSET: + pj_ks (j, "toff", record->type_offset); + break; + case RZ_ANAL_ADDR_HINT_TYPE_ESIL: + pj_ks (j, "esil", record->esil); + break; + case RZ_ANAL_ADDR_HINT_TYPE_HIGH: + pj_kb (j, "high", true); + break; + case RZ_ANAL_ADDR_HINT_TYPE_VAL: + pj_kn (j, "val", record->val); + break; + } + } + } + pj_end (j); + sdb_set (db, key, pj_string (j), 0); + pj_free (j); + return true; +} + +RZ_API void rz_serialize_anal_hints_save(RZ_NONNULL Sdb *db, RZ_NONNULL RzAnal *anal) { + HtUP/**/ *acc = ht_up_new (NULL, hints_at_addr_kv_free, NULL); + rz_anal_addr_hints_foreach (anal, addr_hint_acc_cb, acc); + rz_anal_arch_hints_foreach (anal, arch_hint_acc_cb, acc); + rz_anal_bits_hints_foreach (anal, bits_hint_acc_cb, acc); + ht_up_foreach (acc, hints_acc_store_cb, db); + ht_up_free (acc); +} + +enum { + HINTS_FIELD_ARCH, + HINTS_FIELD_BITS, + HINTS_FIELD_IMMBASE, + HINTS_FIELD_JUMP, + HINTS_FIELD_FAIL, + HINTS_FIELD_STACKFRAME, + HINTS_FIELD_PTR, + HINTS_FIELD_NWORD, + HINTS_FIELD_RET, + HINTS_FIELD_NEW_BITS, + HINTS_FIELD_SIZE, + HINTS_FIELD_SYNTAX, + HINTS_FIELD_OPTYPE, + HINTS_FIELD_OPCODE, + HINTS_FIELD_TYPE_OFFSET, + HINTS_FIELD_ESIL, + HINTS_FIELD_HIGH, + HINTS_FIELD_VAL +}; + +typedef struct { + RzAnal *anal; + KeyParser *parser; +} HintsLoadCtx; + +static bool hints_load_cb(void *user, const char *k, const char *v) { + HintsLoadCtx *ctx = user; + RzAnal *anal = ctx->anal; + + errno = 0; + ut64 addr = strtoull (k, NULL, 0); + if (errno) { + return false; + } + + char *json_str = strdup (v); + if (!json_str) { + return true; + } + RJson *json = rz_json_parse (json_str); + if (!json || json->type != RZ_JSON_OBJECT) { + free (json_str); + return false; + } + + KEY_PARSER_JSON (ctx->parser, json, child, { + case HINTS_FIELD_ARCH: + rz_anal_hint_set_arch (anal, addr, child->type == RZ_JSON_STRING ? child->str_value : NULL); + break; + case HINTS_FIELD_BITS: + rz_anal_hint_set_bits (anal, addr, child->type == RZ_JSON_INTEGER ? (int)child->num.s_value : 0); + break; + case HINTS_FIELD_IMMBASE: + if (child->type != RZ_JSON_INTEGER) { + break; + } + rz_anal_hint_set_immbase (anal, addr, (int)child->num.s_value); + break; + case HINTS_FIELD_JUMP: + if (child->type != RZ_JSON_INTEGER) { + break; + } + rz_anal_hint_set_jump (anal, addr, child->num.u_value); + break; + case HINTS_FIELD_FAIL: + if (child->type != RZ_JSON_INTEGER) { + break; + } + rz_anal_hint_set_fail (anal, addr, child->num.u_value); + break; + case HINTS_FIELD_STACKFRAME: + if (child->type != RZ_JSON_INTEGER) { + break; + } + rz_anal_hint_set_stackframe (anal, addr, child->num.u_value); + break; + case HINTS_FIELD_PTR: + if (child->type != RZ_JSON_INTEGER) { + break; + } + rz_anal_hint_set_pointer (anal, addr, child->num.u_value); + break; + case HINTS_FIELD_NWORD: + if (child->type != RZ_JSON_INTEGER) { + break; + } + rz_anal_hint_set_nword (anal, addr, (int)child->num.s_value); + break; + case HINTS_FIELD_RET: + if (child->type != RZ_JSON_INTEGER) { + break; + } + rz_anal_hint_set_ret (anal, addr, child->num.u_value); + break; + case HINTS_FIELD_NEW_BITS: + if (child->type != RZ_JSON_INTEGER) { + break; + } + rz_anal_hint_set_newbits (anal, addr, (int)child->num.s_value); + break; + case HINTS_FIELD_SIZE: + if (child->type != RZ_JSON_INTEGER) { + break; + } + rz_anal_hint_set_size (anal, addr, child->num.u_value); + break; + case HINTS_FIELD_SYNTAX: + if (child->type != RZ_JSON_STRING) { + break; + } + rz_anal_hint_set_syntax (anal, addr, child->str_value); + break; + case HINTS_FIELD_OPTYPE: + if (child->type != RZ_JSON_INTEGER) { + break; + } + rz_anal_hint_set_type (anal, addr, (int)child->num.s_value); + break; + case HINTS_FIELD_OPCODE: + if (child->type != RZ_JSON_STRING) { + break; + } + rz_anal_hint_set_opcode (anal, addr, child->str_value); + break; + case HINTS_FIELD_TYPE_OFFSET: + if (child->type != RZ_JSON_STRING) { + break; + } + rz_anal_hint_set_offset (anal, addr, child->str_value); + break; + case HINTS_FIELD_ESIL: + if (child->type != RZ_JSON_STRING) { + break; + } + rz_anal_hint_set_esil (anal, addr, child->str_value); + break; + case HINTS_FIELD_HIGH: + if (child->type != RZ_JSON_BOOLEAN || !child->num.u_value) { + break; + } + rz_anal_hint_set_high (anal, addr); + break; + case HINTS_FIELD_VAL: + if (child->type != RZ_JSON_INTEGER) { + break; + } + rz_anal_hint_set_val (anal, addr, child->num.u_value); + break; + default: + break; + }) + + rz_json_free (json); + free (json_str); + + return true; +} + +RZ_API bool rz_serialize_anal_hints_load(RZ_NONNULL Sdb *db, RZ_NONNULL RzAnal *anal, RZ_NULLABLE RzSerializeResultInfo *res) { + HintsLoadCtx ctx = { + .anal = anal, + .parser = key_parser_new (), + }; + bool ret; + if (!ctx.parser) { + SERIALIZE_ERR ("parser init failed"); + ret = false; + goto beach; + } + key_parser_add (ctx.parser, "arch", HINTS_FIELD_ARCH); + key_parser_add (ctx.parser, "bits", HINTS_FIELD_BITS); + key_parser_add (ctx.parser, "immbase", HINTS_FIELD_IMMBASE); + key_parser_add (ctx.parser, "jump", HINTS_FIELD_JUMP); + key_parser_add (ctx.parser, "fail", HINTS_FIELD_FAIL); + key_parser_add (ctx.parser, "frame", HINTS_FIELD_STACKFRAME); + key_parser_add (ctx.parser, "ptr", HINTS_FIELD_PTR); + key_parser_add (ctx.parser, "nword", HINTS_FIELD_NWORD); + key_parser_add (ctx.parser, "ret", HINTS_FIELD_RET); + key_parser_add (ctx.parser, "newbits", HINTS_FIELD_NEW_BITS); + key_parser_add (ctx.parser, "size", HINTS_FIELD_SIZE); + key_parser_add (ctx.parser, "syntax", HINTS_FIELD_SYNTAX); + key_parser_add (ctx.parser, "optype", HINTS_FIELD_OPTYPE); + key_parser_add (ctx.parser, "opcode", HINTS_FIELD_OPCODE); + key_parser_add (ctx.parser, "toff", HINTS_FIELD_TYPE_OFFSET); + key_parser_add (ctx.parser, "esil", HINTS_FIELD_ESIL); + key_parser_add (ctx.parser, "high", HINTS_FIELD_HIGH); + key_parser_add (ctx.parser, "val", HINTS_FIELD_VAL); + ret = sdb_foreach (db, hints_load_cb, &ctx); + if (!ret) { + SERIALIZE_ERR ("hints parsing failed"); + } +beach: + key_parser_free (ctx.parser); + return ret; +} + +RZ_API void rz_serialize_anal_classes_save(RZ_NONNULL Sdb *db, RZ_NONNULL RzAnal *anal) { + sdb_copy (anal->sdb_classes, db); +} + +RZ_API bool rz_serialize_anal_classes_load(RZ_NONNULL Sdb *db, RZ_NONNULL RzAnal *anal, RZ_NULLABLE RzSerializeResultInfo *res) { + if (!sdb_ns (db, "attrs", false)) { + SERIALIZE_ERR ("missing attrs namespace"); + return false; + } + sdb_reset (anal->sdb_classes); + sdb_reset (anal->sdb_classes_attrs); + sdb_copy (db, anal->sdb_classes); + return true; +} + +RZ_API void rz_serialize_anal_types_save(RZ_NONNULL Sdb *db, RZ_NONNULL RzAnal *anal) { + sdb_copy (anal->sdb_types, db); +} + +RZ_API bool rz_serialize_anal_types_load(RZ_NONNULL Sdb *db, RZ_NONNULL RzAnal *anal, RZ_NULLABLE RzSerializeResultInfo *res) { + sdb_reset (anal->sdb_types); + sdb_copy (db, anal->sdb_types); + return true; +} + +RZ_API void rz_serialize_anal_sign_save(RZ_NONNULL Sdb *db, RZ_NONNULL RzAnal *anal) { + sdb_copy (anal->sdb_zigns, db); + rz_serialize_spaces_save (sdb_ns (db, "spaces", true), &anal->zign_spaces); +} + +RZ_API bool rz_serialize_anal_sign_load(RZ_NONNULL Sdb *db, RZ_NONNULL RzAnal *anal, RZ_NULLABLE RzSerializeResultInfo *res) { + sdb_reset (anal->sdb_zigns); + sdb_copy (db, anal->sdb_zigns); + Sdb *spaces_db = sdb_ns (db, "spaces", false); + if (!spaces_db) { + SERIALIZE_ERR ("missing spaces namespace"); + return false; + } + if (!rz_serialize_spaces_load (spaces_db, &anal->zign_spaces, false, res)) { + return false; + } + return true; +} + +RZ_API void rz_serialize_anal_imports_save(RZ_NONNULL Sdb *db, RZ_NONNULL RzAnal *anal) { + RzListIter *it; + const char *imp; + rz_list_foreach (anal->imports, it, imp) { + sdb_set (db, imp, "i", 0); + } +} + +static bool import_load_cb(void *user, const char *k, const char *v) { + rz_anal_add_import (user, k); + return true; +} + +RZ_API bool rz_serialize_anal_imports_load(RZ_NONNULL Sdb *db, RZ_NONNULL RzAnal *anal, RZ_NULLABLE RzSerializeResultInfo *res) { + return sdb_foreach (db, import_load_cb, anal); +} + +RZ_API void rz_serialize_anal_pin_save(RZ_NONNULL Sdb *db, RZ_NONNULL RzAnal *anal) { + sdb_copy (anal->sdb_pins, db); +} + +RZ_API bool rz_serialize_anal_pin_load(RZ_NONNULL Sdb *db, RZ_NONNULL RzAnal *anal, RZ_NULLABLE RzSerializeResultInfo *res) { + sdb_copy (db, anal->sdb_pins); + return true; +} + +RZ_API void rz_serialize_anal_cc_save(RZ_NONNULL Sdb *db, RZ_NONNULL RzAnal *anal) { + sdb_copy (anal->sdb_cc, db); +} + +RZ_API bool rz_serialize_anal_cc_load(RZ_NONNULL Sdb *db, RZ_NONNULL RzAnal *anal, RZ_NULLABLE RzSerializeResultInfo *res) { + sdb_copy (db, anal->sdb_cc); + return true; +} + +RZ_API void rz_serialize_anal_save(RZ_NONNULL Sdb *db, RZ_NONNULL RzAnal *anal) { + rz_serialize_anal_xrefs_save (sdb_ns (db, "xrefs", true), anal); + rz_serialize_anal_blocks_save (sdb_ns (db, "blocks", true), anal); + rz_serialize_anal_functions_save (sdb_ns (db, "functions", true), anal); + rz_serialize_anal_meta_save (sdb_ns (db, "meta", true), anal); + rz_serialize_anal_hints_save (sdb_ns (db, "hints", true), anal); + rz_serialize_anal_classes_save (sdb_ns (db, "classes", true), anal); + rz_serialize_anal_types_save (sdb_ns (db, "types", true), anal); + rz_serialize_anal_sign_save (sdb_ns (db, "zigns", true), anal); + rz_serialize_anal_imports_save (sdb_ns (db, "imports", true), anal); + rz_serialize_anal_pin_save (sdb_ns (db, "pins", true), anal); + rz_serialize_anal_cc_save (sdb_ns (db, "cc", true), anal); +} + +RZ_API bool rz_serialize_anal_load(RZ_NONNULL Sdb *db, RZ_NONNULL RzAnal *anal, RZ_NULLABLE RzSerializeResultInfo *res) { + bool ret = false; + RSerializeAnalDiffParser diff_parser = rz_serialize_anal_diff_parser_new (); + if (!diff_parser) { + goto beach; + } + + rz_anal_purge (anal); + + Sdb *subdb; +#define SUB(ns, call) SUB_DO(ns, call, goto beach;) + SUB ("xrefs", rz_serialize_anal_xrefs_load (subdb, anal, res)); + + SUB ("blocks", rz_serialize_anal_blocks_load (subdb, anal, diff_parser, res)); + // All bbs have ref=1 now + SUB ("functions", rz_serialize_anal_functions_load (subdb, anal, diff_parser, res)); + // BB's refs have increased if they are part of a function. + // We must subtract from each to hold our invariant again. + // If any block has ref=0 then, it should be deleted. But we can't do this while + // iterating the RBTree, otherwise this will become a segfault cacophony, so we cache them. + RzPVector orphaned_bbs; + rz_pvector_init (&orphaned_bbs, (RzPVectorFree)rz_anal_block_unref); + RBIter iter; + RzAnalBlock *block; + rz_rbtree_foreach (anal->bb_tree, iter, block, RzAnalBlock, _rb) { + if (block->ref <= 1) { + rz_pvector_push (&orphaned_bbs, block); + continue; + } + rz_anal_block_unref (block); + } + rz_pvector_clear (&orphaned_bbs); // unrefs all + + SUB ("meta", rz_serialize_anal_meta_load (subdb, anal, res)); + SUB ("hints", rz_serialize_anal_hints_load (subdb, anal, res)); + SUB ("classes", rz_serialize_anal_classes_load (subdb, anal, res)); + SUB ("types", rz_serialize_anal_types_load (subdb, anal, res)); + SUB ("zigns", rz_serialize_anal_sign_load (subdb, anal, res)); + SUB ("imports", rz_serialize_anal_imports_load (subdb, anal, res)); + SUB ("pins", rz_serialize_anal_pin_load (subdb, anal, res)); + SUB ("cc", rz_serialize_anal_cc_load (subdb, anal, res)); + + ret = true; +beach: + rz_serialize_anal_diff_parser_free (diff_parser); + return ret; +} diff --git a/librz/bin/d/Makefile b/librz/bin/d/Makefile index a0d1614ee9..597ed806b9 100644 --- a/librz/bin/d/Makefile +++ b/librz/bin/d/Makefile @@ -19,13 +19,13 @@ DLLS+=CSPMAN DCIMAN DDEML DESKCP16 DIBENG DISPDIB DISPLAY DSKMAINT DLLS+=ENABLE3 FAXCODEC GDI INET16 IOSCLASS KERNEL KEYBOARD LZEXPAND DLLS+=MAINCP16 MAPI MAPIU MAPIX MCIAVI MCICDA MCIMIDI MCIOLE DLLS+=MCIWAVE MIDIMAP ML3XEC16 MMCI MMSYSTEM MODEM MODEMUI MOUSE -DLLS+=MSACM MSACMMAP MSDOS MSDOSD MSGSRV32 MSJSTICK MSMIXMGR MSPCIC +DLLS+=MSACM MSACMMAP MSDOS MSDOSD MSJSTICK MSMIXMGR MSPCIC DLLS+=MSPRINT MSTCP MSVIDEO NETAPI NETCPL NETDI NETOS NETWARE DLLS+=NW16 OLE2 OLE2CONV OLE2DISP OLE2NLS OLECLI OLESVR PIFMGR DLLS+=PKPD PMSPL POWER RASAPI16 RNASETUP RSRC16 SB16SND SBFM DLLS+=SETUP4 SETUPX SHELL SOUND SPOOLER STORAGE SYSCLASS SYSDETMG -DLLS+=SYSDM SYSEDIT SYSTEM SYSTHUNK TAPI TAPIADDR TAPIEXE -DLLS+=TAPIINI TOOLHELP TYPELIB UMDM16 USER VER WHLP16T WIN32S16 +DLLS+=SYSDM SYSEDIT SYSTEM SYSTHUNK TAPI TAPIADDR +DLLS+=TOOLHELP TYPELIB UMDM16 USER VER WHLP16T WIN32S16 DLLS+=WIN87EM WINASPI WINNET16 WINOLDAP WINSOCK WINSPL16 WPSAPD WPSUNI DLLS+=WPSUNIRE WSASRV DLL_SDB=$(addsuffix .sdb,$(addprefix dll/,$(DLLS))) diff --git a/librz/bin/d/dll/MSGSRV32.sdb.txt b/librz/bin/d/dll/MSGSRV32.sdb.txt deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/librz/bin/d/dll/TAPIEXE.sdb.txt b/librz/bin/d/dll/TAPIEXE.sdb.txt deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/librz/bin/d/dll/TAPIINI.sdb.txt b/librz/bin/d/dll/TAPIINI.sdb.txt deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/librz/bin/d/meson.build b/librz/bin/d/meson.build index ef699c07c5..08c6cdc2c9 100644 --- a/librz/bin/d/meson.build +++ b/librz/bin/d/meson.build @@ -89,7 +89,6 @@ sdb_files = [ 'MSACMMAP', 'MSDOS', 'MSDOSD', - 'MSGSRV32', 'MSJSTICK', 'MSMIXMGR', 'MSPCIC', @@ -131,8 +130,6 @@ sdb_files = [ 'SYSTHUNK', 'TAPI', 'TAPIADDR', - 'TAPIEXE', - 'TAPIINI', 'TOOLHELP', 'TYPELIB', 'UMDM16', diff --git a/librz/config/Makefile b/librz/config/Makefile index b5a9b419fe..b3c549d3ea 100644 --- a/librz/config/Makefile +++ b/librz/config/Makefile @@ -2,6 +2,6 @@ include ../config.mk NAME=rz_config RZ_DEPS=rz_util -OBJS=config.o callback.o hold.o +OBJS=config.o callback.o hold.o serialize_config.o include ../rules.mk diff --git a/librz/config/meson.build b/librz/config/meson.build index c61b095109..18dec9e853 100644 --- a/librz/config/meson.build +++ b/librz/config/meson.build @@ -2,6 +2,7 @@ rz_config_sources = [ 'callback.c', 'config.c', 'hold.c', + 'serialize_config.c' ] rz_config = library('rz_config', rz_config_sources, diff --git a/librz/config/serialize_config.c b/librz/config/serialize_config.c new file mode 100644 index 0000000000..968a5fa4d2 --- /dev/null +++ b/librz/config/serialize_config.c @@ -0,0 +1,39 @@ +/* radare - LGPL - Copyright 2019 - thestr4ng3r */ + +#include +#include + +/* + * + * RzConfig isn't completely serialized, only the values. + * + * SDB Format: + * + * / + * = + * ... + * + */ + +RZ_API void rz_serialize_config_save(RZ_NONNULL Sdb *db, RZ_NONNULL RzConfig *config) { + RzListIter *iter; + RzConfigNode *node; + rz_list_foreach (config->nodes, iter, node) { + sdb_set (db, node->name, node->value, 0); + } +} + +static bool load_config_cb(void *user, const char *k, const char *v) { + RzConfig *config = user; + RzConfigNode *node = rz_config_node_get (config, k); + if (!node) { + return 1; + } + rz_config_set (config, k, v); + return 1; +} + +RZ_API bool rz_serialize_config_load(RZ_NONNULL Sdb *db, RZ_NONNULL RzConfig *config, RZ_NULLABLE RzSerializeResultInfo *res) { + sdb_foreach (db, load_config_cb, config); + return true; +} diff --git a/librz/core/Makefile b/librz/core/Makefile index a12922d4fe..d4d80b4c2c 100644 --- a/librz/core/Makefile +++ b/librz/core/Makefile @@ -11,7 +11,7 @@ OBJS+=fortune.o hack.o vasm.o patch.o cbin.o corelog.o rtr.o cmd_api.o OBJS+=carg.o canal.o project.o gdiff.o casm.o disasm.o cplugin.o OBJS+=vmenus.o vmenus_graph.o vmenus_zigns.o zdiff.o citem.o OBJS+=task.o panels.o pseudo.o vmarks.o anal_tp.o anal_objc.o blaze.o cundo.o -OBJS+=cannotated_code.o +OBJS+=cannotated_code.o serialize_core.o CFLAGS+=-I../../shlr/heap/include CFLAGS+=-I../../shlr/tree-sitter/lib/include -I../../shlr/rizin-shell-parser/src/tree_parser diff --git a/librz/core/cconfig.c b/librz/core/cconfig.c index ba17090334..7d67321c6f 100644 --- a/librz/core/cconfig.c +++ b/librz/core/cconfig.c @@ -3221,12 +3221,7 @@ RZ_API int rz_core_config_init(RzCore *core) { SETCB ("bin.verbose", "false", &cb_binverbose, "Show RzBin warnings when loading binaries"); /* prj */ - SETPREF ("prj.name", "", "Name of current project"); - SETBPREF ("prj.files", "false", "Save the target binary inside the project directory"); - SETBPREF ("prj.git", "false", "Every project is a git repo and saving is committing"); - SETBPREF ("prj.zip", "false", "Use ZIP format for project files"); - SETBPREF ("prj.gpg", "false", "TODO: Encrypt project with GnuPGv2"); - SETBPREF ("prj.simple", "false", "Use simple project saving style (functions, comments, options)"); + SETPREF ("prj.file", "", "Path of the currently opened project"); /* cfg */ SETBPREF ("cfg.r2wars", "false", "Enable some tweaks for the r2wars game"); diff --git a/librz/core/cmd.c b/librz/core/cmd.c index 055056f21d..e37c8f940b 100644 --- a/librz/core/cmd.c +++ b/librz/core/cmd.c @@ -2117,7 +2117,6 @@ static struct autocomplete_flag_map_t { { "$flsp", "shows known flag-spaces hints", RZ_CORE_AUTOCMPLT_FLSP }, { "$zign", "shows known zignatures hints", RZ_CORE_AUTOCMPLT_ZIGN }, { "$eval", "shows known evals hints", RZ_CORE_AUTOCMPLT_EVAL }, - { "$prjt", "shows known projects hints", RZ_CORE_AUTOCMPLT_PRJT }, { "$mins", NULL, RZ_CORE_AUTOCMPLT_MINS }, { "$brkp", "shows known breakpoints hints", RZ_CORE_AUTOCMPLT_BRKP }, { "$macro", NULL, RZ_CORE_AUTOCMPLT_MACR }, @@ -7159,7 +7158,7 @@ RZ_API void rz_core_cmd_init(RzCore *core) { { "L", "manage dynamically loaded plugins", cmd_plugins, NULL, &L_help }, { "o", "open or map file", cmd_open, cmd_open_init, &o_help }, { "p", "print current block", cmd_print, cmd_print_init, &p_help }, - { "P", "project", cmd_project, cmd_project_init, &P_help }, + { "P", "project", NULL, cmd_project_init, NULL, &P_group_help, RZ_CMD_DESC_TYPE_GROUP}, { "q", "exit program session", cmd_quit, cmd_quit_init, &q_help }, { "Q", "alias for q!", cmd_Quit, NULL, &Q_help }, { ":", "long commands starting with :", cmd_colon, NULL, &colon_help }, diff --git a/librz/core/cmd_helps.c b/librz/core/cmd_helps.c index 44d76abe45..9baee37a6c 100644 --- a/librz/core/cmd_helps.c +++ b/librz/core/cmd_helps.c @@ -383,8 +383,18 @@ const RzCmdDescHelp p_help = { .summary = "print commands", }; -const RzCmdDescHelp P_help = { - .summary = "project management utilities", +const RzCmdDescHelp P_group_help = { + .summary = "project management", +}; + +const RzCmdDescHelp Ps_help = { + .summary = "save a project", + .args_str = " [project.rzdb]" +}; + +const RzCmdDescHelp Po_help = { + .summary = "load a project", + .args_str = " [project.rzdb]" }; const RzCmdDescHelp q_help = { diff --git a/librz/core/cmd_helps.h b/librz/core/cmd_helps.h index 0c61f03df6..0ab64f046b 100644 --- a/librz/core/cmd_helps.h +++ b/librz/core/cmd_helps.h @@ -43,7 +43,9 @@ extern const RzCmdDescHelp L_help; extern const RzCmdDescHelp m_help; extern const RzCmdDescHelp o_help; extern const RzCmdDescHelp p_help; -extern const RzCmdDescHelp P_help; +extern const RzCmdDescHelp P_group_help; +extern const RzCmdDescHelp Ps_help; +extern const RzCmdDescHelp Po_help; extern const RzCmdDescHelp q_help; extern const RzCmdDescHelp Q_help; extern const RzCmdDescHelp colon_help; diff --git a/librz/core/cmd_project.c b/librz/core/cmd_project.c index 9d38721221..7c9a9ae071 100644 --- a/librz/core/cmd_project.c +++ b/librz/core/cmd_project.c @@ -1,244 +1,37 @@ /* radare - LGPL - Copyright 2009-2017 - pancake */ -#include "rz_config.h" -#include "rz_core.h" -#include "rz_util.h" +#include -static const char *help_msg_P[] = { - "Usage:", "P[?osi] [file]", "Project management", - "P", "", "list all projects", - "Pc", " [file]", "show project script to console", - "Pd", " [file]", "delete project", - "Pi", " [file]", "show project information", - "Pn", "[j]", "show project notes (Pnj for json)", - "Pn", " [base64]", "set notes text", - "Pn", " -", "edit notes with cfg.editor", - "Po", " [file]", "open project", - "Ps", " [file]", "save project", - "PS", " [file]", "save script file", - "P-", " [file]", "delete project (alias for Pd)", - "NOTE:", "", "See 'e??prj.'", - "NOTE:", "", "project are stored in " RZ_JOIN_2_PATHS ("~", RZ_HOME_PROJECTS), - NULL -}; +static RzCmdStatus Ps_handler(RzCore *core, int argc, const char **argv) { + if (argc != 2) { + return RZ_CMD_STATUS_WRONG_ARGS; + } + RzProjectErr err = rz_project_save_file (core, argv[1]); + if (err != RZ_PROJECT_ERR_SUCCESS) { + eprintf ("Failed to save project: %s\n", rz_project_err_message (err)); + } + return RZ_CMD_STATUS_OK; +} -static const char *help_msg_Pn[] = { - "Usage:", "Pn[j-?] [...]", "Project Notes", - "Pn", "", "show project notes", - "Pn", " -", "edit notes with cfg.editor", - "Pn-", "", "delete notes", - "Pn-", "str", "delete lines matching /str/ in notes", - "Pn+", "str", "append one line to the notes", - "Pnj", "", "show notes in base64", - "Pnj", " [base64]", "set notes in base64", - "Pnx", "", "run project note commands", - NULL -}; +static RzCmdStatus Po_handler(RzCore *core, int argc, const char **argv) { + if (argc != 2) { + return RZ_CMD_STATUS_WRONG_ARGS; + } + RzSerializeResultInfo *res = rz_serialize_result_info_new (); + RzProjectErr err = rz_project_load_file (core, argv[1], res); + if (err != RZ_PROJECT_ERR_SUCCESS) { + eprintf ("Failed to load project: %s\n", rz_project_err_message (err)); + RzListIter *it; + char *s; + rz_list_foreach (res, it, s) { + eprintf (" %s\n", s); + } + } + rz_serialize_result_info_free (res); + return RZ_CMD_STATUS_OK; +} static void cmd_project_init(RzCore *core, RzCmdDesc *parent) { - DEPRECATED_DEFINE_CMD_DESCRIPTOR (core, P); - DEPRECATED_DEFINE_CMD_DESCRIPTOR (core, Pn); -} - -static int cmd_project(void *data, const char *input) { - RzCore *core = (RzCore *) data; - const char *file, *arg; - const char *fileproject = rz_config_get (core->config, "prj.name"); - char *str = NULL; - - if (!input) { - return false; - } - str = strdup (fileproject); - arg = strchr (input, ' '); - if (arg) { - arg++; - } else { - if (*input) { - arg = input + 1; - if (*arg == '&') { - arg++; - } - } - } - file = arg; - switch (input[0]) { - case 'c': - if (input[1] == ' ') { - rz_core_project_cat (core, input + 2); - } else { - eprintf ("Usage: Pc [prjname]\n"); - } - break; - case 'o': - // if (rz_file_is_regular (file)) - if (input[1] == '&') { - rz_core_cmdf (core, "& Po %s", file); - } else if (input[1]) { - rz_core_project_open (core, file, false); - } else { - if (file && *file) { - rz_cons_println (file); - } - } - break; - case 'd': - case '-': - rz_core_project_delete (core, file); - break; - case 's': - if (!file || !file[0]) { /* if no argument specified use current project */ - file = str; - } - if (rz_core_project_save (core, file)) { - rz_cons_println (file); - } - break; - case 'S': - if (input[1] == ' ') { - rz_core_project_save_script (core, input + 2, RZ_CORE_PRJ_ALL); - } else { - eprintf ("Usage: PS [file]\n"); - } - break; - case 'n': // "Pn" - if (input[1] == '?') { - rz_core_cmd_help (core, help_msg_Pn); - } else if (!fileproject || !*fileproject) { - eprintf ("No project\n"); - } else { - switch (input[1]) { - case '-': // "Pn-" - /* remove lines containing specific words */ - { - FILE *fd = rz_sandbox_fopen (str, "w"); - if (!fd) { - eprintf ("Cannot open %s\n", str); - } else { - char *str = rz_core_project_notes_file (core, fileproject); - char *data = rz_file_slurp (str, NULL); - int del = 0; - if (data) { - char *ptr, *nl; - for (ptr = data; ptr; ptr = nl) { - nl = strchr (ptr, '\n'); - if (nl) { - *nl++ = 0; - if (strstr (ptr, input + 2)) { - del++; - } else { - fprintf (fd, "%s\n", ptr); - } - } - } - free (data); - } - if (del > 0) { - eprintf ("Deleted %d lines\n", del); - } - free (str); - fclose (fd); - } - } - break; - case ' ': // "Pn " - if (input[2] == '-') { - char *str = rz_core_project_notes_file (core, fileproject); - // edit with cfg.editor - const char *editor = rz_config_get (core->config, "cfg.editor"); - if (str && *str && editor && *editor) { - rz_sys_cmdf ("%s %s", editor, str); - } else { - eprintf ("No cfg.editor configured\n"); - } - free (str); - } else { - // char *str = rz_core_project_notes_file (core, fileproject); - // append line to project notes - char *str = rz_core_project_notes_file (core, fileproject); - char *data = rz_file_slurp (str, NULL); - FILE *fd = rz_sandbox_fopen (str, "a"); - if (fd) { - fprintf (fd, "%s\n", input + 2); - fclose (fd); - } - free (str); - free (data); - } - break; - case '+': // "Pn+" - { - char *str = rz_core_project_notes_file (core, fileproject); - char *data = rz_file_slurp (str, NULL); - data = rz_str_append (data, input + 2); - data = rz_str_append (data, "\n"); - rz_file_dump (str, (const ut8*)data, strlen (data), false); - free (data); - free (str); - } - break; - case 'j': // "Pnj" - if (!input[2]) { - size_t len = 0; - /* get base64 string */ - char *str = rz_core_project_notes_file (core, fileproject); - if (str) { - char *data = rz_file_slurp (str, &len); - char *res = rz_base64_encode_dyn (data, (int)len); - if (res) { - rz_cons_println (res); - free (res); - } - free (data); - free (str); - } - } else if (input[2] == ' ') { - /* set base64 string */ - ut8 *data = rz_base64_decode_dyn (input + 3, -1); - if (data) { - char *str = rz_core_project_notes_file (core, fileproject); - if (str) { - rz_file_dump (str, data, strlen ((const char *) data), 0); - free (str); - } - free (data); - } - } else { - eprintf ("Usage: `Pnj` or `Pnj ...`\n"); - } - break; - case 'x': // "Pnx" - rz_core_project_execute_cmds (core, fileproject); - break; - case 0: // "Pn" - { - char *str = rz_core_project_notes_file (core, fileproject); - char *data = rz_file_slurp (str, NULL); - if (data) { - rz_cons_println (data); - free (data); - } - free (str); - } - break; - } - } - break; - case 'i': - if (file && *file) { - char *prjName = rz_core_project_info (core, file); - rz_cons_println (prjName); - free (prjName); - } - break; - case 0: - case 'j': - rz_core_project_list (core, input[0]); - break; - default: - rz_core_cmd_help (core, help_msg_P); - break; - } - free (str); - return true; + DEFINE_CMD_ARGV_DESC (core, Ps, parent); + DEFINE_CMD_ARGV_DESC (core, Po, parent); } diff --git a/librz/core/core.c b/librz/core/core.c index 8da7b4d302..5aff2485dc 100644 --- a/librz/core/core.c +++ b/librz/core/core.c @@ -1216,25 +1216,6 @@ static void autocomplete_evals(RzCore *core, RLineCompletion *completion, const } } -static void autocomplete_project(RzCore *core, RLineCompletion *completion, const char* str) { - rz_return_if_fail (str); - char *foo, *projects_path = rz_file_abspath (rz_config_get (core->config, "dir.projects")); - RzList *list = rz_sys_dir (projects_path); - RzListIter *iter; - int n = strlen (str); - if (projects_path) { - rz_list_foreach (list, iter, foo) { - if (rz_core_is_project (core, foo)) { - if (!strncmp (foo, str, n)) { - rz_line_completion_push (completion, foo); - } - } - } - free (projects_path); - rz_list_free (list); - } -} - static void autocomplete_minus(RzCore *core, RLineCompletion *completion, const char *str) { rz_return_if_fail (str); int count; @@ -1547,9 +1528,6 @@ static bool find_autocomplete(RzCore *core, RLineCompletion *completion, RLineBu case RZ_CORE_AUTOCMPLT_EVAL: autocomplete_evals (core, completion, p); break; - case RZ_CORE_AUTOCMPLT_PRJT: - autocomplete_project (core, completion, p); - break; case RZ_CORE_AUTOCMPLT_MINS: autocomplete_minus (core, completion, p); break; @@ -2257,10 +2235,7 @@ static void __init_autocomplete_default (RzCore* core) { "idp", "idpi", "L", "obf", "o+", "oc", "rz", "rz_bin", "rz_asm", "rz_hash", "rz_ax", "rz_find", "cd", "ls", "on", "op", "wf", "rm", "wF", "wp", "Sd", "Sl", "to", "pm", "/m", "zos", "zfd", "zfs", "zfz", "cat", "wta", "wtf", "wxf", "dml", "vi", - "less", "head", "tail", NULL - }; - const char *projs[] = { - "Pc", "Pd", "Pi", "Po", "Ps", "P-", NULL + "less", "head", "tail", "Ps", "Pl", NULL }; __foreach (core, flags, RZ_CORE_AUTOCMPLT_FLAG); __foreach (core, seeks, RZ_CORE_AUTOCMPLT_SEEK); @@ -2268,7 +2243,6 @@ static void __init_autocomplete_default (RzCore* core) { __foreach (core, evals, RZ_CORE_AUTOCMPLT_EVAL); __foreach (core, breaks, RZ_CORE_AUTOCMPLT_BRKP); __foreach (core, files, RZ_CORE_AUTOCMPLT_FILE); - __foreach (core, projs, RZ_CORE_AUTOCMPLT_PRJT); rz_core_autocomplete_add (core->autocomplete, "-", RZ_CORE_AUTOCMPLT_MINS, true); rz_core_autocomplete_add (core->autocomplete, "zs", RZ_CORE_AUTOCMPLT_ZIGN, true); diff --git a/librz/core/meson.build b/librz/core/meson.build index b25d38aa2c..73dc2413a0 100644 --- a/librz/core/meson.build +++ b/librz/core/meson.build @@ -65,15 +65,16 @@ rz_core_sources = [ 'yank.c', 'p/core_a2f.c', 'p/core_java.c', - 'cannotated_code.c' + 'cannotated_code.c', + 'serialize_core.c' ] rz_core_inc = [] if host_machine.system() != 'windows' rz_core_inc += ['../../shlr/heap/include'] else - rz_core_sources += 'windows_heap.c' - rz_core_inc += ['../../shlr/heap/include/rz_windows'] + rz_core_sources += 'windows_heap.c' + rz_core_inc += ['../../shlr/heap/include/rz_windows'] endif rz_core_inc = [platform_inc, include_directories(rz_core_inc)] diff --git a/librz/core/project.c b/librz/core/project.c index 04ddbe3a51..b1cb7e11f0 100644 --- a/librz/core/project.c +++ b/librz/core/project.c @@ -1,939 +1,99 @@ -/* radare - LGPL - Copyright 2010-2020 - pancake, maijin */ +/* rizin - LGPL - Copyright 2020 - thestr4ng3r */ -#include -#include -#include -#include -#define USE_R2 1 -#include +#include -static bool is_valid_project_name(const char *name) { - int i; - if (rz_str_endswith (name, ".zip")) { - return false; - } - for (i = 0; name[i]; i++) { - switch (name[i]) { - case '\\': // for w32 - case '.': - case '_': - case ':': - case '-': - continue; - } - if (name[i] >= 'a' && name[i] <= 'z') { - continue; - } - if (name[i] >= 'A' && name[i] <= 'Z') { - continue; - } - if (IS_DIGIT (name[i])) { - continue; - } - return false; - } - return true; -} +#include "../util/serialize_helper.h" -static char *get_project_script_path(RzCore *core, const char *file) { - const char *magic = "# r2 rdb project file"; - char *data, *prjfile; - if (rz_file_is_abspath (file)) { - prjfile = strdup (file); - } else { - if (!is_valid_project_name (file)) { - return NULL; - } - prjfile = rz_file_abspath (rz_config_get (core->config, "dir.projects")); - prjfile = rz_str_append (prjfile, RZ_SYS_DIR); - prjfile = rz_str_append (prjfile, file); - if (!rz_file_exists (prjfile) || rz_file_is_directory (prjfile)) { - prjfile = rz_str_append (prjfile, RZ_SYS_DIR "rc"); - } - } - data = rz_file_slurp (prjfile, NULL); - if (data) { - if (strncmp (data, magic, strlen (magic))) { - RZ_FREE (prjfile); - } - } - free (data); - return prjfile; -} +#define RZ_DB_KEY_TYPE "type" +#define RZ_DB_KEY_VERSION "version" -static int make_projects_directory(RzCore *core) { - char *prjdir = rz_file_abspath (rz_config_get (core->config, "dir.projects")); - int ret = rz_sys_mkdirp (prjdir); - if (!ret) { - eprintf ("Cannot mkdir dir.projects\n"); - } - free (prjdir); - return ret; -} +#define RZ_DB_PROJECT_VERSION 1 +#define RZ_DB_PROJECT_TYPE "rizin rz-db project" -RZ_API bool rz_core_is_project(RzCore *core, const char *name) { - bool ret = false; - if (name && *name && *name != '.') { - char *path = get_project_script_path (core, name); - if (!path) { - return false; - } - if (rz_str_endswith (path, RZ_SYS_DIR "rc") && rz_file_exists (path)) { - ret = true; - } else { - path = rz_str_append (path, ".d"); - if (rz_file_is_directory (path)) { - ret = true; - } - } - free (path); - } - return ret; -} - -RZ_API int rz_core_project_cat(RzCore *core, const char *name) { - char *path = get_project_script_path (core, name); - if (path) { - char *data = rz_file_slurp (path, NULL); - if (data) { - rz_cons_println (data); - free (data); - } - } - free (path); - return 0; -} - -RZ_API int rz_core_project_list(RzCore *core, int mode) { - PJ *pj = NULL; - RzListIter *iter; - RzList *list; - - char *foo, *path = rz_file_abspath (rz_config_get (core->config, "dir.projects")); - if (!path) { - return 0; - } - list = rz_sys_dir (path); - switch (mode) { - case 'j': - pj = pj_new (); - if (!pj) { - break; - } - pj_a (pj); - rz_list_foreach (list, iter, foo) { - // todo. escape string - if (rz_core_is_project (core, foo)) { - pj_s (pj, foo); - } - } - pj_end (pj); - rz_cons_printf ("%s\n", pj_string (pj)); - pj_free (pj); - break; - default: - rz_list_foreach (list, iter, foo) { - if (rz_core_is_project (core, foo)) { - rz_cons_println (foo); - } - } +RZ_API RZ_NONNULL const char *rz_project_err_message(RzProjectErr err) { + switch (err) { + case RZ_PROJECT_ERR_SUCCESS: + return "success"; + case RZ_PROJECT_ERR_FILE: + return "file access error"; + case RZ_PROJECT_ERR_INVALID_TYPE: + return "invalid file type"; + case RZ_PROJECT_ERR_INVALID_VERSION: + return "invalid project version"; + case RZ_PROJECT_ERR_NEWER_VERSION: + return "newer project version"; + case RZ_PROJECT_ERR_INVALID_CONTENTS: + return "invalid content encountered"; + case RZ_PROJECT_ERR_UNKNOWN: break; } - rz_list_free (list); - free (path); - return 0; + return "unknown error"; } -static inline void remove_project_file(char * path) { - if (rz_file_exists (path)) { - rz_file_rm (path); - eprintf ("rm %s\n", path); - } +RZ_API RzProjectErr rz_project_save(RzCore *core, RzProject *prj) { + sdb_set (prj, RZ_DB_KEY_TYPE, RZ_DB_PROJECT_TYPE, 0); + sdb_set (prj, RZ_DB_KEY_VERSION, sdb_fmt ("%u", RZ_DB_PROJECT_VERSION), 0); + rz_serialize_core_save (sdb_ns (prj, "core", true), core); + return RZ_PROJECT_ERR_SUCCESS; } -static inline void remove_notes_file(char *prjDir) { - char *notes_txt = rz_str_newf ("%s%s%s", prjDir, RZ_SYS_DIR, "notes.txt"); - if (rz_file_exists (notes_txt)) { - rz_file_rm (notes_txt); - eprintf ("rm %s\n", notes_txt); - } - free(notes_txt); -} - -static inline void remove_rop_directory(char *prjDir) { - char *rop_d = rz_str_newf ("%s%s%s", prjDir, RZ_SYS_DIR, "rop.d"); - - if (rz_file_is_directory (rop_d)) { - char *f; - RzListIter *iter; - RzList *files = rz_sys_dir (rop_d); - rz_list_foreach (files, iter, f) { - char *filepath = rz_str_append (strdup (rop_d), RZ_SYS_DIR); - filepath = rz_str_append (filepath, f); - if (!rz_file_is_directory (filepath)) { - eprintf ("rm %s\n", filepath); - rz_file_rm (filepath); - } - - free (filepath); - } - - rz_file_rm (rop_d); - eprintf ("rm %s\n", rop_d); - rz_list_free (files); - } - - free (rop_d); -} -RZ_API int rz_core_project_delete(RzCore *core, const char *prjfile) { - if (rz_sandbox_enable (0)) { - eprintf ("Cannot delete project in sandbox mode\n"); - return 0; - } - char *path = get_project_script_path (core, prjfile); - if (!path) { - eprintf ("Invalid project name '%s'\n", prjfile); - return false; - } - if (rz_core_is_project (core, prjfile)) { - char *prjDir = rz_file_dirname (path); - if (!prjDir) { - eprintf ("Cannot resolve directory\n"); - free (path); - return false; - } - remove_project_file (path); - remove_notes_file (prjDir); - remove_rop_directory (prjDir); - // remove directory only if it's empty - rz_file_rm (prjDir); - free (prjDir); - } - free (path); - return 0; -} - -static bool load_project_rop(RzCore *core, const char *prjfile) { - char *path, *db = NULL, *path_ns; - bool found = 0; - SdbListIter *it; - SdbNs *ns; - - if (!prjfile || !*prjfile) { - return false; - } - - Sdb *rop_db = sdb_ns (core->sdb, "rop", false); - Sdb *nop_db = sdb_ns (rop_db, "nop", false); - Sdb *mov_db = sdb_ns (rop_db, "mov", false); - Sdb *const_db = sdb_ns (rop_db, "const", false); - Sdb *arithm_db = sdb_ns (rop_db, "arithm", false); - Sdb *arithmct_db = sdb_ns (rop_db, "arithm_ct", false); - - char *rcPath = get_project_script_path (core, prjfile); - char *prjDir = rz_file_dirname (rcPath); - - if (rz_str_endswith (prjfile, RZ_SYS_DIR "rc")) { - // XXX - eprintf ("ENDS WITH\n"); - path = strdup (prjfile); - path[strlen (path) - 3] = 0; - } else if (rz_file_fexists ("%s" RZ_SYS_DIR "rc", prjDir, prjfile)) { - path = rz_str_newf ("%s" RZ_SYS_DIR, prjDir, prjfile); - } else { - if (*prjfile == RZ_SYS_DIR[0]) { - db = rz_str_newf ("%s.d", prjfile); - if (!db) { - free (prjDir); - free (rcPath); - return false; - } - path = strdup (db); - } else { - db = rz_str_newf ("%s" RZ_SYS_DIR "%s.d", prjDir, prjfile); - if (!db) { - free (prjDir); - free (rcPath); - return false; - } - path = rz_file_abspath (db); - } - } - if (!path) { - free (db); - free (prjDir); - free (rcPath); - return false; - } - if (rop_db) { - ls_foreach (core->sdb->ns, it, ns){ - if (ns->sdb == rop_db) { - ls_delete (core->sdb->ns, it); - found = true; - break; - } - } - } - if (!found) { - sdb_free (rop_db); - } - rop_db = sdb_new (path, "rop", 0); - if (!rop_db) { - free (db); - free (path); - free (prjDir); - free (rcPath); - return false; - } - sdb_ns_set (core->sdb, "rop", rop_db); - - path_ns = rz_str_newf ("%s" RZ_SYS_DIR "rop", prjDir); - if (!rz_file_exists (path_ns)) { - path_ns = rz_str_append (path_ns, ".sdb"); - } - nop_db = sdb_new (path_ns, "nop", 0); - sdb_ns_set (rop_db, "nop", nop_db); - - mov_db = sdb_new (path_ns, "mov", 0); - sdb_ns_set (rop_db, "mov", mov_db); - - const_db = sdb_new (path_ns, "const", 0); - sdb_ns_set (rop_db, "const", const_db); - - arithm_db = sdb_new (path_ns, "arithm", 0); - sdb_ns_set (rop_db, "arithm", arithm_db); - - arithmct_db = sdb_new (path_ns, "arithm_ct", 0); - sdb_ns_set (rop_db, "arithm_ct", arithmct_db); - - free (path); - free (path_ns); - free (db); - free (prjDir); - free (rcPath); - return true; -} - -RZ_API void rz_core_project_execute_cmds(RzCore *core, const char *prjfile) { - char *str = rz_core_project_notes_file (core, prjfile); - char *data = rz_file_slurp (str, NULL); - if (!data) { - free (str); - return; - } - Output out; - out.fout = NULL; - out.cout = rz_strbuf_new (NULL); - rz_strbuf_init (out.cout); - struct Proc proc; - spp_proc_set (&proc, "spp", 1); - spp_eval (data, &out); - free (data); - data = strdup (rz_strbuf_get (out.cout)); - char *bol = strtok (data, "\n"); - while (bol) { - if (bol[0] == ':') { - rz_core_cmd0 (core, bol + 1); - } - bol = strtok (NULL, "\n"); - } - free (data); - free (str); -} - -/*** vvv thready ***/ - -typedef struct { - RzCore *core; - char *prjName; - char *rcPath; -} ProjectState; - -static RzThreadFunctionRet project_load_background(RzThread *th) { - ProjectState *ps = th->user; - rz_core_project_load (ps->core, ps->prjName, ps->rcPath); - free (ps->prjName); - free (ps->rcPath); - free (ps); - return RZ_TH_STOP; -} - -RZ_API RzThread *rz_core_project_load_bg(RzCore *core, const char *prjName, const char *rcPath) { - ProjectState *ps = RZ_NEW (ProjectState); - ps->core = core; - ps->prjName = strdup (prjName); - ps->rcPath = strdup (rcPath); - RzThread *th = rz_th_new (project_load_background, ps, false); - if (th) { - rz_th_start (th, true); - char thname[16] = {0}; - size_t thlen = RZ_MIN (strlen(prjName), sizeof(thname) - 1); - strncpy (thname, prjName, thlen); - thname[15] = 0; - rz_th_setname (th, thname); - } - return th; -} - -/*** ^^^ thready ***/ - -static ut64 getProjectLaddr(RzCore *core, const char *prjfile) { - ut64 laddr = 0; - char *buf = rz_file_slurp (prjfile, NULL); - char *pos; - if (buf) { - if ((pos = strstr(buf, "\"e bin.laddr = "))) { - laddr = rz_num_math (NULL, pos + 15); - } - free (buf); - } - return laddr; -} - -RZ_API bool rz_core_project_open(RzCore *core, const char *prjfile, bool thready) { - bool askuser = true; - int ret, close_current_session = 1; - char *oldbin; - const char *newbin; - ut64 mapaddr = 0; - if (!prjfile || !*prjfile) { - return false; - } - if (thready) { - eprintf ("Loading projects in a thread has been deprecated. Use tasks\n"); - return false; - } - char *prj = get_project_script_path (core, prjfile); +RZ_API RzProjectErr rz_project_save_file(RzCore *core, const char *file) { + RzProject *prj = sdb_new0 (); if (!prj) { - eprintf ("Invalid project name '%s'\n", prjfile); - return false; + return RZ_PROJECT_ERR_UNKNOWN; } - char *filepath = rz_core_project_info (core, prj); - // eprintf ("OPENING (%s) from %s\n", prj, rz_config_get (core->config, "file.path")); - /* if it is not an URI */ - if (!filepath) { - eprintf ("Cannot retrieve information for project '%s'\n", prj); - free (prj); - return false; + RzProjectErr err = rz_project_save (core, prj); + if (err != RZ_PROJECT_ERR_SUCCESS) { + sdb_free (prj); + return err; } - - if (!filepath[0]) { - goto cookiefactory; + if (!sdb_text_save (prj, file, true)) { + err = RZ_PROJECT_ERR_FILE; } - if (!strstr (filepath, "://")) { - /* check if path exists */ - if (!rz_file_exists (filepath)) { - eprintf ("Cannot find file '%s'\n", filepath); - free (prj); - free (filepath); - return false; - } - } - cookiefactory: - ; - const char *file_path = rz_config_get (core->config, "file.path"); - if (!file_path || !*file_path) { - file_path = rz_config_get (core->config, "file.lastpath"); - } - oldbin = strdup (file_path); - if (!strcmp (prjfile, rz_config_get (core->config, "prj.name"))) { - // eprintf ("Reloading project\n"); - askuser = false; -#if 0 - free (prj); - free (filepath); - return false; -#endif - } - if (askuser) { - if (rz_cons_is_interactive ()) { - close_current_session = rz_cons_yesno ('y', "Close current session? (Y/n)"); - } - } - if (close_current_session) { - // delete - rz_core_file_close_fd (core, -1); - rz_io_close_all (core->io); - rz_anal_purge (core->anal); - rz_flag_unset_all (core->flags); - rz_bin_file_delete_all (core->bin); - // open new file - // TODO: handle read/read-write mode - if (filepath[0]) { - /* Old-style project without embedded on commands to open all files. */ - if (!rz_core_file_open (core, filepath, 0, UT64_MAX)) { - eprintf ("Cannot open file '%s'\n", filepath); - ret = false; - goto beach; - } - } - } - mapaddr = getProjectLaddr (core, prj); - if (mapaddr) { - rz_config_set_i (core->config, "bin.laddr", mapaddr); - } - if (filepath[0] && close_current_session && rz_config_get_i (core->config, "file.info")) { - mapaddr = rz_config_get_i (core->config, "file.offset"); - (void)rz_core_bin_load (core, filepath, mapaddr? mapaddr: UT64_MAX); - } - /* load sdb stuff in here */ - ret = rz_core_project_load (core, prjfile, prj); - if (filepath[0]) { - newbin = rz_config_get (core->config, "file.path"); - if (!newbin || !*newbin) { - newbin = rz_config_get (core->config, "file.lastpath"); - } - if (strcmp (oldbin, newbin)) { - eprintf ("WARNING: file.path changed: %s => %s\n", oldbin, newbin); - } - } -beach: - free (oldbin); - free (filepath); - free (prj); - return ret; + sdb_free (prj); + return err; } -RZ_API char *rz_core_project_info(RzCore *core, const char *prjfile) { - FILE *fd; - char buf[256], *file = NULL; - char *prj = get_project_script_path (core, prjfile); +RZ_API RzProjectErr rz_project_load(RzCore *core, RzProject *prj, RZ_NULLABLE const char *file, RzSerializeResultInfo *res) { + const char *type = sdb_const_get (prj, RZ_DB_KEY_TYPE, 0); + if (!type || strcmp (type, RZ_DB_PROJECT_TYPE) != 0) { + return RZ_PROJECT_ERR_INVALID_TYPE; + } + const char *version_str = sdb_const_get (prj, RZ_DB_KEY_VERSION, 0); + if (!version_str) { + return RZ_PROJECT_ERR_INVALID_VERSION; + } + unsigned long version = strtoul (version_str, NULL, 0); + if (!version || version == ULONG_MAX) { + return RZ_PROJECT_ERR_INVALID_VERSION; + } else if (version > RZ_DB_PROJECT_VERSION) { + return RZ_PROJECT_ERR_NEWER_VERSION; + } + + Sdb *core_db = sdb_ns (prj, "core", false); + if (!core_db) { + SERIALIZE_ERR ("missing core namespace"); + return RZ_PROJECT_ERR_INVALID_CONTENTS; + } + if (!rz_serialize_core_load (core_db, core, res)) { + return RZ_PROJECT_ERR_INVALID_CONTENTS; + } + + rz_config_set (core->config, "prj.file", file); + + return RZ_PROJECT_ERR_SUCCESS; +} + +RZ_API RzProjectErr rz_project_load_file(RzCore *core, const char *file, RzSerializeResultInfo *res) { + RzProject *prj = sdb_new0 (); if (!prj) { - eprintf ("Invalid project name '%s'\n", prjfile); - return NULL; + return RZ_PROJECT_ERR_UNKNOWN; } - fd = rz_sandbox_fopen (prj, "r"); - if (fd) { - for (;;) { - if (!fgets (buf, sizeof (buf), fd)) { - break; - } - if (feof (fd)) { - break; - } - if (!strncmp (buf, "\"e file.path = ", 15)) { - buf[strlen (buf) - 2] = 0; - file = rz_str_new (buf + 15); - break; - } - if (!strncmp (buf, "\"e file.lastpath = ", 19)) { - buf[strlen (buf) - 2] = 0; - file = rz_str_new (buf + 19); - break; - } - // TODO: deprecate before 1.0 - if (!strncmp (buf, "e file.path = ", 14)) { - buf[strlen (buf) - 1] = 0; - file = rz_str_new (buf + 14); - break; - } - } - fclose (fd); - } else { - eprintf ("Cannot open project info (%s)\n", prj); + if (!sdb_text_load (prj, file)) { + SERIALIZE_ERR ("failed to read database file"); + return RZ_PROJECT_ERR_FILE; } -#if 0 - if (file) { - rz_cons_printf ("Project: %s\n", prj); - rz_cons_printf ("FilePath: %s\n", file); - } -#endif - free (prj); - return file; -} - -static int fdc; //this is a ugly, remove it, when we have $fd - -static bool store_files_and_maps (RzCore *core, RzIODesc *desc, ut32 id) { - RzList *maps = NULL; - RzListIter *iter; - RzIOMap *map; - if (desc) { - // reload bin info - rz_cons_printf ("\"obf %s\"\n", desc->uri); - rz_cons_printf ("\"ofs \\\"%s\\\" %s\"\n", desc->uri, rz_str_rwx_i (desc->perm)); - if ((maps = rz_io_map_get_for_fd (core->io, id))) { - rz_list_foreach (maps, iter, map) { - rz_cons_printf ("om %d 0x%"PFMT64x" 0x%"PFMT64x" 0x%"PFMT64x" %s%s%s\n", fdc, - map->itv.addr, map->itv.size, map->delta, rz_str_rwx_i (map->perm), - map->name ? " " : "", map->name ? map->name : ""); - } - rz_list_free (maps); - } - fdc++; - } - return true; -} - -static bool simple_project_save_script(RzCore *core, const char *file, int opts) { - char *filename, *hl, *ohl = NULL; - int fd, fdold; - - if (!file || * file == '\0') { - return false; - } - - filename = rz_str_word_get_first (file); - fd = rz_sandbox_open (file, O_BINARY | O_RDWR | O_CREAT | O_TRUNC, 0644); - if (fd == -1) { - free (filename); - return false; - } - - hl = rz_cons_singleton ()->highlight; - if (hl) { - ohl = strdup (hl); - rz_cons_highlight (NULL); - } - - fdold = rz_cons_singleton ()->fdout; - rz_cons_singleton ()->fdout = fd; - rz_cons_singleton ()->context->is_interactive = false; // NOES must use api - - rz_str_write (fd, "# r2 rdb project file\n"); - - if (opts & RZ_CORE_PRJ_EVAL) { - rz_str_write (fd, "# eval\n"); - rz_config_list (core->config, NULL, true); - rz_cons_flush (); - } - - if (opts & RZ_CORE_PRJ_FCNS) { - rz_str_write (fd, "# functions\n"); - rz_str_write (fd, "fs functions\n"); - rz_core_cmd (core, "afl*", 0); - rz_cons_flush (); - } - - if (opts & RZ_CORE_PRJ_FLAGS) { - rz_str_write (fd, "# flags\n"); - rz_core_cmd (core, "f.**", 0); - rz_cons_flush (); - } - if (opts & RZ_CORE_PRJ_META) { - rz_str_write (fd, "# meta\n"); - rz_meta_print_list_all (core->anal, RZ_META_TYPE_ANY, 1); - rz_cons_flush (); - rz_core_cmd (core, "fV*", 0); - rz_cons_flush (); - } - if (opts & RZ_CORE_PRJ_XREFS) { - rz_str_write (fd, "# xrefs\n"); - rz_core_cmd (core, "ax*", 0); - rz_cons_flush (); - } - - - rz_cons_singleton ()->fdout = fdold; - rz_cons_singleton ()->context->is_interactive = true; - - if (ohl) { - rz_cons_highlight (ohl); - free (ohl); - } - - close (fd); - free (filename); - - return true; -} - -static bool project_save_script(RzCore *core, const char *file, int opts) { - char *filename, *hl, *ohl = NULL; - int fd, fdold; - - if (!file || *file == '\0') { - return false; - } - - filename = rz_str_word_get_first (file); - fd = rz_sandbox_open (file, O_BINARY | O_RDWR | O_CREAT | O_TRUNC, 0644); - if (fd == -1) { - free (filename); - return false; - } - - hl = rz_cons_singleton ()->highlight; - if (hl) { - ohl = strdup (hl); - rz_cons_highlight (NULL); - } - fdold = rz_cons_singleton ()->fdout; - rz_cons_singleton ()->fdout = fd; - rz_cons_singleton ()->context->is_interactive = false; - rz_str_write (fd, "# r2 rdb project file\n"); - if (!core->bin->is_debugger && !rz_config_get_i (core->config, "asm.emu")) { - if (core->bin->file) { - char *fpath = rz_file_abspath (core->bin->file); - if (fpath) { - char *reopen = rz_str_newf ("\"o %s\"\n", fpath); - if (reopen) { - rz_str_write (fd, reopen); - free (reopen); - free (fpath); - } - } - } - - } - // Set file.path and file.lastpath to empty string to signal - // new behaviour to project load routine (see io maps below). - rz_config_set (core->config, "file.path", ""); - rz_config_set (core->config, "file.lastpath", ""); - if (opts & RZ_CORE_PRJ_EVAL) { - rz_str_write (fd, "# eval\n"); - rz_config_list (core->config, NULL, true); - rz_cons_flush (); - } - - if (opts & RZ_CORE_PRJ_FCNS) { - rz_str_write (fd, "# functions\n"); - rz_str_write (fd, "fs functions\n"); - rz_core_cmd (core, "afl*", 0); - rz_cons_flush (); - } - - if (opts & RZ_CORE_PRJ_FLAGS) { - rz_str_write (fd, "# flags\n"); - rz_flag_space_push (core->flags, NULL); - rz_flag_list (core->flags, true, NULL); - rz_flag_space_pop (core->flags); - rz_cons_flush (); - } - if (opts & RZ_CORE_PRJ_IO_MAPS && core->io && core->io->files) { - fdc = 3; - rz_id_storage_foreach (core->io->files, (RIDStorageForeachCb)store_files_and_maps, core); - rz_cons_flush (); - } - { - rz_core_cmd (core, "fz*", 0); - rz_cons_flush (); - } - if (opts & RZ_CORE_PRJ_META) { - rz_str_write (fd, "# meta\n"); - rz_meta_print_list_all (core->anal, RZ_META_TYPE_ANY, 1); - rz_cons_flush (); - rz_core_cmd (core, "fV*", 0); - rz_cons_flush (); - } - if (opts & RZ_CORE_PRJ_XREFS) { - rz_core_cmd (core, "ax*", 0); - rz_cons_flush (); - } - if (opts & RZ_CORE_PRJ_FLAGS) { - rz_core_cmd (core, "f.**", 0); - rz_cons_flush (); - } - if (opts & RZ_CORE_PRJ_DBG_BREAK) { - rz_core_cmd (core, "db*", 0); - rz_cons_flush (); - } - if (opts & RZ_CORE_PRJ_ANAL_HINTS) { - rz_core_cmd (core, "ah*", 0); - rz_cons_flush (); - } - if (opts & RZ_CORE_PRJ_ANAL_TYPES) { - rz_str_write (fd, "# types\n"); - rz_core_cmd (core, "t*", 0); - rz_cons_flush (); - } - if (opts & RZ_CORE_PRJ_ANAL_MACROS) { - rz_str_write (fd, "# macros\n"); - rz_core_cmd (core, "(*", 0); - rz_str_write (fd, "# aliases\n"); - rz_core_cmd (core, "$*", 0); - rz_cons_flush (); - } - if (opts & RZ_CORE_PRJ_ANAL_SEEK) { - rz_cons_printf ("# seek\n" - "s 0x%08"PFMT64x "\n", core->offset); - rz_cons_flush (); - } - - rz_cons_singleton ()->fdout = fdold; - rz_cons_singleton ()->context->is_interactive = true; - - if (ohl) { - rz_cons_highlight (ohl); - free (ohl); - } - - close (fd); - free (filename); - - return true; -} - -// TODO: rename to rz_core_project_save_script -RZ_API bool rz_core_project_save_script(RzCore *core, const char *file, int opts) { - return project_save_script (core, file, opts); -} - -#define TRANSITION 1 - -RZ_API bool rz_core_project_save(RzCore *core, const char *prjName) { - bool scr_null = false; - bool ret = true; - char *scriptPath, *prjDir; - SdbListIter *it; - SdbNs *ns; - char *oldPrjName = NULL; - - if (!prjName || !*prjName) { - return false; - } - scriptPath = get_project_script_path (core, prjName); - if (!scriptPath) { - eprintf ("Invalid project name '%s'\n", prjName); - return false; - } - if (rz_str_endswith (scriptPath, RZ_SYS_DIR "rc")) { - /* new project format */ - prjDir = rz_file_dirname (scriptPath); - } else { - prjDir = rz_str_newf ("%s.d", scriptPath); - } - if (rz_file_exists (scriptPath)) { - if (rz_file_is_directory (scriptPath)) { - eprintf ("WTF. rc is a directory?\n"); - } - if (rz_str_endswith (prjDir, ".d")) { - eprintf ("Upgrading project...\n"); -#if TRANSITION - rz_file_rm (scriptPath); - rz_sys_mkdirp (prjDir); - eprintf ("Please remove: rm -rf %s %s.d\n", prjName, prjName); - char *rc = rz_str_newf ("%s" RZ_SYS_DIR "rc", prjDir); - if (!rc) { - free (prjDir); - free (scriptPath); - return false; - } - free (scriptPath); - scriptPath = rc; - free (prjDir); - prjDir = rz_file_dirname (scriptPath); -#endif - } - } - if (!prjDir) { - prjDir = strdup (prjName); - } - if (!rz_file_exists (prjDir)) { - rz_sys_mkdirp (prjDir); - } - if (rz_config_get_i (core->config, "scr.null")) { - rz_config_set_i (core->config, "scr.null", false); - scr_null = true; - } - make_projects_directory (core); - - Sdb *rop_db = sdb_ns (core->sdb, "rop", false); - if (rop_db) { - /* set filepath for all the rop sub-dbs */ - ls_foreach (rop_db->ns, it, ns) { - char *rop_path = rz_str_newf ("%s" RZ_SYS_DIR "rop.d" RZ_SYS_DIR "%s", prjDir, ns->name); - sdb_file (ns->sdb, rop_path); - sdb_sync (ns->sdb); - free (rop_path); - } - } - - const char *oldPrjNameC = rz_config_get (core->config, "prj.name"); - if (oldPrjNameC) { - oldPrjName = strdup (oldPrjNameC); - } - rz_config_set (core->config, "prj.name", prjName); - if (rz_config_get_i (core->config, "prj.simple")) { - if (!simple_project_save_script (core, scriptPath, RZ_CORE_PRJ_ALL)) { - eprintf ("Cannot open '%s' for writing\n", prjName); - ret = false; - } - } else { - if (!project_save_script (core, scriptPath, RZ_CORE_PRJ_ALL)) { - eprintf ("Cannot open '%s' for writing\n", prjName); - ret = false; - } - } - - if (rz_config_get_i (core->config, "prj.files")) { - eprintf ("TODO: prj.files: support copying more than one file into the project directory\n"); - char *binFile = rz_core_project_info (core, prjName); - const char *binFileName = rz_file_basename (binFile); - char *prjBinDir = rz_str_newf ("%s" RZ_SYS_DIR "bin", prjDir); - char *prjBinFile = rz_str_newf ("%s" RZ_SYS_DIR "%s", prjBinDir, binFileName); - rz_sys_mkdirp (prjBinDir); - if (!rz_file_copy (binFile, prjBinFile)) { - eprintf ("Warning: Cannot copy '%s' into '%s'\n", binFile, prjBinFile); - } - free (prjBinFile); - free (prjBinDir); - free (binFile); - } - if (rz_config_get_i (core->config, "prj.git")) { - char *cwd = rz_sys_getdir (); - char *gitDir = rz_str_newf ("%s" RZ_SYS_DIR ".git", prjDir); - if (rz_sys_chdir (prjDir)) { - if (!rz_file_is_directory (gitDir)) { - rz_sys_cmd ("git init"); - } - rz_sys_cmd ("git add * ; git commit -a"); - } else { - eprintf ("Cannot chdir %s\n", prjDir); - } - rz_sys_chdir (cwd); - free (gitDir); - free (cwd); - } - if (rz_config_get_i (core->config, "prj.zip")) { - char *cwd = rz_sys_getdir (); - const char *prjName = rz_file_basename (prjDir); - if (rz_sys_chdir (prjDir)) { - if (!strchr (prjName, '\'')) { - rz_sys_chdir (".."); - rz_sys_cmdf ("rm -f '%s.zip'; zip -r '%s'.zip '%s'", - prjName, prjName, prjName); - } else { - eprintf ("Command injection attempt?\n"); - } - } else { - eprintf ("Cannot chdir %s\n", prjDir); - } - rz_sys_chdir (cwd); - free (cwd); - } - // LEAK : not always in heap free (prjName); - free (prjDir); - if (scr_null) { - rz_config_set_i (core->config, "scr.null", true); - } - if (!ret && oldPrjName) { - // reset prj.name on fail - rz_config_set (core->config, "prj.name", oldPrjName); - } - free (scriptPath); - free (oldPrjName); - return ret; -} - -RZ_API char *rz_core_project_notes_file(RzCore *core, const char *prjName) { - char *notes_txt; - const char *prjdir = rz_config_get (core->config, "dir.projects"); - char *prjpath = rz_file_abspath (prjdir); - notes_txt = rz_str_newf ("%s"RZ_SYS_DIR "%s"RZ_SYS_DIR "notes.txt", prjpath, prjName); - free (prjpath); - return notes_txt; -} - -RZ_API bool rz_core_project_load(RzCore *core, const char *prjName, const char *rcpath) { - const bool cfg_fortunes = rz_config_get_i (core->config, "cfg.fortunes"); - const bool scr_interactive = rz_cons_is_interactive (); - const bool scr_prompt = rz_config_get_i (core->config, "scr.prompt"); - (void) load_project_rop (core, prjName); - bool ret = rz_core_cmd_file (core, rcpath); - rz_config_set_i (core->config, "cfg.fortunes", cfg_fortunes); - rz_config_set_i (core->config, "scr.interactive", scr_interactive); - rz_config_set_i (core->config, "scr.prompt", scr_prompt); - rz_config_bump (core->config, "asm.arch"); + RzProjectErr ret = rz_project_load (core, prj, file, res); + sdb_free (prj); return ret; } diff --git a/librz/core/serialize_core.c b/librz/core/serialize_core.c new file mode 100644 index 0000000000..e4a067dc26 --- /dev/null +++ b/librz/core/serialize_core.c @@ -0,0 +1,64 @@ +/* radare - LGPL - Copyright 2019 - thestr4ng3r */ + +#include +#include + +#include "../util/serialize_helper.h" + +/* + * SDB Format: + * + * / + * /config => see config.c + * /flags => see flag.c + * /anal => see anal.c + * offset= + * blocksize= + */ + +RZ_API void rz_serialize_core_save(RZ_NONNULL Sdb *db, RZ_NONNULL RzCore *core) { + rz_serialize_config_save (sdb_ns (db, "config", true), core->config); + rz_serialize_flag_save (sdb_ns (db, "flags", true), core->flags); + rz_serialize_anal_save (sdb_ns (db, "anal", true), core->anal); + + char buf[0x20]; + if (snprintf (buf, sizeof (buf), "0x%"PFMT64x, core->offset) < 0) { + return; + } + sdb_set (db, "offset", buf, 0); + + if (snprintf (buf, sizeof (buf), "0x%"PFMT32x, core->blocksize) < 0) { + return; + } + sdb_set (db, "blocksize", buf, 0); +} + +RZ_API bool rz_serialize_core_load(RZ_NONNULL Sdb *db, RZ_NONNULL RzCore *core, RZ_NULLABLE RzSerializeResultInfo *res) { + Sdb *subdb; + +#define SUB(ns, call) SUB_DO(ns, call, return false;) + + SUB ("config", rz_serialize_config_load (subdb, core->config, res)); + SUB ("flags", rz_serialize_flag_load (subdb, core->flags, res)); + SUB ("anal", rz_serialize_anal_load (subdb, core->anal, res)); + + const char *str = sdb_get (db, "offset", 0); + if (!str || !*str) { + SERIALIZE_ERR ("missing offset in core"); + return false; + } + core->offset = strtoull (str, NULL, 0); + + str = sdb_get (db, "blocksize", 0); + if (!str || !*str) { + SERIALIZE_ERR ("missing blocksize in core"); + return false; + } + ut64 bs = strtoull (str, NULL, 0); + rz_core_block_size (core, (int)bs); + + // handled by config already: + // cfglog, cmdrepeat, cmdtimes + + return true; +} diff --git a/librz/debug/dsession.c b/librz/debug/dsession.c index 199c35eb29..12d8b980fc 100644 --- a/librz/debug/dsession.c +++ b/librz/debug/dsession.c @@ -395,7 +395,7 @@ static void serialize_checkpoints(Sdb *db, RzVector *checkpoints) { * "data":"", "perm":, "user":, "shared":} * * Notes: - * - This mostly follows r2db-style serialization and uses sdb_json as the parser. + * - This mostly follows rz-db-style serialization and uses sdb_json as the parser. */ RZ_API void rz_debug_session_serialize(RzDebugSession *session, Sdb *db) { sdb_num_set (db, "maxcnum", session->maxcnum, 0); diff --git a/librz/flag/Makefile b/librz/flag/Makefile index de61cc79b3..557238180d 100644 --- a/librz/flag/Makefile +++ b/librz/flag/Makefile @@ -2,6 +2,6 @@ include ../config.mk NAME=rz_flag RZ_DEPS=rz_util -OBJS=flag.o zones.o tags.o +OBJS=flag.o zones.o tags.o serialize_flag.o include ../rules.mk diff --git a/librz/flag/meson.build b/librz/flag/meson.build index 127f5897af..3f4b7227e0 100644 --- a/librz/flag/meson.build +++ b/librz/flag/meson.build @@ -1,7 +1,8 @@ rz_flag_sources = [ 'flag.c', 'tags.c', - 'zones.c' + 'zones.c', + 'serialize_flag.c' ] rz_flag = library('rz_flag', rz_flag_sources, diff --git a/librz/flag/serialize_flag.c b/librz/flag/serialize_flag.c new file mode 100644 index 0000000000..7738ead45d --- /dev/null +++ b/librz/flag/serialize_flag.c @@ -0,0 +1,329 @@ +/* radare - LGPL - Copyright 2019 - thestr4ng3r */ + +#include +#include + +#include "../util/serialize_helper.h" + +#if R_FLAG_ZONE_USE_SDB +#error "R_FLAG_ZONE_USE_SDB not supported by rz_serialize" +#endif + +/* + * SDB Format: + * + * / + * base= + * realnames= + * /spaces + * see spaces.c + * /tags + * like RzFlag.tags + * /zones + * ={"from":,"to":} + * /flags + * ={"realname":,"demangled":,"offset":,"size":,"space":,"color":,"comment":,"alias":} + * + */ + +RZ_API void rz_serialize_flag_zones_save(RZ_NONNULL Sdb *db, RZ_NONNULL RzList/**/ *zones) { + RzListIter *it; + RzFlagZoneItem *item; + rz_list_foreach (zones, it, item) { + PJ *j = pj_new (); + if (!j) { + return; + } + pj_o (j); + pj_kn (j, "from", item->from); + pj_kn (j, "to", item->to); + pj_end (j); + sdb_set (db, item->name, pj_string (j), 0); + pj_free (j); + } +} + +static bool zone_load_cb(void *user, const char *k, const char *v) { + RzList *list = user; + char *json_str = strdup (v); + if (!json_str) { + return true; + } + RJson *json = rz_json_parse (json_str); + if (!json) { + free (json_str); + return true; + } + if (json->type != RZ_JSON_OBJECT) { + goto beach; + } + const RJson *child; + RzFlagZoneItem *item = RZ_NEW0 (RzFlagZoneItem); + if (!item) { + goto beach; + } + item->name = strdup (k); + if (!item->name) { + free (item); + goto beach; + } + for (child = json->children.first; child; child = child->next) { + if (child->type != RZ_JSON_INTEGER) { + continue; + } + if (strcmp (child->key, "from") == 0) { + item->from = child->num.u_value; + } else if (strcmp (child->key, "to") == 0) { + item->to = child->num.u_value; + } + } + rz_list_append (list, item); +beach: + rz_json_free (json); + free (json_str); + return true; +} + +RZ_API bool rz_serialize_flag_zones_load(RZ_NONNULL Sdb *db, RZ_NONNULL RzList/**/ *zones, RZ_NULLABLE RzSerializeResultInfo *res) { + rz_return_val_if_fail (zones, false); + rz_list_purge (zones); + bool r = sdb_foreach (db, zone_load_cb, zones); + if (!r) { + SERIALIZE_ERR ("failed to parse a flag zone json"); + } + return r; +} + +static bool flag_save_cb(RzFlagItem *flag, void *user) { + Sdb *db = user; + PJ *j = pj_new (); + if (!j) { + return false; + } + pj_o (j); + if (flag->realname) { + pj_ks (j, "realname", flag->realname); + } + pj_kb (j, "demangled", flag->demangled); + pj_kn (j, "offset", flag->offset); + pj_kn (j, "size", flag->size); + if (flag->space) { + pj_ks (j, "space", flag->space->name); + } + if (flag->color) { + pj_ks (j, "color", flag->color); + } + if (flag->comment) { + pj_ks (j, "comment", flag->comment); + } + if (flag->alias) { + pj_ks (j, "alias", flag->alias); + } + pj_end (j); + sdb_set (db, flag->name, pj_string (j), 0); + pj_free (j); + return true; +} + +RZ_API void rz_serialize_flag_save(RZ_NONNULL Sdb *db, RZ_NONNULL RzFlag *flag) { + rz_serialize_spaces_save (sdb_ns (db, "spaces", true), &flag->spaces); + char buf[32]; + if (snprintf (buf, sizeof (buf), "%"PFMT64d, flag->base) < 0) { + return; + } + sdb_set (db, "base", buf, 0); + sdb_set (db, "realnames", flag->realnames ? "1" : "0", 0); + sdb_copy (flag->tags, sdb_ns (db, "tags", true)); + rz_serialize_flag_zones_save (sdb_ns (db, "zones", true), flag->zones); + rz_flag_foreach (flag, flag_save_cb, sdb_ns (db, "flags", true)); +} + +typedef enum { + FLAG_FIELD_REALNAME, + FLAG_FIELD_DEMANGLED, + FLAG_FIELD_OFFSET, + FLAG_FIELD_SIZE, + FLAG_FIELD_SPACE, + FLAG_FIELD_COLOR, + FLAG_FIELD_COMMENT, + FLAG_FIELD_ALIAS +} FlagField; + +typedef struct { + RzFlag *flag; + KeyParser *parser; +} FlagLoadCtx; + +static bool flag_load_cb(void *user, const char *k, const char *v) { + FlagLoadCtx *ctx = user; + + char *json_str = strdup (v); + if (!json_str) { + return true; + } + RJson *json = rz_json_parse (json_str); + if (!json || json->type != RZ_JSON_OBJECT) { + free (json_str); + return false; + } + + RzFlagItem proto = { 0 }; + bool offset_set = false; + bool size_set = false; + + KEY_PARSER_JSON (ctx->parser, json, child, { + case FLAG_FIELD_REALNAME: + if (child->type != RZ_JSON_STRING) { + break; + } + proto.realname = (char *)child->str_value; + break; + case FLAG_FIELD_DEMANGLED: + if (child->type != RZ_JSON_BOOLEAN) { + break; + } + proto.demangled = child->num.u_value != 0; + break; + case FLAG_FIELD_OFFSET: + if (child->type != RZ_JSON_INTEGER) { + break; + } + proto.offset = child->num.u_value; + offset_set = true; + break; + case FLAG_FIELD_SIZE: + if (child->type != RZ_JSON_INTEGER) { + break; + } + proto.size = child->num.u_value; + size_set = true; + break; + case FLAG_FIELD_SPACE: + if (child->type != RZ_JSON_STRING) { + break; + } + proto.space = rz_flag_space_get (ctx->flag, child->str_value); + break; + case FLAG_FIELD_COLOR: + if (child->type != RZ_JSON_STRING) { + break; + } + proto.color = (char *)child->str_value; + break; + case FLAG_FIELD_COMMENT: + if (child->type != RZ_JSON_STRING) { + break; + } + proto.comment = (char *)child->str_value; + break; + case FLAG_FIELD_ALIAS: + if (child->type != RZ_JSON_STRING) { + break; + } + proto.alias = (char *)child->str_value; + break; + default: + break; + }); + + bool res = true; + if (!offset_set || !size_set) { + res = false; + goto beach; + } + + RzFlagItem *item = rz_flag_set (ctx->flag, k, proto.offset - ctx->flag->base, proto.size); + if (proto.realname) { + rz_flag_item_set_realname (item, proto.realname); + } + item->demangled = proto.demangled; + item->space = proto.space; + if (proto.color) { + rz_flag_item_set_color (item, proto.color); + } + if (proto.comment) { + rz_flag_item_set_comment (item, proto.comment); + } + if(proto.alias) { + rz_flag_item_set_alias (item, proto.alias); + } + +beach: + rz_json_free (json); + free (json_str); + return res; +} + +static bool load_flags(RZ_NONNULL Sdb *flags_db, RZ_NONNULL RzFlag *flag) { + FlagLoadCtx ctx = { flag, key_parser_new () }; + if (!ctx.parser) { + return false; + } + key_parser_add (ctx.parser, "realname", FLAG_FIELD_REALNAME); + key_parser_add (ctx.parser, "demangled", FLAG_FIELD_DEMANGLED); + key_parser_add (ctx.parser, "offset", FLAG_FIELD_OFFSET); + key_parser_add (ctx.parser, "size", FLAG_FIELD_SIZE); + key_parser_add (ctx.parser, "space", FLAG_FIELD_SPACE); + key_parser_add (ctx.parser, "color", FLAG_FIELD_COLOR); + key_parser_add (ctx.parser, "comment", FLAG_FIELD_COMMENT); + key_parser_add (ctx.parser, "alias", FLAG_FIELD_ALIAS); + bool r = sdb_foreach (flags_db, flag_load_cb, &ctx); + key_parser_free (ctx.parser); + return r; +} + +RZ_API bool rz_serialize_flag_load(RZ_NONNULL Sdb *db, RZ_NONNULL RzFlag *flag, RZ_NULLABLE RzSerializeResultInfo *res) { + rz_flag_unset_all (flag); + + const char *str = sdb_const_get (db, "base", NULL); + if (!str) { + SERIALIZE_ERR ("flag base key is missing"); + return false; + } + flag->base = strtoll (str, NULL, 0); + + str = sdb_const_get (db, "realnames", 0); + if (!str) { + SERIALIZE_ERR ("flag realnames key is missing"); + return false; + } + flag->realnames = strtoul (str, NULL, 0) ? true : false; + + Sdb *spaces_db = sdb_ns (db, "spaces", false); + if (!spaces_db) { + SERIALIZE_ERR ("missing spaces namespace"); + return false; + } + if (!rz_serialize_spaces_load (spaces_db, &flag->spaces, false, res)) { + return false; + } + + Sdb *tags_db = sdb_ns (db, "tags", false); + if (!tags_db) { + SERIALIZE_ERR ("missing tags namespace"); + return false; + } + sdb_copy (tags_db, flag->tags); + + Sdb *zones_db = sdb_ns (db, "zones", false); + if (!zones_db) { + SERIALIZE_ERR ("missing zones namespace"); + return false; + } + rz_flag_zone_reset (flag); + if (!rz_serialize_flag_zones_load (zones_db, flag->zones, res)) { + return false; + } + + Sdb *flags_db = sdb_ns (db, "flags", false); + if (!flags_db) { + SERIALIZE_ERR ("missing flags sub-namespace"); + return false; + } + if (!load_flags (flags_db, flag)) { + SERIALIZE_ERR ("failed to parse a flag json"); + return false; + } + + return true; +} diff --git a/librz/include/rz_anal.h b/librz/include/rz_anal.h index d27abca754..4edc80202b 100644 --- a/librz/include/rz_anal.h +++ b/librz/include/rz_anal.h @@ -2122,6 +2122,54 @@ RZ_API void rz_anal_base_type_free(RzAnalBaseType *type); RZ_API RzAnalBaseType *rz_anal_base_type_new(RzAnalBaseTypeKind kind); RZ_API void rz_anal_dwarf_process_info(const RzAnal *anal, RzAnalDwarfContext *ctx); RZ_API void rz_anal_dwarf_integrate_functions(RzAnal *anal, RzFlag *flags, Sdb *dwarf_sdb); + +/* serialize */ +RZ_API void rz_serialize_anal_case_op_save(RZ_NONNULL PJ *j, RZ_NONNULL RzAnalCaseOp *op); +RZ_API void rz_serialize_anal_switch_op_save(RZ_NONNULL PJ *j, RZ_NONNULL RzAnalSwitchOp *op); +RZ_API RzAnalSwitchOp *rz_serialize_anal_switch_op_load(RZ_NONNULL const RJson *json); + +typedef void *RSerializeAnalDiffParser; +RZ_API RSerializeAnalDiffParser rz_serialize_anal_diff_parser_new(void); +RZ_API void rz_serialize_anal_diff_parser_free(RSerializeAnalDiffParser parser); +RZ_API RZ_NULLABLE RzAnalDiff *rz_serialize_anal_diff_load(RZ_NONNULL RSerializeAnalDiffParser parser, RZ_NONNULL const RJson *json); +RZ_API void rz_serialize_anal_diff_save(RZ_NONNULL PJ *j, RZ_NONNULL RzAnalDiff *diff); +RZ_API void rz_serialize_anal_blocks_save(RZ_NONNULL Sdb *db, RZ_NONNULL RzAnal *anal); + +/** + * RzAnal must not contain any blocks when calling this function! + * All loaded blocks will have a ref of 1 after this function and should be unrefd once after loading functions. + */ +RZ_API bool rz_serialize_anal_blocks_load(RZ_NONNULL Sdb *db, RZ_NONNULL RzAnal *anal, RSerializeAnalDiffParser diff_parser, RZ_NULLABLE RzSerializeResultInfo *res); + +typedef void *RSerializeAnalVarParser; +RZ_API RSerializeAnalVarParser rz_serialize_anal_var_parser_new(void); +RZ_API void rz_serialize_anal_var_parser_free(RSerializeAnalVarParser parser); +RZ_API RZ_NULLABLE RzAnalVar *rz_serialize_anal_var_load(RZ_NONNULL RzAnalFunction *fcn, RZ_NONNULL RSerializeAnalVarParser parser, RZ_NONNULL const RJson *json); + +RZ_API void rz_serialize_anal_functions_save(RZ_NONNULL Sdb *db, RZ_NONNULL RzAnal *anal); +RZ_API bool rz_serialize_anal_functions_load(RZ_NONNULL Sdb *db, RZ_NONNULL RzAnal *anal, RSerializeAnalDiffParser diff_parser, RZ_NULLABLE RzSerializeResultInfo *res); +RZ_API void rz_serialize_anal_xrefs_save(RZ_NONNULL Sdb *db, RZ_NONNULL RzAnal *anal); +RZ_API bool rz_serialize_anal_xrefs_load(RZ_NONNULL Sdb *db, RZ_NONNULL RzAnal *anal, RZ_NULLABLE RzSerializeResultInfo *res); +RZ_API void rz_serialize_anal_meta_save(RZ_NONNULL Sdb *db, RZ_NONNULL RzAnal *anal); +RZ_API bool rz_serialize_anal_meta_load(RZ_NONNULL Sdb *db, RZ_NONNULL RzAnal *anal, RZ_NULLABLE RzSerializeResultInfo *res); +RZ_API void rz_serialize_anal_hints_save(RZ_NONNULL Sdb *db, RZ_NONNULL RzAnal *anal); +RZ_API bool rz_serialize_anal_hints_load(RZ_NONNULL Sdb *db, RZ_NONNULL RzAnal *anal, RZ_NULLABLE RzSerializeResultInfo *res); +RZ_API void rz_serialize_anal_classes_save(RZ_NONNULL Sdb *db, RZ_NONNULL RzAnal *anal); +RZ_API bool rz_serialize_anal_classes_load(RZ_NONNULL Sdb *db, RZ_NONNULL RzAnal *anal, RZ_NULLABLE RzSerializeResultInfo *res); +RZ_API void rz_serialize_anal_types_save(RZ_NONNULL Sdb *db, RZ_NONNULL RzAnal *anal); +RZ_API bool rz_serialize_anal_types_load(RZ_NONNULL Sdb *db, RZ_NONNULL RzAnal *anal, RZ_NULLABLE RzSerializeResultInfo *res); +RZ_API void rz_serialize_anal_sign_save(RZ_NONNULL Sdb *db, RZ_NONNULL RzAnal *anal); +RZ_API bool rz_serialize_anal_sign_load(RZ_NONNULL Sdb *db, RZ_NONNULL RzAnal *anal, RZ_NULLABLE RzSerializeResultInfo *res); +RZ_API void rz_serialize_anal_imports_save(RZ_NONNULL Sdb *db, RZ_NONNULL RzAnal *anal); +RZ_API bool rz_serialize_anal_imports_load(RZ_NONNULL Sdb *db, RZ_NONNULL RzAnal *anal, RZ_NULLABLE RzSerializeResultInfo *res); +RZ_API void rz_serialize_anal_pin_save(RZ_NONNULL Sdb *db, RZ_NONNULL RzAnal *anal); +RZ_API bool rz_serialize_anal_pin_load(RZ_NONNULL Sdb *db, RZ_NONNULL RzAnal *anal, RZ_NULLABLE RzSerializeResultInfo *res); +RZ_API void rz_serialize_anal_cc_save(RZ_NONNULL Sdb *db, RZ_NONNULL RzAnal *anal); +RZ_API bool rz_serialize_anal_cc_load(RZ_NONNULL Sdb *db, RZ_NONNULL RzAnal *anal, RZ_NULLABLE RzSerializeResultInfo *res); + +RZ_API void rz_serialize_anal_save(RZ_NONNULL Sdb *db, RZ_NONNULL RzAnal *anal); +RZ_API bool rz_serialize_anal_load(RZ_NONNULL Sdb *db, RZ_NONNULL RzAnal *anal, RZ_NULLABLE RzSerializeResultInfo *res); + /* plugin pointers */ extern RzAnalPlugin rz_anal_plugin_null; extern RzAnalPlugin rz_anal_plugin_6502; diff --git a/librz/include/rz_config.h b/librz/include/rz_config.h index 414912cb2d..5c3686f617 100644 --- a/librz/include/rz_config.h +++ b/librz/include/rz_config.h @@ -3,6 +3,7 @@ #include "rz_types.h" #include "rz_util.h" +#include "rz_util/rz_serialize.h" #ifdef __cplusplus extern "C" { @@ -113,6 +114,11 @@ static inline bool rz_config_node_is_ro(RzConfigNode *node) { static inline bool rz_config_node_is_str(RzConfigNode *node) { return node->flags & CN_STR; } + +/* serialize */ + +RZ_API void rz_serialize_config_save(RZ_NONNULL Sdb *db, RZ_NONNULL RzConfig *config); +RZ_API bool rz_serialize_config_load(RZ_NONNULL Sdb *db, RZ_NONNULL RzConfig *config, RZ_NULLABLE RzSerializeResultInfo *res); #endif #ifdef __cplusplus diff --git a/librz/include/rz_core.h b/librz/include/rz_core.h index b5e7b5fc1a..959728ce24 100644 --- a/librz/include/rz_core.h +++ b/librz/include/rz_core.h @@ -162,7 +162,6 @@ typedef enum rz_core_autocomplete_types_t { RZ_CORE_AUTOCMPLT_FCN, RZ_CORE_AUTOCMPLT_ZIGN, RZ_CORE_AUTOCMPLT_EVAL, - RZ_CORE_AUTOCMPLT_PRJT, RZ_CORE_AUTOCMPLT_MINS, RZ_CORE_AUTOCMPLT_BRKP, RZ_CORE_AUTOCMPLT_MACR, @@ -522,11 +521,6 @@ RZ_API void rz_core_fortune_list_types(void); RZ_API void rz_core_fortune_list(RzCore *core); RZ_API void rz_core_fortune_print_random(RzCore *core); -/* project */ -RZ_API bool rz_core_project_load(RzCore *core, const char *prjfile, const char *rcfile); -RZ_API RzThread *rz_core_project_load_bg(RzCore *core, const char *prjfile, const char *rcfile); -RZ_API void rz_core_project_execute_cmds(RzCore *core, const char *prjfile); - #define RZ_CORE_FOREIGN_ADDR -1 RZ_API int rz_core_yank(RzCore *core, ut64 addr, int len); RZ_API int rz_core_yank_string(RzCore *core, ut64 addr, int maxlen); @@ -674,15 +668,6 @@ RZ_API int rz_core_zdiff(RzCore *c, RzCore *c2); RZ_API int rz_core_gdiff(RzCore *core1, RzCore *core2); RZ_API int rz_core_gdiff_fcn(RzCore *c, ut64 addr, ut64 addr2); -RZ_API bool rz_core_project_open(RzCore *core, const char *file, bool thready); -RZ_API int rz_core_project_cat(RzCore *core, const char *name); -RZ_API int rz_core_project_delete(RzCore *core, const char *prjfile); -RZ_API int rz_core_project_list(RzCore *core, int mode); -RZ_API bool rz_core_project_save_script(RzCore *core, const char *file, int opts); -RZ_API bool rz_core_project_save(RzCore *core, const char *file); -RZ_API char *rz_core_project_info(RzCore *core, const char *file); -RZ_API char *rz_core_project_notes_file (RzCore *core, const char *file); - RZ_API char *rz_core_sysenv_begin(RzCore *core, const char *cmd); RZ_API void rz_core_sysenv_end(RzCore *core, const char *cmd); @@ -959,6 +944,11 @@ RZ_API void rz_core_annotated_code_print(RAnnotatedCode *code, RzVector *line_of */ RZ_API void rz_core_annotated_code_print_comment_cmds(RAnnotatedCode *code); +/* serialize */ + +RZ_API void rz_serialize_core_save(RZ_NONNULL Sdb *db, RZ_NONNULL RzCore *core); +RZ_API bool rz_serialize_core_load(RZ_NONNULL Sdb *db, RZ_NONNULL RzCore *core, RZ_NULLABLE RzSerializeResultInfo *res); + #endif #ifdef __cplusplus diff --git a/librz/include/rz_flag.h b/librz/include/rz_flag.h index a46a5e14d7..c5d5e33cb7 100644 --- a/librz/include/rz_flag.h +++ b/librz/include/rz_flag.h @@ -5,6 +5,7 @@ #include #include #include +#include #ifdef __cplusplus extern "C" { @@ -196,6 +197,13 @@ RZ_API bool rz_flag_zone_list(RzFlag *fz, int mode); RZ_API bool rz_flag_zone_reset(RzFlag *f); RZ_API RzList *rz_flag_zone_barlist(RzFlag *f, ut64 from, ut64 bsize, int rows); +/* serialize */ + +RZ_API void rz_serialize_flag_zones_save(RZ_NONNULL Sdb *db, RZ_NONNULL RzList/**/ *zones); +RZ_API bool rz_serialize_flag_zones_load(RZ_NONNULL Sdb *db, RZ_NONNULL RzList/**/ *zones, RZ_NULLABLE RzSerializeResultInfo *res); +RZ_API void rz_serialize_flag_save(RZ_NONNULL Sdb *db, RZ_NONNULL RzFlag *flag); +RZ_API bool rz_serialize_flag_load(RZ_NONNULL Sdb *db, RZ_NONNULL RzFlag *flag, RZ_NULLABLE RzSerializeResultInfo *res); + #endif #ifdef __cplusplus diff --git a/librz/include/rz_project.h b/librz/include/rz_project.h new file mode 100644 index 0000000000..4fab6f5777 --- /dev/null +++ b/librz/include/rz_project.h @@ -0,0 +1,35 @@ + +#ifndef R2_PROJECT_H +#define R2_PROJECT_H + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef Sdb RzProject; + +typedef enum rz_project_err { + RZ_PROJECT_ERR_SUCCESS, + RZ_PROJECT_ERR_FILE, + RZ_PROJECT_ERR_INVALID_TYPE, + RZ_PROJECT_ERR_INVALID_VERSION, + RZ_PROJECT_ERR_NEWER_VERSION, + RZ_PROJECT_ERR_INVALID_CONTENTS, + RZ_PROJECT_ERR_UNKNOWN +} RzProjectErr; + +RZ_API RZ_NONNULL const char *rz_project_err_message(RzProjectErr err); +RZ_API RzProjectErr rz_project_save(RzCore *core, RzProject *prj); +RZ_API RzProjectErr rz_project_save_file(RzCore *core, const char *file); +RZ_API RzProjectErr rz_project_load(RzCore *core, RzProject *prj, RZ_NULLABLE const char *file, RzSerializeResultInfo *res); +RZ_API RzProjectErr rz_project_load_file(RzCore *core, const char *file, RzSerializeResultInfo *res); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/librz/include/rz_util/rz_serialize.h b/librz/include/rz_util/rz_serialize.h new file mode 100644 index 0000000000..5b0b23c36a --- /dev/null +++ b/librz/include/rz_util/rz_serialize.h @@ -0,0 +1,13 @@ +/* rizin - LGPL - Copyright 2020 - thestr4ng3r */ + +#ifndef RZ_SERIALIZE_H +#define RZ_SERIALIZE_H + +#include +#include + +typedef RzList RzSerializeResultInfo; +static inline RzSerializeResultInfo *rz_serialize_result_info_new(void) { return rz_list_newf (free); } +static inline void rz_serialize_result_info_free(RzSerializeResultInfo *info) { rz_list_free (info); } + +#endif //RZ_SERIALIZE_H diff --git a/librz/include/rz_util/rz_spaces.h b/librz/include/rz_util/rz_spaces.h index c4a6741975..dbafc19545 100644 --- a/librz/include/rz_util/rz_spaces.h +++ b/librz/include/rz_util/rz_spaces.h @@ -4,6 +4,7 @@ #define RZ_SPACES_MAX 512 #include "rz_util.h" +#include "rz_serialize.h" #ifdef __cplusplus extern "C" { @@ -103,6 +104,14 @@ typedef RBIter RSpaceIter; #define rz_spaces_foreach(sp, it, s) \ rz_rbtree_foreach ((sp)->spaces, (it), (s), RSpace, rb) +/* serialize */ + +RZ_API void rz_serialize_spaces_save(RZ_NONNULL Sdb *db, RZ_NONNULL RSpaces *spaces); +/** + * @param load_name whether to overwrite the name in spaces with the value from db + */ +RZ_API bool rz_serialize_spaces_load(RZ_NONNULL Sdb *db, RZ_NONNULL RSpaces *spaces, bool load_name, RZ_NULLABLE RzSerializeResultInfo *res); + #ifdef __cplusplus } #endif diff --git a/librz/io/Makefile b/librz/io/Makefile index aec10b32dd..ec8cef6122 100644 --- a/librz/io/Makefile +++ b/librz/io/Makefile @@ -8,7 +8,7 @@ RZ_DEPS+=rz_cons RZ_DEPS+=rz_crypto STATIC_OBJS=$(subst ..,p/..,$(subst io_,p/io_,$(STATIC_OBJ))) OBJS=${STATIC_OBJS} -OBJS+=io.o io_plugin.o io_map.o io_desc.o io_cache.o p_cache.o undo.o ioutils.o io_fd.o +OBJS+=io.o io_plugin.o io_map.o io_desc.o io_cache.o p_cache.o undo.o ioutils.o io_fd.o serialize_io.o CFLAGS+=-Wall -DRZ_PLUGIN_INCORE diff --git a/librz/io/meson.build b/librz/io/meson.build index 46a99a8fc1..b88638316e 100644 --- a/librz/io/meson.build +++ b/librz/io/meson.build @@ -8,6 +8,7 @@ rz_io_sources = [ 'ioutils.c', 'undo.c', 'p_cache.c', + 'serialize_io.c', 'p/io_ar.c', 'p/io_fd.c', 'p/io_bfdbg.c', diff --git a/librz/io/serialize_io.c b/librz/io/serialize_io.c new file mode 100644 index 0000000000..fad3ccda2f --- /dev/null +++ b/librz/io/serialize_io.c @@ -0,0 +1,113 @@ +/* rizin - LGPL - Copyright 2020 - thestr4ng3r */ + +#include +#include + +#include "../util/serialize_helper.h" + +#include + +/* + * + * SDB Format: + * + * / + * /files + * ={perm:, uri:, name:, referer?:} + * /pcache + * .={cached:, data:} + */ + + +typedef struct { + int fd; + int perm; + char *uri; + char *name; + char *referer; + HtUP *cache; + void *data; + struct rz_io_plugin_t *plugin; + RzIO *io; +} RzIODescasd; + +typedef struct { + int fd; + Sdb *db; +} PCacheSaveCtx; + +static bool pcache_save_cb(void *user, const ut64 k, const void *v) { + PCacheSaveCtx *ctx = user; + const RzIODescCache *cache = v; + char key[0x30]; + if (snprintf (key, sizeof (key), "%d.0x%"PFMT64x, ctx->fd, k) < 0) { + return false; + } + char val[RZ_IO_DESC_CACHE_SIZE * 4 + 1]; + rz_base64_encode (val, cache->cdata, RZ_IO_DESC_CACHE_SIZE); + return true; +} + +static bool file_save_cb(void *user, void *data, ut32 id) { + Sdb *db = user; + RzIODesc *desc = (RzIODesc *)data; + + char key[0x20]; + if (snprintf (key, sizeof (key), "%d", desc->fd) < 0) { + return false; + } + + PJ *j = pj_new (); + if (!j) { + return false; + } + pj_o (j); + + pj_ki (j, "perm", desc->perm); + // obsz is irrelevant (never written, always 0) + pj_ks (j, "uri", desc->uri); + pj_ks (j, "name", desc->name); + if (desc->referer) { + pj_ks (j, "referer", desc->referer); + } + // TODO: plugin + + pj_end (j); + sdb_set (db, key, pj_string (j), 0); + pj_free (j); + + if (desc->cache->count) { + PCacheSaveCtx ctx = { + .fd = desc->fd, + .db = sdb_ns (db, "pcache", true) + }; + ht_up_foreach (desc->cache, pcache_save_cb, &ctx); + } + return true; +} + +RZ_API void rz_serialize_io_files_save(RZ_NONNULL Sdb *db, RZ_NONNULL RzIO *io) { + sdb_ns (db, "pcache", true); + rz_id_storage_foreach (io->files, file_save_cb, db); +} + +RZ_API bool rz_serialize_io_files_load(RZ_NONNULL Sdb *db, RZ_NONNULL RzIO *io, RZ_NULLABLE RzSerializeResultInfo *res) { + // TODO + return true; +} + +RZ_API void rz_serialize_io_save(RZ_NONNULL Sdb *db, RZ_NONNULL RzIO *io) { + rz_serialize_io_files_save (sdb_ns (db, "files", true), io); +} + +RZ_API bool rz_serialize_io_load(RZ_NONNULL Sdb *db, RZ_NONNULL RzIO *io, RZ_NULLABLE RzSerializeResultInfo *res) { + // TODO: purge RzIO? + bool ret = false; + Sdb *subdb; +#define SUB(ns, call) SUB_DO(ns, call, goto beach;) + SUB ("files", rz_serialize_io_files_load (subdb, io, res)); +#undef SUB + ret = true; +beach: + return ret; +} diff --git a/librz/main/rizin.c b/librz/main/rizin.c index 81baf3b516..3d78fbbe14 100644 --- a/librz/main/rizin.c +++ b/librz/main/rizin.c @@ -10,6 +10,7 @@ #endif #include +#include static bool is_valid_gdb_file(RzCoreFile *fh) { RzIODesc *d = fh && fh->core ? rz_io_desc_get (fh->core->io, fh->fd) : NULL; @@ -121,7 +122,7 @@ static int main_help(int line) { " -q quiet mode (no prompt) and quit after -i\n" " -qq quit after running all -c and -i\n" " -Q quiet mode (no prompt) and quit faster (quickLeak=true)\n" - " -p [prj] use project, list if no arg, load if no file\n" + " -p [p.rzdb] load project file\n" " -P [file] apply rapatch file and quit\n" " -r [rz_run] specify rz_run profile to load (same as -e dbg.profile=X)\n" " -R [rrz_testule] specify custom rz_run directive\n" @@ -337,7 +338,6 @@ RZ_API int rz_main_rizin(int argc, const char **argv) { int zflag = 0; bool do_connect = false; bool fullfile = false; - int has_project; bool zerosep = false; int help = 0; enum { LOAD_BIN_ALL, LOAD_BIN_NOTHING, LOAD_BIN_STRUCTURES_ONLY } load_bin = LOAD_BIN_ALL; @@ -422,12 +422,6 @@ RZ_API int rz_main_rizin(int argc, const char **argv) { r->io->envprofile = envprofile; rz_core_task_sync_begin (&r->tasks); - if (argc == 2 && !strcmp (argv[1], "-p")) { - rz_core_project_list (r, 0); - rz_cons_flush (); - LISTS_FREE (); - return 0; - } // HACK TO PERMIT '#!/usr/bin/rz - -i' hashbangs if (argc > 2 && !strcmp (argv[1], "-") && !strcmp (argv[2], "-i")) { argv[1] = argv[0]; @@ -578,13 +572,7 @@ RZ_API int rz_main_rizin(int argc, const char **argv) { } break; case 'p': - if (!strcmp (opt.arg, "?")) { - rz_core_project_list (r, 0); - rz_cons_flush (); - LISTS_FREE (); - return 0; - } - rz_config_set (r->config, "prj.name", opt.arg); + prj = *opt.arg ? opt.arg : NULL; break; case 'P': if (RZ_STR_ISEMPTY (opt.arg)) { @@ -804,10 +792,18 @@ RZ_API int rz_main_rizin(int argc, const char **argv) { rz_bin_force_plugin (r->bin, forcebin); - prj = rz_config_get (r->config, "prj.name"); - if (prj && *prj) { - rz_core_project_open (r, prj, false); - rz_config_set (r->config, "bin.strings", "false"); + if (prj) { + RzSerializeResultInfo *res = rz_serialize_result_info_new (); + RzProjectErr err = rz_project_load_file (r, prj, res); + if (err != RZ_PROJECT_ERR_SUCCESS) { + eprintf ("Failed to load project: %s\n", rz_project_err_message (err)); + RzListIter *it; + char *s; + rz_list_foreach (res, it, s) { + eprintf (" %s\n", s); + } + } + rz_serialize_result_info_free (res); } if (do_connect) { @@ -923,7 +919,7 @@ RZ_API int rz_main_rizin(int argc, const char **argv) { LISTS_FREE (); return 1; } - } else if (strcmp (argv[opt.ind - 1], "--") && !(rz_config_get (r->config, "prj.name") && rz_config_get (r->config, "prj.name")[0]) ) { + } else if (strcmp (argv[opt.ind - 1], "--")) { if (debug) { if (asmbits) { rz_config_set (r->config, "asm.bits", asmbits); @@ -1118,25 +1114,11 @@ RZ_API int rz_main_rizin(int argc, const char **argv) { } } } else { - const char *prj = rz_config_get (r->config, "prj.name"); - if (prj && *prj) { - pfile = rz_core_project_info (r, prj); - if (pfile) { - if (!fh) { - fh = rz_core_file_open (r, pfile, perms, mapaddr); - } - // load_bin = LOAD_BIN_NOTHING; - load_bin = LOAD_BIN_STRUCTURES_ONLY; - } else { - eprintf ("Cannot find project file\n"); - } - } else { - if (fh) { - iod = r->io ? rz_io_desc_get (r->io, fh->fd) : NULL; - if (iod) { - perms = iod->perm; - rz_io_map_new (r->io, iod->fd, perms, 0LL, 0LL, rz_io_desc_size (iod)); - } + if (fh) { + iod = r->io ? rz_io_desc_get (r->io, fh->fd) : NULL; + if (iod) { + perms = iod->perm; + rz_io_map_new (r->io, iod->fd, perms, 0LL, 0LL, rz_io_desc_size (iod)); } } } @@ -1280,11 +1262,7 @@ RZ_API int rz_main_rizin(int argc, const char **argv) { if (iod && !strstr (iod->uri, "://")) { const char *npath; char *path = strdup (rz_config_get (r->config, "file.path")); - has_project = rz_core_project_open (r, rz_config_get (r->config, "prj.name"), false); iod = r->io ? rz_io_desc_get (r->io, fh->fd) : NULL; - if (has_project) { - rz_config_set (r->config, "bin.strings", "false"); - } npath = rz_config_get (r->config, "file.path"); if (!quiet && path && *path && npath && strcmp (path, npath)) { eprintf ("WARNING: file.path change: %s => %s\n", path, npath); @@ -1448,15 +1426,15 @@ RZ_API int rz_main_rizin(int argc, const char **argv) { } } - prj = rz_config_get (r->config, "prj.name"); + prj = rz_config_get (r->config, "prj.file"); if (no_question_save) { if (prj && *prj && y_save_project){ - rz_core_project_save (r, prj); + rz_project_save_file (r, prj); // TODO: check error } } else { question = rz_str_newf ("Do you want to save the '%s' project? (Y/n)", prj); if (prj && *prj && rz_cons_yesno ('y', "%s", question)) { - rz_core_project_save (r, prj); + rz_project_save_file (r, prj); // TODO: check error } free (question); } diff --git a/librz/meson.build b/librz/meson.build index effe1cfaa7..c2f445195f 100644 --- a/librz/meson.build +++ b/librz/meson.build @@ -345,6 +345,7 @@ include_files =[ 'include/rz_magic.h', 'include/rz_parse.h', 'include/rz_pdb.h', + 'include/rz_project.h', 'include/rz_qrcode.h', 'include/rz_regex.h', 'include/rz_reg.h', @@ -399,6 +400,7 @@ rz_util_files = [ 'include/rz_util/rz_rbtree.h', 'include/rz_util/rz_intervaltree.h', 'include/rz_util/rz_sandbox.h', + 'include/rz_util/rz_serialize.h', 'include/rz_util/rz_signal.h', 'include/rz_util/rz_spaces.h', 'include/rz_util/rz_stack.h', diff --git a/librz/util/Makefile b/librz/util/Makefile index ff10fd056d..f29dffd1a9 100644 --- a/librz/util/Makefile +++ b/librz/util/Makefile @@ -23,7 +23,7 @@ OBJS+=udiff.o bdiff.o stack.o queue.o tree.o idpool.o assert.o OBJS+=punycode.o pkcs7.o x509.o asn1.o astr.o json_parser.o json_indent.o skiplist.o OBJS+=pj.o rbtree.o intervaltree.o qrcode.o vector.o str_constpool.o str_trim.o OBJS+=ascii_table.o protobuf.o graph_drawable.o -OBJS+=annotated_code.o +OBJS+=annotated_code.o serialize_spaces.o ifeq (${HAVE_LIB_GMP},1) OBJS+=big-gmp.o diff --git a/librz/util/meson.build b/librz/util/meson.build index dc9dddc38f..1d9e2f405b 100644 --- a/librz/util/meson.build +++ b/librz/util/meson.build @@ -82,7 +82,8 @@ rz_util_sources = [ 'regex/regcomp.c', 'regex/regexec.c', 'regex/regerror.c', - 'annotated_code.c' + 'annotated_code.c', + 'serialize_spaces.c' ] rz_util_deps = [ldl, mth, spp_dep, pth, utl, sdb_dep, zlib_dep, platform_deps] diff --git a/librz/util/sdb.mk b/librz/util/sdb.mk index b4933aa6ea..f0e55a62bd 100644 --- a/librz/util/sdb.mk +++ b/librz/util/sdb.mk @@ -28,6 +28,7 @@ SDB_OBJS+=num.o SDB_OBJS+=util.o SDB_OBJS+=journal.o SDB_OBJS+=diff.o +SDB_OBJS+=text.o SDBOBJS=$(addprefix ${SDBPATH},${SDB_OBJS}) diff --git a/librz/util/serialize_helper.h b/librz/util/serialize_helper.h new file mode 100644 index 0000000000..d89981d563 --- /dev/null +++ b/librz/util/serialize_helper.h @@ -0,0 +1,51 @@ + +#ifndef RZ_SERIALIZE_UTIL_H +#define RZ_SERIALIZE_UTIL_H + +#include + +#define SERIALIZE_ERR(...) do { if(res) { rz_list_push (res, rz_str_newf(__VA_ARGS__)); } } while(0) + +// Hashtable-based key parser to prevent strcmp chains +typedef HtPP KeyParser; + +static inline KeyParser *key_parser_new(void) { + return ht_pp_new0 (); +} + +static inline void key_parser_free(KeyParser *parser) { + ht_pp_free (parser); +} + +static inline void key_parser_add(KeyParser *parser, const char *key, int val) { + ht_pp_insert (parser, key, (void *)(size_t)val); +} + +#define KEY_PARSER_UNKNOWN -1 + +#define KEY_PARSER_SWITCH(parser, key) \ + bool key_parser_found = false; \ + int key_parser_v = (int)(size_t)ht_pp_find (parser, key, &key_parser_found); \ + if (!key_parser_found) { \ + key_parser_v = KEY_PARSER_UNKNOWN; \ + } \ + switch (key_parser_v) + +#define KEY_PARSER_JSON(parser, json, child, body) \ + if(json->type == RZ_JSON_OBJECT) { \ + for(RJson *child = json->children.first; child; child = child->next) { \ + KEY_PARSER_SWITCH (parser, child->key) { body } \ + } \ + } + +#define SUB_DO(ns, call, rip) \ + subdb = sdb_ns (db, ns, false); \ + if (!subdb) { \ + SERIALIZE_ERR ("missing " ns " namespace"); \ + rip \ + } \ + if (!(call)) { \ + rip \ + } \ + +#endif //RZ_SERIALIZE_UTIL_H diff --git a/librz/util/serialize_spaces.c b/librz/util/serialize_spaces.c new file mode 100644 index 0000000000..c618b5fa52 --- /dev/null +++ b/librz/util/serialize_spaces.c @@ -0,0 +1,110 @@ + +#include +#include + +#include "../util/serialize_helper.h" + +/* + * SDB Format: + * + * / + * name= + * spacestack=[,,, ] (json) + * /spaces + * ="s" + * ... + */ + +#define KEY_NAME "name" +#define KEY_SPACESTACK "spacestack" +#define KEY_SPACES "spaces" + +RZ_API void rz_serialize_spaces_save(RZ_NONNULL Sdb *db, RZ_NONNULL RSpaces *spaces) { + sdb_set (db, KEY_NAME, spaces->name, 0); + + PJ *j = pj_new (); + if(!j) { + return; + } + pj_a (j); + RzListIter *iter; + char *spacename; + rz_list_foreach (spaces->spacestack, iter, spacename) { + pj_s (j, spacename); + } + pj_s (j, spaces->current ? spaces->current->name : "*"); // push current manually, will be popped on load + pj_end (j); + sdb_set (db, KEY_SPACESTACK, pj_string (j), 0); + pj_free (j); + + Sdb *db_spaces = sdb_ns (db, KEY_SPACES, true); + RBIter rbiter; + RSpace *space; + rz_rbtree_foreach (spaces->spaces, rbiter, space, RSpace, rb) { + sdb_set (db_spaces, space->name, "s", 0); + } +} + +static bool foreach_space_cb(void *user, const char *k, const char *v) { + RSpaces *spaces = user; + rz_spaces_add (spaces, k); + return true; +} + +RZ_API bool rz_serialize_spaces_load(RZ_NONNULL Sdb *db, RZ_NONNULL RSpaces *spaces, bool load_name, RZ_NULLABLE RzSerializeResultInfo *res) { + if (load_name) { + char *old_name = (char *)spaces->name; + spaces->name = sdb_get (db, KEY_NAME, NULL); + if (!spaces->name) { + spaces->name = old_name; + SERIALIZE_ERR ("failed to get spaces name from db"); + return false; + } + free (old_name); + } + + rz_spaces_purge (spaces); + + Sdb *db_spaces = sdb_ns (db, KEY_SPACES, false); + if (!db_spaces) { + SERIALIZE_ERR ("failed to get spaces sub-namespace"); + return false; + } + sdb_foreach (db_spaces, foreach_space_cb, spaces); + + char *stack_json_str = sdb_get (db, KEY_SPACESTACK, NULL); + if (!stack_json_str) { + SERIALIZE_ERR ("spacestack is missing"); + return false; + } + + bool ret = true; + RJson *stack_json = rz_json_parse (stack_json_str); + if (!stack_json) { + SERIALIZE_ERR ("failed to parse stackspace json"); + ret = false; + goto beach; + } + if (stack_json->type != RZ_JSON_ARRAY) { + SERIALIZE_ERR ("stackspace json is not an array"); + ret = false; + goto beach; + } + RJson *stack_element; + for (stack_element = stack_json->children.first; stack_element; stack_element = stack_element->next) { + if (stack_element->type != RZ_JSON_STRING) { + SERIALIZE_ERR ("stackspace element is not a string"); + ret = false; + goto beach; + } + RSpace *space = rz_spaces_get (spaces, stack_element->str_value); + rz_list_append (spaces->spacestack, space ? space->name : "*"); + } + + rz_spaces_pop (spaces); // current is the top stack element, pop it + +beach: + rz_json_free (stack_json); + free (stack_json_str); + return ret; +} diff --git a/meson.build b/meson.build index 90e69e0740..63024e96f6 100644 --- a/meson.build +++ b/meson.build @@ -469,6 +469,7 @@ sdb_files = [ 'shlr/sdb/src/query.c', 'shlr/sdb/src/sdb.c', 'shlr/sdb/src/sdbht.c', + 'shlr/sdb/src/text.c', 'shlr/sdb/src/util.c' ] diff --git a/shlr/sdb b/shlr/sdb index f0902e0ad5..807d74f048 160000 --- a/shlr/sdb +++ b/shlr/sdb @@ -1 +1 @@ -Subproject commit f0902e0ad52c89468d0dfea1e99598a15aa023ad +Subproject commit 807d74f0488cb010501e8e908938a747e1c8945d diff --git a/test/db/anal/x86_64 b/test/db/anal/x86_64 index 890049ea2e..04db7dab73 100644 --- a/test/db/anal/x86_64 +++ b/test/db/anal/x86_64 @@ -1067,20 +1067,13 @@ e asm.bits=64 pd 1 Cf 1 x pd 1 -Ps temp_proj Cf- pd 1 -Ps temp_proj -Po temp_proj -pd 1 EOF EXPECT=< /dev/null -C* -EOF -EXPECT=< /dev/null -CC* -EOF -EXPECT=< /dev/null -Pc testw~afF -rm .tmp/testw -EOF -EXPECT=< /dev/null -cat .tmp/testexp/rc | grep base64 -rmdir -p .tmp/testexp -EOF -EXPECT=< /dev/null -C- @ 0x00404890 -C- @ 0x00404800 -Po project.rz -cat .tmp/testset/rc | grep CC -rmdir -p .tmp/testset -EOF -EXPECT=< /dev/null -axq -EOF -EXPECT=< 0x00000001 UNKNOWN -0x00000000 -> 0x00000001 UNKNOWN -EOF -RUN - -NAME=Import mapped project -FILE=malloc://512 -CMDS=< /dev/null -pu 3 @0xc000 -EOF -EXPECT=< /dev/null -Po emu -CC- -om `o~[0]` 0 -pd 1 -rm .tmp/emu -EOF -EXPECT=< 0x2000000 -EOF -RUN - -NAME=Variables in project file -FILE=bins/efi/bootia32.efi -CMDS=< /dev/null -Pc var~ImageHandle -Pc var~blahblah -rm .tmp/var -EOF -EXPECT=< /dev/null -Pc types~?link.,symlink,unlink -Pc types~?zoby -EOF -EXPECT=< /dev/null -Pc breakpoints~?0x1234567 -EOF -EXPECT=< /dev/null -Pc vmarks~fV -Pd vmarks > /dev/null -EOF -EXPECT=< /dev/null -Pc custom_types~?char ** -"td struct Foo { int bar; int x; int y; };" -Ps custom_types > /dev/null -Pc custom_types~?char ** -Pc custom_types~?Foo -Pd custom_types > /dev/null -EOF -EXPECT=< /dev/null -t- char ** -t- Foo -tk~?char ** -tk~?Foo -Po custom_types > /dev/null -tk~?char ** -tk~?Foo -Pd custom_types > /dev/null -EOF -EXPECT=< /dev/null -Po ulumulu > /dev/null -o*~? -EOF -EXPECT=< /dev/null -Po blub > /dev/null -i~format -EOF -EXPECT=< /dev/null -Po hand > /dev/null -om*~? -om*~[2] -EOF -EXPECT=< /dev/null -Pd cannot > /dev/null -Pl~cannot - -EOF -EXPECT=< /dev/null -P- erase > /dev/null -Pl~erase -EOF -EXPECT=< /dev/null -Pd anesthetize > /dev/null -Pl~blah -ls -l `e dir.projects`~anesthetize -EOF -EXPECT=< /dev/null -P- lightbulb > /dev/null -Pl~lightbulb -ls -l `e dir.projects`~lightbulb -EOF -EXPECT=< /dev/null -Po sun > /dev/null -e asm.bits -EOF -EXPECT=< /dev/null -e prj.name= -Po named_prj > /dev/null -e prj.name -rm .tmp/named_prj -EOF -EXPECT=< /dev/null -Po stupid > /dev/null -Ps stupid > /dev/null -Po stupid > /dev/null -e prj.name -EOF -EXPECT=< /dev/null -Po dream > /dev/null -axq -EOF -EXPECT=< 0x00000009 CALL -EOF -RUN - -NAME=Reopening projects bin info loading -FILE=bins/elf/analysis/main -BROKEN=1 -CMDS=< /dev/null -Po bininfo -i~? -ie~? -ii~? -EOF -EXPECT=< /dev/null -e dir.projects = .tmp/ -s main > /dev/null -CC test -Ps lapa > /dev/null -Po lapa -pd 1 ~test -rm .tmp/ -EOF -EXPECT=<addr, 0x1337, "created addr"); mu_assert_eq (block->size, 42, "created size"); @@ -35,7 +32,7 @@ bool test_r_anal_block_create() { mu_assert_eq (blocks_count (anal), 1, "count after create"); RzAnalBlock *block2 = rz_anal_create_block (anal, 0x133f, 100); - assert_invariants (anal); + assert_block_invariants (anal); mu_assert ("created block (overlap)", block2); mu_assert_eq (block2->addr, 0x133f, "created addr"); mu_assert_eq (block2->size, 100, "created size"); @@ -43,14 +40,14 @@ bool test_r_anal_block_create() { mu_assert_eq (blocks_count (anal), 2, "count after create"); RzAnalBlock *block3 = rz_anal_create_block (anal, 0x1337, 5); - assert_invariants (anal); + assert_block_invariants (anal); mu_assert ("no double create on same start", !block3); mu_assert_eq (blocks_count (anal), 2, "count after failed create"); rz_anal_block_unref (block); rz_anal_block_unref (block2); - assert_leaks (anal); + assert_block_leaks (anal); rz_anal_free (anal); mu_end; } @@ -69,10 +66,10 @@ bool test_r_anal_block_contains() { bool test_r_anal_block_split() { RzAnal *anal = rz_anal_new (); - assert_invariants (anal); + assert_block_invariants (anal); RzAnalBlock *block = rz_anal_create_block (anal, 0x1337, 42); - assert_invariants (anal); + assert_block_invariants (anal); mu_assert_eq (blocks_count (anal), 1, "count after create"); block->jump = 0xdeadbeef; block->fail = 0xc0ffee; @@ -84,14 +81,14 @@ bool test_r_anal_block_split() { rz_anal_bb_set_offset (block, 4, 30); RzAnalBlock *second = rz_anal_block_split (block, 0x1337); - assert_invariants (anal); + assert_block_invariants (anal); mu_assert_ptreq (second, block, "nop split on first addr"); mu_assert_eq (blocks_count (anal), 1, "count after nop split"); mu_assert_eq (block->ref, 2, "ref after nop split"); rz_anal_block_unref (block); second = rz_anal_block_split (block, 0x1339); - assert_invariants (anal); + assert_block_invariants (anal); mu_assert_ptrneq (second, block, "non-nop split"); mu_assert_eq (blocks_count (anal), 2, "count after non-nop split"); @@ -117,27 +114,27 @@ bool test_r_anal_block_split() { rz_anal_block_unref (block); rz_anal_block_unref (second); - assert_leaks (anal); + assert_block_leaks (anal); rz_anal_free (anal); mu_end; } bool test_r_anal_block_split_in_function() { RzAnal *anal = rz_anal_new (); - assert_invariants (anal); + assert_block_invariants (anal); RzAnalFunction *fcn = rz_anal_create_function (anal, "bbowner", 0x1337, 0, NULL); - assert_invariants (anal); + assert_block_invariants (anal); RzAnalBlock *block = rz_anal_create_block (anal, 0x1337, 42); - assert_invariants (anal); + assert_block_invariants (anal); mu_assert_eq (blocks_count (anal), 1, "count after create"); rz_anal_function_add_block (fcn, block); - assert_invariants (anal); + assert_block_invariants (anal); mu_assert_eq (block->ref, 2, "block refs after adding to function"); RzAnalBlock *second = rz_anal_block_split (block, 0x1339); - assert_invariants (anal); + assert_block_invariants (anal); mu_assert_ptrneq (second, block, "non-nop split"); mu_assert_eq (blocks_count (anal), 2, "count after non-nop split"); mu_assert_eq (block->ref, 2, "first block refs after adding to function"); @@ -151,18 +148,18 @@ bool test_r_anal_block_split_in_function() { rz_anal_block_unref (block); rz_anal_block_unref (second); - assert_leaks (anal); + assert_block_leaks (anal); rz_anal_free (anal); mu_end; } bool test_r_anal_block_merge() { RzAnal *anal = rz_anal_new (); - assert_invariants (anal); + assert_block_invariants (anal); RzAnalBlock *first = rz_anal_create_block (anal, 0x1337, 42); RzAnalBlock *second = rz_anal_create_block (anal, 0x1337 + 42, 624); - assert_invariants (anal); + assert_block_invariants (anal); mu_assert_eq (blocks_count (anal), 2, "count after create"); second->jump = 0xdeadbeef; second->fail = 0xc0ffee; @@ -179,7 +176,7 @@ bool test_r_anal_block_merge() { rz_anal_bb_set_offset (second, 3, 30); bool success = rz_anal_block_merge (first, second); - assert_invariants (anal); + assert_block_invariants (anal); mu_assert ("merge success", success); mu_assert_eq (blocks_count (anal), 1, "count after merge"); mu_assert_eq (first->addr, 0x1337, "addr after merge"); @@ -199,29 +196,29 @@ bool test_r_anal_block_merge() { rz_anal_block_unref (first); // second must be already freed by the merge! - assert_invariants (anal); + assert_block_invariants (anal); rz_anal_free (anal); mu_end; } bool test_r_anal_block_merge_in_function() { RzAnal *anal = rz_anal_new (); - assert_invariants (anal); + assert_block_invariants (anal); RzAnalFunction *fcn = rz_anal_create_function (anal, "bbowner", 0x1337, 0, NULL); RzAnalBlock *first = rz_anal_create_block (anal, 0x1337, 42); RzAnalBlock *second = rz_anal_create_block (anal, 0x1337 + 42, 624); - assert_invariants (anal); + assert_block_invariants (anal); mu_assert_eq (blocks_count (anal), 2, "count after create"); rz_anal_function_add_block (fcn, first); - assert_invariants (anal); + assert_block_invariants (anal); rz_anal_function_add_block (fcn, second); - assert_invariants (anal); + assert_block_invariants (anal); bool success = rz_anal_block_merge (first, second); - assert_invariants (anal); + assert_block_invariants (anal); mu_assert ("merge success", success); mu_assert_eq (blocks_count (anal), 1, "count after merge"); mu_assert_eq (rz_list_length (fcn->bbs), 1, "fcn bbs after merge"); @@ -232,29 +229,29 @@ bool test_r_anal_block_merge_in_function() { rz_anal_block_unref (first); // second must be already freed by the merge! - assert_invariants (anal); + assert_block_invariants (anal); rz_anal_free (anal); mu_end; } bool test_r_anal_block_delete() { RzAnal *anal = rz_anal_new (); - assert_invariants (anal); + assert_block_invariants (anal); RzAnalFunction *fcn = rz_anal_create_function (anal, "bbowner", 0x1337, 0, NULL); RzAnalBlock *block = rz_anal_create_block (anal, 0x1337, 42); - assert_invariants (anal); + assert_block_invariants (anal); mu_assert_eq (blocks_count (anal), 1, "count after create"); rz_anal_function_add_block (fcn, block); - assert_invariants (anal); + assert_block_invariants (anal); mu_assert_eq (block->ref, 2, "refs after adding"); mu_assert_eq (rz_list_length (fcn->bbs), 1, "fcn bbs after add"); mu_assert_eq (rz_list_length (block->fcns), 1, "bb fcns after add"); rz_anal_delete_block (block); - assert_invariants (anal); + assert_block_invariants (anal); mu_assert_eq (block->ref, 1, "refs after delete"); mu_assert_eq (rz_list_length (fcn->bbs), 0, "fcn bbs after delete"); mu_assert_eq (rz_list_length (block->fcns), 0, "bb fcns after delete"); @@ -267,38 +264,38 @@ bool test_r_anal_block_delete() { bool test_r_anal_block_set_size() { RzAnal *anal = rz_anal_new (); - assert_invariants (anal); + assert_block_invariants (anal); RzAnalFunction *fcn = rz_anal_create_function (anal, "bbowner", 0x1337, 0, NULL); RzAnalBlock *block = rz_anal_create_block (anal, 0x1337, 42); - assert_invariants (anal); + assert_block_invariants (anal); rz_anal_function_add_block (fcn, block); - assert_invariants (anal); + assert_block_invariants (anal); rz_anal_block_set_size (block, 300); - assert_invariants (anal); + assert_block_invariants (anal); mu_assert_eq (block->size, 300, "size after set_size"); RzAnalBlock *second = rz_anal_create_block (anal, 0x1337+300, 100); - assert_invariants (anal); + assert_block_invariants (anal); rz_anal_function_add_block (fcn, block); - assert_invariants (anal); + assert_block_invariants (anal); rz_anal_function_linear_size (fcn); // trigger lazy calculation of min/max cache - assert_invariants (anal); + assert_block_invariants (anal); rz_anal_block_set_size (second, 500); - assert_invariants (anal); + assert_block_invariants (anal); mu_assert_eq (second->size, 500, "size after set_size"); rz_anal_block_set_size (block, 80); - assert_invariants (anal); + assert_block_invariants (anal); mu_assert_eq (block->size, 80, "size after set_size"); rz_anal_block_unref (block); rz_anal_block_unref (second); - assert_invariants (anal); + assert_block_invariants (anal); rz_anal_free (anal); mu_end; @@ -306,64 +303,64 @@ bool test_r_anal_block_set_size() { bool test_r_anal_block_relocate() { RzAnal *anal = rz_anal_new (); - assert_invariants (anal); + assert_block_invariants (anal); RzAnalFunction *fcn = rz_anal_create_function (anal, "bbowner", 0x1337, 0, NULL); RzAnalBlock *block = rz_anal_create_block (anal, 0x1337, 42); - assert_invariants (anal); + assert_block_invariants (anal); rz_anal_function_add_block (fcn, block); - assert_invariants (anal); + assert_block_invariants (anal); rz_anal_function_linear_size (fcn); // trigger lazy calculation of min/max cache - assert_invariants (anal); + assert_block_invariants (anal); bool success = rz_anal_block_relocate (block, 0x200, 0x100); mu_assert ("relocate success", success); - assert_invariants (anal); + assert_block_invariants (anal); mu_assert_eq (block->addr, 0x200, "addr after relocate"); mu_assert_eq (block->size, 0x100, "size after relocate"); RzAnalBlock *second = rz_anal_create_block (anal, 0x1337+300, 100); - assert_invariants (anal); + assert_block_invariants (anal); rz_anal_function_add_block (fcn, second); - assert_invariants (anal); + assert_block_invariants (anal); success = rz_anal_block_relocate (second, 0x400, 0x123); mu_assert ("relocate success", success); - assert_invariants (anal); + assert_block_invariants (anal); mu_assert_eq (second->addr, 0x400, "addr after relocate"); mu_assert_eq (second->size, 0x123, "size after relocate"); rz_anal_function_linear_size (fcn); // trigger lazy calculation of min/max cache - assert_invariants (anal); + assert_block_invariants (anal); success = rz_anal_block_relocate (block, 0x400, 0x333); mu_assert ("relocate fail on same addr", !success); - assert_invariants (anal); + assert_block_invariants (anal); mu_assert_eq (block->addr, 0x200, "addr after failed relocate"); mu_assert_eq (block->size, 0x100, "size after failed relocate"); rz_anal_function_linear_size (fcn); // trigger lazy calculation of min/max cache - assert_invariants (anal); + assert_block_invariants (anal); // jump after the other block success = rz_anal_block_relocate (block, 0x500, 0x333); mu_assert ("relocate success", success); - assert_invariants (anal); + assert_block_invariants (anal); mu_assert_eq (block->addr, 0x500, "addr after failed relocate"); mu_assert_eq (block->size, 0x333, "size after failed relocate"); rz_anal_function_linear_size (fcn); // trigger lazy calculation of min/max cache - assert_invariants (anal); + assert_block_invariants (anal); // jump before the other block success = rz_anal_block_relocate (block, 0x10, 0x333); mu_assert ("relocate success", success); - assert_invariants (anal); + assert_block_invariants (anal); mu_assert_eq (block->addr, 0x10, "addr after failed relocate"); mu_assert_eq (block->size, 0x333, "size after failed relocate"); rz_anal_block_unref (block); rz_anal_block_unref (second); - assert_invariants (anal); + assert_block_invariants (anal); rz_anal_free (anal); mu_end; @@ -371,7 +368,7 @@ bool test_r_anal_block_relocate() { bool test_r_anal_block_query() { RzAnal *anal = rz_anal_new (); - assert_invariants (anal); + assert_block_invariants (anal); #define N 200 #define MAXSIZE 0x300 @@ -382,7 +379,7 @@ bool test_r_anal_block_query() { size_t i; for (i = 0; i < N; i++) { blocks[i] = rz_anal_create_block (anal, rand () % SPACE, rand () % MAXSIZE); // may return null on duplicates - assert_invariants (anal); + assert_block_invariants (anal); } // -- @@ -475,7 +472,7 @@ bool test_r_anal_block_query() { rz_anal_block_unref (blocks[i]); } - assert_leaks (anal); + assert_block_leaks (anal); rz_anal_free (anal); mu_end; } @@ -488,7 +485,7 @@ bool addr_list_cb(ut64 addr, void *user) { bool test_r_anal_block_successors() { RzAnal *anal = rz_anal_new (); - assert_invariants (anal); + assert_block_invariants (anal); RzAnalBlock *blocks[10]; blocks[0] = rz_anal_create_block (anal, 0x10, 0x10); @@ -501,7 +498,7 @@ bool test_r_anal_block_successors() { blocks[7] = rz_anal_create_block (anal, 0x140, 0x10); blocks[8] = rz_anal_create_block (anal, 0xa0, 0x10); blocks[9] = rz_anal_create_block (anal, 0xc0, 0x10); - assert_invariants (anal); + assert_block_invariants (anal); blocks[0]->jump = 0x30; blocks[0]->fail = 0x50; @@ -562,7 +559,7 @@ bool test_r_anal_block_successors() { rz_anal_block_unref (blocks[i]); } - assert_leaks (anal); + assert_block_leaks (anal); rz_anal_free (anal); mu_end; } @@ -571,7 +568,7 @@ bool test_r_anal_block_automerge() { size_t i; for (i = 0; i < SAMPLES; i++) { RzAnal *anal = rz_anal_new (); - assert_invariants (anal); + assert_block_invariants (anal); RzAnalBlock *a = rz_anal_create_block (anal, 0x100, 0x10); @@ -624,7 +621,7 @@ bool test_r_anal_block_automerge() { rz_list_free (all_blocks); rz_anal_block_automerge (shuffled_blocks); - assert_invariants (anal); + assert_block_invariants (anal); //mu_assert_eq (rz_list_length (shuffled_blocks), 4, "length after automerge"); mu_assert ("remaining blocks a", rz_list_contains (shuffled_blocks, a)); mu_assert ("remaining blocks b", rz_list_contains (shuffled_blocks, b)); @@ -638,8 +635,8 @@ bool test_r_anal_block_automerge() { } rz_list_free (shuffled_blocks); - assert_invariants (anal); - assert_leaks (anal); + assert_block_invariants (anal); + assert_block_leaks (anal); rz_anal_free (anal); } mu_end; @@ -647,7 +644,7 @@ bool test_r_anal_block_automerge() { bool test_r_anal_block_chop_noreturn(void) { RzAnal *anal = rz_anal_new (); - assert_invariants (anal); + assert_block_invariants (anal); RzAnalBlock *a = rz_anal_create_block (anal, 0x100, 0x10); RzAnalBlock *b = rz_anal_create_block (anal, 0x110, 0x10); @@ -665,7 +662,7 @@ bool test_r_anal_block_chop_noreturn(void) { rz_anal_block_chop_noreturn (b, 0x111); - assert_invariants (anal); + assert_block_invariants (anal); mu_end; } diff --git a/test/unit/test_anal_block_invars.inl b/test/unit/test_anal_block_invars.inl index 63f13bcc53..f022dc361e 100644 --- a/test/unit/test_anal_block_invars.inl +++ b/test/unit/test_anal_block_invars.inl @@ -68,3 +68,6 @@ static bool block_check_leaks(RzAnal *anal) { } return true; } + +#define assert_block_invariants(anal) do { if (!block_check_invariants (anal)) { return false; } } while (0) +#define assert_block_leaks(anal) do { if (!block_check_leaks (anal)) { return false; } } while (0) diff --git a/test/unit/test_sdb.h b/test/unit/test_sdb.h index 010898848c..d4e3c43ffc 100644 --- a/test/unit/test_sdb.h +++ b/test/unit/test_sdb.h @@ -1,6 +1,9 @@ #ifndef TEST_SDB_H #define TEST_SDB_H +#define PERTURBATOR "\\,\";] [}{'" +#define PERTURBATOR_JSON "\\\\,\\\";] [}{'" + static void diff_cb(const SdbDiff *diff, void *user) { char buf[2048]; if (sdb_diff_format (buf, sizeof (buf), diff) < 0) { @@ -17,4 +20,9 @@ static inline void print_sdb(Sdb *sdb) { #define assert_sdb_eq(actual, expected, msg) mu_assert ((msg), sdb_diff (expected, actual, diff_cb, NULL)); +#define assert_streq_null(actual, expected, message) do { \ + mu_assert (message, (!(actual)) == (!(expected))); \ + if (expected) { mu_assert_streq (actual, expected, message); } \ +} while(0) + #endif //R2DB_TEST_UTILS_H diff --git a/test/unit/test_serialize_anal.c b/test/unit/test_serialize_anal.c new file mode 100644 index 0000000000..19f3535263 --- /dev/null +++ b/test/unit/test_serialize_anal.c @@ -0,0 +1,1847 @@ + +#include +#include +#include "minunit.h" +#include "test_sdb.h" + +#include "test_anal_block_invars.inl" + +bool test_anal_diff_save() { + RzAnalDiff *diff = rz_anal_diff_new (); + + PJ *j = pj_new (); + rz_serialize_anal_diff_save (j, diff); + mu_assert_streq (pj_string (j), "{}", "empty diff"); + pj_free (j); + + diff->name = strdup (PERTURBATOR_JSON); + diff->dist = 42.3; + diff->addr = 0x1337; + diff->type = RZ_ANAL_DIFF_TYPE_MATCH; + diff->size = 0x4242; + j = pj_new (); + rz_serialize_anal_diff_save (j, diff); + mu_assert_streq (pj_string (j), "{\"type\":\"m\",\"addr\":4919,\"dist\":42.300000,\"name\":\"\\\\\\\\,\\\\\\\";] [}{'\",\"size\":16962}", "full diff"); + pj_free (j); + + diff->type = RZ_ANAL_DIFF_TYPE_UNMATCH; + j = pj_new (); + rz_serialize_anal_diff_save (j, diff); + mu_assert_streq (pj_string (j), "{\"type\":\"u\",\"addr\":4919,\"dist\":42.300000,\"name\":\"\\\\\\\\,\\\\\\\";] [}{'\",\"size\":16962}", "full unmatch diff"); + pj_free (j); + + rz_anal_diff_free (diff); + mu_end; +} + +bool test_anal_diff_load() { + RSerializeAnalDiffParser parser = rz_serialize_anal_diff_parser_new (); + + char *str = strdup ("{}"); + RJson *json = rz_json_parse (str); + RzAnalDiff *diff = rz_serialize_anal_diff_load (parser, json); + rz_json_free (json); + free (str); + mu_assert_notnull (diff, "diff"); + mu_assert_eq (diff->addr, UT64_MAX, "addr"); + mu_assert_eq (diff->size, 0, "size"); + mu_assert_eq (diff->type, RZ_ANAL_DIFF_TYPE_NULL, "type"); + mu_assert_eq (diff->dist, 0.0, "dist"); + mu_assert_null (diff->name, "name"); + rz_anal_diff_free (diff); + + str = strdup ("{\"type\":\"m\",\"addr\":4919,\"dist\":42.300000,\"name\":\"\\\\\\\\,\\\\\\\";] [}{'\",\"size\":16962}"); + json = rz_json_parse (str); + diff = rz_serialize_anal_diff_load (parser, json); + rz_json_free (json); + free (str); + mu_assert_notnull (diff, "diff"); + mu_assert_eq (diff->addr, 0x1337, "addr"); + mu_assert_eq (diff->size, 0x4242, "size"); + mu_assert_eq (diff->type, RZ_ANAL_DIFF_TYPE_MATCH, "type"); + mu_assert_eq (diff->dist, 42.3, "dist"); + mu_assert_streq (diff->name, PERTURBATOR_JSON, "name"); + rz_anal_diff_free (diff); + + str = strdup ("{\"type\":\"u\",\"addr\":4919,\"dist\":42.300000,\"name\":\"\\\\\\\\,\\\\\\\";] [}{'\",\"size\":16962}"); + json = rz_json_parse (str); + diff = rz_serialize_anal_diff_load (parser, json); + rz_json_free (json); + free (str); + mu_assert_notnull (diff, "diff"); + mu_assert_eq (diff->addr, 0x1337, "addr"); + mu_assert_eq (diff->size, 0x4242, "size"); + mu_assert_eq (diff->type, RZ_ANAL_DIFF_TYPE_UNMATCH, "type"); + mu_assert_eq (diff->dist, 42.3, "dist"); + mu_assert_streq (diff->name, PERTURBATOR_JSON, "name"); + rz_anal_diff_free (diff); + + rz_serialize_anal_diff_parser_free (parser); + mu_end; +} + +bool test_anal_switch_op_save() { + RzAnalSwitchOp *op = rz_anal_switch_op_new (1337, 42, 45, 46); + + PJ *j = pj_new (); + rz_serialize_anal_switch_op_save (j, op); + mu_assert_streq (pj_string (j), "{\"addr\":1337,\"min\":42,\"max\":45,\"def\":46,\"cases\":[]}", "empty switch"); + pj_free (j); + + rz_anal_switch_op_add_case (op, 1339, 42, 0xdead); + rz_anal_switch_op_add_case (op, 1340, 43, 0xbeef); + j = pj_new (); + rz_serialize_anal_switch_op_save (j, op); + mu_assert_streq (pj_string (j), "{\"addr\":1337,\"min\":42,\"max\":45,\"def\":46,\"cases\":[{\"addr\":1339,\"jump\":57005,\"value\":42},{\"addr\":1340,\"jump\":48879,\"value\":43}]}", "full switch"); + pj_free (j); + + rz_anal_switch_op_free (op); + mu_end; +} + +bool test_anal_switch_op_load() { + char *str = strdup ("{\"addr\":1337,\"min\":42,\"max\":45,\"def\":46,\"cases\":[]}"); + RJson *json = rz_json_parse (str); + RzAnalSwitchOp *sop = rz_serialize_anal_switch_op_load (json); + rz_json_free (json); + free (str); + mu_assert_notnull (sop, "sop"); + mu_assert_eq (sop->addr, 1337, "addr"); + mu_assert_eq (sop->min_val, 42, "min val"); + mu_assert_eq (sop->max_val, 45, "max val"); + mu_assert_eq (sop->def_val, 46, "def val"); + mu_assert_true (rz_list_empty (sop->cases), "no cases"); + rz_anal_switch_op_free (sop); + + str = strdup("{\"addr\":1337,\"min\":42,\"max\":45,\"def\":46,\"cases\":[{\"addr\":1339,\"jump\":57005,\"value\":42},{\"addr\":1340,\"jump\":48879,\"value\":43}]}"); + json = rz_json_parse (str); + sop = rz_serialize_anal_switch_op_load (json); + rz_json_free (json); + free (str); + mu_assert_notnull (sop, "sop"); + mu_assert_eq (sop->addr, 1337, "addr"); + mu_assert_eq (sop->min_val, 42, "min val"); + mu_assert_eq (sop->max_val, 45, "max val"); + mu_assert_eq (sop->def_val, 46, "def val"); + mu_assert_eq (rz_list_length (sop->cases), 2, "cases count"); + RzAnalCaseOp *cop = rz_list_get_n (sop->cases, 0); + mu_assert_eq (cop->addr, 1339, "addr"); + mu_assert_eq (cop->jump, 0xdead, "jump"); + mu_assert_eq (cop->value, 42, "value"); + cop = rz_list_get_n (sop->cases, 1); + mu_assert_eq (cop->addr, 1340, "addr"); + mu_assert_eq (cop->jump, 0xbeef, "jump"); + mu_assert_eq (cop->value, 43, "value"); + rz_anal_switch_op_free (sop); + + mu_end; +} + +Sdb *blocks_ref_db() { + Sdb *db = sdb_new0 (); + sdb_set (db, "0x539", "{\"size\":42}", 0); + sdb_set (db, "0x4d2", "{\"size\":32,\"jump\":4883,\"fail\":16915,\"traced\":true,\"folded\":true,\"colorize\":16711680,\"fingerprint\":\"AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8=\",\"diff\":{\"addr\":54123},\"switch_op\":{\"addr\":49232,\"min\":3,\"max\":5,\"def\":7,\"cases\":[]},\"ninstr\":3,\"op_pos\":[4,7],\"stackptr\":43,\"parent_stackptr\":57,\"cmpval\":3735928559,\"cmpreg\":\"rax\"}", 0); + return db; +} + +bool test_anal_block_save() { + RzAnal *anal = rz_anal_new (); + + rz_anal_create_block (anal, 1337, 42); + + RzAnalBlock *block = rz_anal_create_block (anal, 1234, 32); + block->jump = 0x1313; + block->fail = 0x4213; + block->traced = true; + block->folded = true; + block->colorize = 0xff0000; + block->fingerprint = malloc (block->size); + ut8 v; + for (v = 0; v < block->size; v++) { + block->fingerprint[v] = v; + } + block->diff = rz_anal_diff_new (); + block->diff->addr = 54123; + block->switch_op = rz_anal_switch_op_new (49232, 3, 5, 7); + block->ninstr = 3; + mu_assert ("enough size for op_pos test", block->op_pos_size >= 2); // if this fails, just change the test + block->op_pos[0] = 4; + block->op_pos[1] = 7; + block->stackptr = 43; + block->parent_stackptr = 57; + block->cmpval = 0xdeadbeef; + block->cmpreg = "rax"; + + Sdb *db = sdb_new0 (); + rz_serialize_anal_blocks_save (db, anal); + + Sdb *expected = blocks_ref_db (); + assert_sdb_eq (db, expected, "anal blocks save"); + sdb_free (db); + sdb_free (expected); + rz_anal_free (anal); + mu_end; +} + +bool test_anal_block_load() { + RzAnal *anal = rz_anal_new (); + + Sdb *db = blocks_ref_db (); + RSerializeAnalDiffParser diff_parser = rz_serialize_anal_diff_parser_new (); + bool succ = rz_serialize_anal_blocks_load (db, anal, diff_parser, NULL); + mu_assert ("load success", succ); + + RzAnalBlock *a = NULL; + RzAnalBlock *b = NULL; + size_t count = 0; + + RBIter iter; + RzAnalBlock *block; + rz_rbtree_foreach (anal->bb_tree, iter, block, RzAnalBlock, _rb) { + count++; + if (block->addr == 1337) { + a = block; + } else if (block->addr == 1234) { + b = block; + } + } + mu_assert_eq (count, 2, "loaded blocks count"); + + mu_assert_notnull (a, "block a"); + mu_assert_eq (a->size, 42, "size"); + mu_assert_eq (a->jump, UT64_MAX, "jump"); + mu_assert_eq (a->fail, UT64_MAX, "fail"); + mu_assert ("traced", !a->traced); + mu_assert ("folded", !a->folded); + mu_assert_eq (a->colorize, 0, "colorize"); + mu_assert_null (a->fingerprint, "fingerprint"); + mu_assert_null (a->diff, "diff"); + mu_assert_null (a->switch_op, "switch op"); + mu_assert_eq (a->ninstr, 0, "ninstr"); + mu_assert_eq (a->stackptr, 0, "stackptr"); + mu_assert_eq (a->parent_stackptr, INT_MAX, "parent_stackptr"); + mu_assert_eq (a->cmpval, UT64_MAX, "cmpval"); + mu_assert_null (a->cmpreg, "cmpreg"); + + mu_assert_notnull (b, "block b"); + mu_assert_eq (b->size, 32, "size"); + mu_assert_eq (b->jump, 0x1313, "jump"); + mu_assert_eq (b->fail, 0x4213, "fail"); + mu_assert ("traced", b->traced); + mu_assert ("folded", b->folded); + mu_assert_eq (b->colorize, 0xff0000, "colorize"); + mu_assert_notnull (b->fingerprint, "fingerprint"); + mu_assert_memeq (b->fingerprint, + (const ut8 *)"\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10" + "\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", 32, "fingerprint"); + mu_assert_notnull (b->diff, "diff"); + mu_assert_eq (b->diff->addr, 54123, "diff addr"); // diff is covered in detail by its own tests + mu_assert_notnull (b->switch_op, "switch op"); + mu_assert_eq (b->switch_op->addr, 49232, "switch op addr"); // switch_op is covered in detail by its own tests + mu_assert_eq (b->ninstr, 3, "ninstr"); + mu_assert ("op_pos_size", b->op_pos_size >= b->ninstr - 1); + mu_assert_eq (b->op_pos[0], 4, "op_pos[0]"); + mu_assert_eq (b->op_pos[1], 7, "op_pos[1]"); + mu_assert_eq (b->stackptr, 43, "stackptr"); + mu_assert_eq (b->parent_stackptr, 57, "parent_stackptr"); + mu_assert_eq (b->cmpval, 0xdeadbeef, "cmpval"); + mu_assert_ptreq (b->cmpreg, rz_str_constpool_get (&anal->constpool, "rax"), "cmpreg from pool"); + + rz_anal_free (anal); + anal = rz_anal_new (); + // This could lead to a buffer overflow if unchecked: + sdb_set (db, "0x539", "{\"size\":42,\"ninstr\":4,\"op_pos\":[4,7]}", 0); + succ = rz_serialize_anal_blocks_load (db, anal, diff_parser, NULL); + mu_assert ("reject invalid op_pos array length", !succ); + + rz_anal_free (anal); + anal = rz_anal_new (); + // This could lead to a buffer overflow if unchecked: + sdb_set (db, "0x539", "{\"size\":33,\"fingerprint\":\"AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8=\"}", 0); + succ = rz_serialize_anal_blocks_load (db, anal, diff_parser, NULL); + mu_assert ("reject invalid fingerprint size", !succ); + + assert_block_invariants (anal); + // assert_block_leaks would fail here because loading blocks "leaks" them on purpose to be added to functions later. + // (this just means there are blocks associated with no function) + // so all cool and good here! + + sdb_free (db); + rz_anal_free (anal); + rz_serialize_anal_diff_parser_free (diff_parser); + mu_end; +} + +Sdb *functions_ref_db() { + Sdb *db = sdb_new0 (); + sdb_set (db, "0x4d2", "{\"name\":\"effekt\",\"type\":1,\"stack\":0,\"maxstack\":0,\"ninstr\":0,\"bp_frame\":true,\"pure\":true,\"diff\":{},\"bbs\":[1337]}", 0); + sdb_set (db, "0xbeef", "{\"name\":\"eskapist\",\"bits\":32,\"type\":16,\"stack\":0,\"maxstack\":0,\"ninstr\":0,\"bp_frame\":true,\"diff\":{},\"bbs\":[]}", 0); + sdb_set (db, "0x539", "{\"name\":\"hirsch\",\"bits\":16,\"type\":0,\"cc\":\"fancycall\",\"stack\":42,\"maxstack\":123,\"ninstr\":13,\"folded\":true,\"bp_frame\":true,\"bp_off\":4,\"fingerprint\":\"AAECAwQFBgcICQoLDA0ODw==\",\"diff\":{\"addr\":4321},\"bbs\":[1337,1234],\"imports\":[\"earth\",\"rise\"],\"labels\":{\"beach\":1400,\"another\":1450,\"year\":1440}}", 0); + sdb_set (db, "0xdead", "{\"name\":\"agnosie\",\"bits\":32,\"type\":8,\"stack\":0,\"maxstack\":0,\"ninstr\":0,\"bp_frame\":true,\"diff\":{},\"bbs\":[]}", 0); + sdb_set (db, "0xc0ffee", "{\"name\":\"lifnej\",\"bits\":32,\"type\":32,\"stack\":0,\"maxstack\":0,\"ninstr\":0,\"bp_frame\":true,\"diff\":{},\"bbs\":[]}", 0); + sdb_set (db, "0x1092", "{\"name\":\"hiberno\",\"bits\":32,\"type\":2,\"stack\":0,\"maxstack\":0,\"ninstr\":0,\"diff\":{},\"bbs\":[]}", 0); + sdb_set (db, "0x67932", "{\"name\":\"anamnesis\",\"bits\":32,\"type\":4,\"stack\":0,\"maxstack\":0,\"ninstr\":0,\"bp_frame\":true,\"noreturn\":true,\"diff\":{},\"bbs\":[]}", 0); + sdb_set (db, "0x31337", "{\"name\":\"aldebaran\",\"bits\":32,\"type\":-1,\"stack\":0,\"maxstack\":0,\"ninstr\":0,\"bp_frame\":true,\"diff\":{},\"bbs\":[]}", 0); + return db; +} + +bool test_anal_function_save() { + RzAnal *anal = rz_anal_new (); + + RzAnalBlock *ba = rz_anal_create_block (anal, 1337, 42); + RzAnalBlock *bb = rz_anal_create_block (anal, 1234, 32); + + RzAnalFunction *f = rz_anal_create_function (anal, "hirsch", 1337, RZ_ANAL_FCN_TYPE_NULL, NULL); + rz_anal_function_add_block (f, ba); + rz_anal_function_add_block (f, bb); + f->bits = 16; + f->cc = rz_str_constpool_get (&anal->constpool, "fancycall"); + f->stack = 42; + f->maxstack = 123; + f->bp_off = 4; + f->ninstr = 13; + f->folded = true; + f->fingerprint_size = 0x10; + f->fingerprint = malloc (f->fingerprint_size); + ut8 v; + for (v = 0; v < f->fingerprint_size; v++) { + f->fingerprint[v] = v; + } + f->diff->addr = 4321; + f->imports = rz_list_newf (free); + rz_list_push (f->imports, strdup ("earth")); + rz_list_push (f->imports, strdup ("rise")); + rz_anal_function_set_label (f, "beach", 1400); + rz_anal_function_set_label (f, "another", 1450); + rz_anal_function_set_label (f, "year", 1440); + + f = rz_anal_create_function (anal, "effekt", 1234, RZ_ANAL_FCN_TYPE_FCN, NULL); + rz_anal_function_add_block (f, ba); + f->is_pure = true; + f->bits = 0; + + f = rz_anal_create_function (anal, "hiberno", 4242, RZ_ANAL_FCN_TYPE_LOC, NULL); + f->bp_frame = false; + + f = rz_anal_create_function (anal, "anamnesis", 424242, RZ_ANAL_FCN_TYPE_SYM, NULL); + f->is_noreturn = true; + + rz_anal_create_function (anal, "agnosie", 0xdead, RZ_ANAL_FCN_TYPE_IMP, NULL); + rz_anal_create_function (anal, "eskapist", 0xbeef, RZ_ANAL_FCN_TYPE_INT, NULL); + rz_anal_create_function (anal, "lifnej", 0xc0ffee, RZ_ANAL_FCN_TYPE_ROOT, NULL); + rz_anal_create_function (anal, "aldebaran", 0x31337, RZ_ANAL_FCN_TYPE_ANY, NULL); + + rz_anal_block_unref (ba); + rz_anal_block_unref (bb); + + Sdb *db = sdb_new0 (); + rz_serialize_anal_functions_save (db, anal); + + Sdb *expected = functions_ref_db (); + assert_sdb_eq (db, expected, "functions save"); + sdb_free (db); + sdb_free (expected); + rz_anal_free (anal); + mu_end; +} + +bool test_anal_function_load() { + RzAnal *anal = rz_anal_new (); + + Sdb *db = functions_ref_db (); + RSerializeAnalDiffParser diff_parser = rz_serialize_anal_diff_parser_new (); + + RzAnalBlock *ba = rz_anal_create_block (anal, 1337, 42); + RzAnalBlock *bb = rz_anal_create_block (anal, 1234, 32); + + bool succ = rz_serialize_anal_functions_load (db, anal, diff_parser, NULL); + mu_assert ("load success", succ); + + mu_assert_eq (ba->ref, 3, "ba refs"); + mu_assert_eq (bb->ref, 2, "bb refs"); + rz_anal_block_unref (ba); + rz_anal_block_unref (bb); + + mu_assert_eq (rz_list_length (anal->fcns), 8, "loaded fcn count"); + + RzAnalFunction *f = rz_anal_get_function_at (anal, 1337); + mu_assert_notnull (f, "function"); + mu_assert_streq (f->name, "hirsch", "name"); + mu_assert_eq (f->type, RZ_ANAL_FCN_TYPE_NULL, "type"); + mu_assert_eq (rz_list_length (f->bbs), 2, "bbs count"); + mu_assert ("bb", rz_list_contains (f->bbs, ba)); + mu_assert ("bb", rz_list_contains (f->bbs, bb)); + mu_assert_eq (f->bits, 16, "bits"); + mu_assert_ptreq (f->cc, rz_str_constpool_get (&anal->constpool, "fancycall"), "cc"); + mu_assert_eq (f->stack, 42, "stack"); + mu_assert_eq (f->maxstack, 123, "maxstack"); + mu_assert_eq (f->ninstr, 13, "ninstr"); + mu_assert ("folded", f->folded); + mu_assert ("pure", !f->is_pure); + mu_assert ("noreturn", !f->is_noreturn); + mu_assert ("bp_frame", f->bp_frame); + mu_assert_eq (f->bp_off, 4, "bp off"); + mu_assert_eq (f->fingerprint_size, 0x10, "fingerprint size"); + mu_assert_memeq (f->fingerprint, (const ut8 *)"\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f", 0x10, "fingerprint"); + mu_assert_notnull (f->diff, "diff"); + mu_assert_eq (f->diff->addr, 4321, "diff addr"); // diff is covered in detail by its own tests + mu_assert_notnull (f->imports, "imports"); + mu_assert_eq (rz_list_length (f->imports), 2, "imports count"); + mu_assert_streq (rz_list_get_n (f->imports, 0), "earth", "import"); + mu_assert_streq (rz_list_get_n (f->imports, 1), "rise", "import"); + mu_assert_eq (f->labels->count, 3, "labels count"); + mu_assert_eq (rz_anal_function_get_label (f, "beach"), 1400, "label"); + mu_assert_eq (rz_anal_function_get_label (f, "another"), 1450, "label"); + mu_assert_eq (rz_anal_function_get_label (f, "year"), 1440, "label"); + + f = rz_anal_get_function_at (anal, 1234); + mu_assert_notnull (f, "function"); + mu_assert_streq (f->name, "effekt", "name"); + mu_assert_eq (f->type, RZ_ANAL_FCN_TYPE_FCN, "type"); + mu_assert_eq (rz_list_length (f->bbs), 1, "bbs count"); + mu_assert ("bb", rz_list_contains (f->bbs, ba)); + mu_assert_eq (f->bits, 0, "bits"); + mu_assert_null (f->cc, "cc"); + mu_assert_eq (f->stack, 0, "stack"); + mu_assert_eq (f->maxstack, 0, "maxstack"); + mu_assert_eq (f->ninstr, 0, "ninstr"); + mu_assert ("folded", !f->folded); + mu_assert ("pure", f->is_pure); + mu_assert ("noreturn", !f->is_noreturn); + mu_assert ("bp_frame", f->bp_frame); + mu_assert_eq (f->bp_off, 0, "bp off"); + mu_assert_null (f->fingerprint, "fingerprint"); + mu_assert_notnull (f->diff, "diff"); + mu_assert_null (f->imports, "imports"); + mu_assert_eq (f->labels->count, 0, "labels count"); + + f = rz_anal_get_function_at (anal, 4242); + mu_assert_notnull (f, "function"); + mu_assert_streq (f->name, "hiberno", "name"); + mu_assert_eq (f->type, RZ_ANAL_FCN_TYPE_LOC, "type"); + mu_assert_eq (rz_list_length (f->bbs), 0, "bbs count"); + mu_assert_eq (f->bits, 32, "bits"); + mu_assert_null (f->cc, "cc"); + mu_assert_eq (f->stack, 0, "stack"); + mu_assert_eq (f->maxstack, 0, "maxstack"); + mu_assert_eq (f->ninstr, 0, "ninstr"); + mu_assert ("folded", !f->folded); + mu_assert ("pure", !f->is_pure); + mu_assert ("noreturn", !f->is_noreturn); + mu_assert ("bp_frame", !f->bp_frame); + mu_assert_null (f->fingerprint, "fingerprint"); + mu_assert_notnull (f->diff, "diff"); + mu_assert_null (f->imports, "imports"); + mu_assert_eq (f->labels->count, 0, "labels count"); + + f = rz_anal_get_function_at (anal, 424242); + mu_assert_notnull (f, "function"); + mu_assert_streq (f->name, "anamnesis", "name"); + mu_assert_eq (f->type, RZ_ANAL_FCN_TYPE_SYM, "type"); + mu_assert_eq (rz_list_length (f->bbs), 0, "bbs count"); + mu_assert_eq (f->bits, 32, "bits"); + mu_assert_null (f->cc, "cc"); + mu_assert_eq (f->stack, 0, "stack"); + mu_assert_eq (f->maxstack, 0, "maxstack"); + mu_assert_eq (f->ninstr, 0, "ninstr"); + mu_assert ("folded", !f->folded); + mu_assert ("pure", !f->is_pure); + mu_assert ("noreturn", f->is_noreturn); + mu_assert ("bp_frame", f->bp_frame); + mu_assert_null (f->fingerprint, "fingerprint"); + mu_assert_notnull (f->diff, "diff"); + mu_assert_null (f->imports, "imports"); + mu_assert_eq (f->labels->count, 0, "labels count"); + + f = rz_anal_get_function_at (anal, 0xdead); + mu_assert_notnull (f, "function"); + mu_assert_streq (f->name, "agnosie", "name"); + mu_assert_eq (f->type, RZ_ANAL_FCN_TYPE_IMP, "type"); + mu_assert_eq (f->labels->count, 0, "labels count"); + + f = rz_anal_get_function_at (anal, 0xbeef); + mu_assert_notnull (f, "function"); + mu_assert_streq (f->name, "eskapist", "name"); + mu_assert_eq (f->type, RZ_ANAL_FCN_TYPE_INT, "type"); + mu_assert_eq (f->labels->count, 0, "labels count"); + + f = rz_anal_get_function_at (anal, 0xc0ffee); + mu_assert_notnull (f, "function"); + mu_assert_streq (f->name, "lifnej", "name"); + mu_assert_eq (f->type, RZ_ANAL_FCN_TYPE_ROOT, "type"); + mu_assert_eq (f->labels->count, 0, "labels count"); + + f = rz_anal_get_function_at (anal, 0x31337); + mu_assert_notnull (f, "function"); + mu_assert_streq (f->name, "aldebaran", "name"); + mu_assert_eq (f->type, RZ_ANAL_FCN_TYPE_ANY, "type"); + mu_assert_eq (f->labels->count, 0, "labels count"); + + assert_block_invariants (anal); + assert_block_leaks (anal); + + sdb_free (db); + rz_anal_free (anal); + rz_serialize_anal_diff_parser_free (diff_parser); + mu_end; +} + +Sdb *vars_ref_db() { + Sdb *db = sdb_new0 (); + sdb_set (db, "0x539", "{\"name\":\"hirsch\",\"bits\":64,\"type\":0,\"stack\":0,\"maxstack\":0,\"ninstr\":0,\"bp_frame\":true,\"diff\":{},\"bbs\":[]," + "\"vars\":[" + "{\"name\":\"arg_rax\",\"type\":\"int64_t\",\"kind\":\"r\",\"reg\":\"rax\",\"arg\":true,\"accs\":[{\"off\":3,\"type\":\"r\",\"sp\":42,\"reg\":\"rax\"},{\"off\":13,\"type\":\"rw\",\"sp\":13,\"reg\":\"rbx\"},{\"off\":23,\"type\":\"w\",\"sp\":123,\"reg\":\"rcx\"}],\"constrs\":[0,42,1,84,2,126,3,168,4,210,5,252,6,294,7,336,8,378,9,420,10,462,11,504,12,546,13,588,14,630,15,672]}," + "{\"name\":\"var_sp\",\"type\":\"const char *\",\"kind\":\"s\",\"delta\":16,\"accs\":[{\"off\":3,\"type\":\"w\",\"sp\":321,\"reg\":\"rsp\"}]}," + "{\"name\":\"var_bp\",\"type\":\"struct something\",\"kind\":\"b\",\"delta\":-16}," + "{\"name\":\"arg_bp\",\"type\":\"uint64_t\",\"kind\":\"b\",\"delta\":16,\"arg\":true,\"cmt\":\"I have no idea what this var does\"}]}", 0); + return db; +} + +bool test_anal_var_save() { + RzAnal *anal = rz_anal_new (); + rz_anal_use (anal, "x86"); + rz_anal_set_bits (anal, 64); + + RzAnalFunction *f = rz_anal_create_function (anal, "hirsch", 1337, RZ_ANAL_FCN_TYPE_NULL, NULL); + + RzRegItem *rax = rz_reg_get (anal->reg, "rax", -1); + RzAnalVar *v = rz_anal_function_set_var (f, rax->index, RZ_ANAL_VAR_KIND_REG, "int64_t", 0, true, "arg_rax"); + rz_anal_var_set_access (v, "rax", 1340, RZ_ANAL_VAR_ACCESS_TYPE_READ, 42); + rz_anal_var_set_access (v, "rbx", 1350, RZ_ANAL_VAR_ACCESS_TYPE_READ | RZ_ANAL_VAR_ACCESS_TYPE_WRITE, 13); + rz_anal_var_set_access (v, "rcx", 1360, RZ_ANAL_VAR_ACCESS_TYPE_WRITE, 123); + + ut64 val = 0; + _RzAnalCond cond; + for (cond = RZ_ANAL_COND_AL; cond <= RZ_ANAL_COND_LS; cond++) { + val += 42; + RzAnalVarConstraint constr = { + .cond = cond, + .val = val + }; + rz_anal_var_add_constraint (v, &constr); + } + + v = rz_anal_function_set_var (f, 0x10, RZ_ANAL_VAR_KIND_SPV, "const char *", 0, false, "var_sp"); + rz_anal_var_set_access (v, "rsp", 1340, RZ_ANAL_VAR_ACCESS_TYPE_WRITE, 321); + + rz_anal_function_set_var (f, -0x10, RZ_ANAL_VAR_KIND_BPV, "struct something", 0, false, "var_bp"); + v = rz_anal_function_set_var (f, 0x10, RZ_ANAL_VAR_KIND_BPV, "uint64_t", 0, true, "arg_bp"); + v->comment = strdup ("I have no idea what this var does"); + + Sdb *db = sdb_new0 (); + rz_serialize_anal_functions_save (db, anal); + + Sdb *expected = vars_ref_db (); + assert_sdb_eq (db, expected, "functions save"); + sdb_free (db); + sdb_free (expected); + rz_anal_free (anal); + mu_end; +} + +bool test_anal_var_load() { + RzAnal *anal = rz_anal_new (); + rz_anal_use (anal, "x86"); + rz_anal_set_bits (anal, 64); + + Sdb *db = vars_ref_db (); + RSerializeAnalDiffParser diff_parser = rz_serialize_anal_diff_parser_new (); + + bool succ = rz_serialize_anal_functions_load (db, anal, diff_parser, NULL); + mu_assert ("load success", succ); + RzAnalFunction *f = rz_anal_get_function_at (anal, 1337); + mu_assert_notnull (f, "function"); + + mu_assert_eq (rz_pvector_len (&f->vars), 4, "vars count"); + + RzRegItem *rax = rz_reg_get (anal->reg, "rax", -1); + RzAnalVar *v = rz_anal_function_get_var (f, RZ_ANAL_VAR_KIND_REG, rax->index); + mu_assert_notnull (v, "var"); + mu_assert_streq (v->regname, "rax", "var regname"); + mu_assert_streq (v->name, "arg_rax", "var name"); + mu_assert_streq (v->type, "int64_t", "var type"); + mu_assert ("var arg", v->isarg); + + mu_assert_eq (v->accesses.len, 3, "accesses count"); + bool found[3] = { false, false, false }; + RzAnalVarAccess *acc; + rz_vector_foreach (&v->accesses, acc) { + if (acc->offset == 3 && acc->type == RZ_ANAL_VAR_ACCESS_TYPE_READ && acc->stackptr == 42 && !strcmp(acc->reg, "rax")) { + found[0] = true; + } else if (acc->offset == 13 && acc->type == (RZ_ANAL_VAR_ACCESS_TYPE_READ | RZ_ANAL_VAR_ACCESS_TYPE_WRITE) + && acc->stackptr == 13 && !strcmp(acc->reg, "rbx")) { + found[1] = true; + } else if (acc->offset == 23 && acc->type == RZ_ANAL_VAR_ACCESS_TYPE_WRITE + && acc->stackptr == 123 && !strcmp(acc->reg, "rcx")) { + found[2] = true; + } + } + mu_assert ("var accesses", found[0] && found[1] && found[2]); + RzPVector *used = rz_anal_function_get_vars_used_at (f, 1340); + mu_assert ("var used", rz_pvector_contains (used, v)); + + mu_assert_eq (v->constraints.len, RZ_ANAL_COND_LS + 1, "constraints count"); + ut64 val = 0; + _RzAnalCond cond; + for (cond = RZ_ANAL_COND_AL; cond <= RZ_ANAL_COND_LS; cond++) { + val += 42; + RzAnalVarConstraint *constr = rz_vector_index_ptr (&v->constraints, (size_t)(cond - RZ_ANAL_COND_AL)); + mu_assert_eq (constr->cond, cond, "constraint cond"); + mu_assert_eq (constr->val, val, "constraint val"); + } + + v = rz_anal_function_get_var (f, RZ_ANAL_VAR_KIND_SPV, 0x10); + mu_assert_notnull (v, "var"); + mu_assert_streq (v->name, "var_sp", "var name"); + mu_assert_streq (v->type, "const char *", "var type"); + mu_assert ("var arg", !v->isarg); + mu_assert_eq (v->accesses.len, 1, "accesses count"); + acc = rz_vector_index_ptr (&v->accesses, 0); + mu_assert_eq (acc->offset, 3, "access offset"); + mu_assert_eq (acc->type, RZ_ANAL_VAR_ACCESS_TYPE_WRITE, "access type"); + mu_assert_eq (acc->stackptr, 321, "access stackptr"); + mu_assert_streq (acc->reg, "rsp", "access reg"); + mu_assert ("var used", rz_pvector_contains (used, v)); // used at the same var as the reg one + + v = rz_anal_function_get_var (f, RZ_ANAL_VAR_KIND_BPV, -0x10); + mu_assert_notnull (v, "var"); + mu_assert_streq (v->name, "var_bp", "var name"); + mu_assert_streq (v->type, "struct something", "var type"); + mu_assert ("var arg", !v->isarg); + mu_assert_eq (v->accesses.len, 0, "accesses count"); + + v = rz_anal_function_get_var (f, RZ_ANAL_VAR_KIND_BPV, 0x10); + mu_assert_notnull (v, "var"); + mu_assert_streq (v->name, "arg_bp", "var name"); + mu_assert_streq (v->type, "uint64_t", "var type"); + mu_assert ("var arg", v->isarg); + mu_assert_eq (v->accesses.len, 0, "accesses count"); + mu_assert_streq (v->comment, "I have no idea what this var does", "var comment"); + + sdb_free (db); + rz_anal_free (anal); + rz_serialize_anal_diff_parser_free (diff_parser); + mu_end; +} + +Sdb *xrefs_ref_db() { + Sdb *db = sdb_new0 (); + sdb_set (db, "0x29a", "[{\"to\":333,\"type\":\"s\"}]", 0); + sdb_set (db, "0x1337", "[{\"to\":4242},{\"to\":4243,\"type\":\"c\"}]", 0); + sdb_set (db, "0x2a", "[{\"to\":4321,\"type\":\"d\"}]", 0); + sdb_set (db, "0x4d2", "[{\"to\":4243,\"type\":\"C\"}]", 0); + return db; +} + +bool test_anal_xrefs_save() { + RzAnal *anal = rz_anal_new (); + + rz_anal_xrefs_set (anal, 0x1337, 4242, RZ_ANAL_REF_TYPE_NULL); + rz_anal_xrefs_set (anal, 0x1337, 4243, RZ_ANAL_REF_TYPE_CODE); + rz_anal_xrefs_set (anal, 1234, 4243, RZ_ANAL_REF_TYPE_CALL); + rz_anal_xrefs_set (anal, 42, 4321, RZ_ANAL_REF_TYPE_DATA); + rz_anal_xrefs_set (anal, 666, 333, RZ_ANAL_REF_TYPE_STRING); + + Sdb *db = sdb_new0 (); + rz_serialize_anal_xrefs_save (db, anal); + + Sdb *expected = xrefs_ref_db (); + assert_sdb_eq (db, expected, "xrefs save"); + sdb_free (db); + sdb_free (expected); + rz_anal_free (anal); + mu_end; +} + +bool test_anal_xrefs_load() { + RzAnal *anal = rz_anal_new (); + + Sdb *db = xrefs_ref_db (); + + bool succ = rz_serialize_anal_xrefs_load (db, anal, NULL); + mu_assert ("load success", succ); + mu_assert_eq (rz_anal_xrefs_count (anal), 5, "xrefs count"); + + RzList *xrefs = rz_anal_xrefs_get_from (anal, 0x1337); + mu_assert_eq (rz_list_length (xrefs), 2, "xrefs from count"); + mu_assert_eq (((RzAnalRef *)rz_list_get_n (xrefs, 0))->addr, 4242, "xref to"); + mu_assert_eq (((RzAnalRef *)rz_list_get_n (xrefs, 0))->at, 0x1337, "xref addr"); + mu_assert_eq (((RzAnalRef *)rz_list_get_n (xrefs, 0))->type, RZ_ANAL_REF_TYPE_NULL, "xref type"); + mu_assert_eq (((RzAnalRef *)rz_list_get_n (xrefs, 1))->addr, 4243, "xref to"); + mu_assert_eq (((RzAnalRef *)rz_list_get_n (xrefs, 1))->at, 0x1337, "xref addr"); + mu_assert_eq (((RzAnalRef *)rz_list_get_n (xrefs, 1))->type, RZ_ANAL_REF_TYPE_CODE, "xref type"); + rz_list_free (xrefs); + + xrefs = rz_anal_xrefs_get_from (anal, 1234); + mu_assert_eq (rz_list_length (xrefs), 1, "xrefs from count"); + mu_assert_eq (((RzAnalRef *)rz_list_get_n (xrefs, 0))->addr, 4243, "xref to"); + mu_assert_eq (((RzAnalRef *)rz_list_get_n (xrefs, 0))->at, 1234, "xref addr"); + mu_assert_eq (((RzAnalRef *)rz_list_get_n (xrefs, 0))->type, RZ_ANAL_REF_TYPE_CALL, "xref type"); + rz_list_free (xrefs); + + xrefs = rz_anal_xrefs_get_from (anal, 42); + mu_assert_eq (rz_list_length (xrefs), 1, "xrefs from count"); + mu_assert_eq (((RzAnalRef *)rz_list_get_n (xrefs, 0))->addr, 4321, "xref to"); + mu_assert_eq (((RzAnalRef *)rz_list_get_n (xrefs, 0))->at, 42, "xref addr"); + mu_assert_eq (((RzAnalRef *)rz_list_get_n (xrefs, 0))->type, RZ_ANAL_REF_TYPE_DATA, "xref type"); + rz_list_free (xrefs); + + xrefs = rz_anal_xrefs_get_from (anal, 666); + mu_assert_eq (rz_list_length (xrefs), 1, "xrefs from count"); + mu_assert_eq (((RzAnalRef *)rz_list_get_n (xrefs, 0))->addr, 333, "xref to"); + mu_assert_eq (((RzAnalRef *)rz_list_get_n (xrefs, 0))->at, 666, "xref addr"); + mu_assert_eq (((RzAnalRef *)rz_list_get_n (xrefs, 0))->type, RZ_ANAL_REF_TYPE_STRING, "xref type"); + rz_list_free (xrefs); + + xrefs = rz_anal_xrefs_get (anal, 4243); + mu_assert_eq (rz_list_length (xrefs), 2, "xrefs to count"); + mu_assert_eq (((RzAnalRef *)rz_list_get_n (xrefs, 0))->addr, 1234, "xref to"); + mu_assert_eq (((RzAnalRef *)rz_list_get_n (xrefs, 0))->at, 4243, "xref addr"); + mu_assert_eq (((RzAnalRef *)rz_list_get_n (xrefs, 0))->type, RZ_ANAL_REF_TYPE_CALL, "xref type"); + mu_assert_eq (((RzAnalRef *)rz_list_get_n (xrefs, 1))->addr, 0x1337, "xref to"); + mu_assert_eq (((RzAnalRef *)rz_list_get_n (xrefs, 1))->at, 4243, "xref addr"); + mu_assert_eq (((RzAnalRef *)rz_list_get_n (xrefs, 1))->type, RZ_ANAL_REF_TYPE_CODE, "xref type"); + rz_list_free (xrefs); + + sdb_free (db); + rz_anal_free (anal); + mu_end; +} + +Sdb *meta_ref_db() { + Sdb *db = sdb_new0 (); + Sdb *spaces_db = sdb_ns (db, "spaces", true); + sdb_set (spaces_db, "name", "CS", 0); + sdb_set (spaces_db, "spacestack", "[\"*\"]", 0); + sdb_set (sdb_ns (spaces_db, "spaces", true), "myspace", "s", 0); + sdb_set(db, "0x20a0", "[{\"size\":32,\"type\":\"s\",\"subtype\":66,\"str\":\"utf32be\"}]", 0); + sdb_set(db, "0x20c0", "[{\"size\":32,\"type\":\"s\",\"subtype\":103,\"str\":\"guess\"}]", 0); + sdb_set(db, "0x1337", + "[{\"size\":16,\"type\":\"d\"}," + "{\"size\":17,\"type\":\"c\"}," + "{\"size\":18,\"type\":\"s\",\"str\":\"some string\"}," + "{\"size\":19,\"type\":\"f\"}," + "{\"size\":20,\"type\":\"m\"}," + "{\"size\":21,\"type\":\"h\"}," + "{\"type\":\"C\",\"str\":\"some comment here\"}," + "{\"size\":22,\"type\":\"r\"}," + "{\"size\":23,\"type\":\"H\"}," + "{\"size\":24,\"type\":\"t\"}," + "{\"type\":\"C\",\"str\":\"comment in space\",\"space\":\"myspace\"}]", 0); + sdb_set(db, "0x2000", "[{\"size\":32,\"type\":\"s\",\"subtype\":97,\"str\":\"latin1\"}]", 0); + sdb_set(db, "0x2040", "[{\"size\":32,\"type\":\"s\",\"subtype\":117,\"str\":\"utf16le\"}]", 0); + sdb_set(db, "0x2080", "[{\"size\":32,\"type\":\"s\",\"subtype\":98,\"str\":\"utf16be\"}]", 0); + sdb_set(db, "0x2020", "[{\"size\":32,\"type\":\"s\",\"subtype\":56,\"str\":\"utf8\"}]", 0); + sdb_set(db, "0x2060", "[{\"size\":32,\"type\":\"s\",\"subtype\":85,\"str\":\"utf32le\"}]", 0); + return db; +} + +bool test_anal_meta_save() { + RzAnal *anal = rz_anal_new (); + + rz_meta_set (anal, RZ_META_TYPE_DATA, 0x1337, 0x10, NULL); + rz_meta_set (anal, RZ_META_TYPE_CODE, 0x1337, 0x11, NULL); + rz_meta_set (anal, RZ_META_TYPE_STRING, 0x1337, 0x12, "some string"); + rz_meta_set (anal, RZ_META_TYPE_FORMAT, 0x1337, 0x13, NULL); + rz_meta_set (anal, RZ_META_TYPE_MAGIC, 0x1337, 0x14, NULL); + rz_meta_set (anal, RZ_META_TYPE_HIDE, 0x1337, 0x15, NULL); + rz_meta_set (anal, RZ_META_TYPE_COMMENT, 0x1337, 1, "some comment here"); + rz_meta_set (anal, RZ_META_TYPE_RUN, 0x1337, 0x16, NULL); + rz_meta_set (anal, RZ_META_TYPE_HIGHLIGHT, 0x1337, 0x17, NULL); + rz_meta_set (anal, RZ_META_TYPE_VARTYPE, 0x1337, 0x18, NULL); + + rz_meta_set_with_subtype (anal, RZ_META_TYPE_STRING, RZ_STRING_ENC_LATIN1, 0x2000, 0x20, "latin1"); + rz_meta_set_with_subtype (anal, RZ_META_TYPE_STRING, RZ_STRING_ENC_UTF8, 0x2020, 0x20, "utf8"); + rz_meta_set_with_subtype (anal, RZ_META_TYPE_STRING, RZ_STRING_ENC_UTF16LE, 0x2040, 0x20, "utf16le"); + rz_meta_set_with_subtype (anal, RZ_META_TYPE_STRING, RZ_STRING_ENC_UTF32LE, 0x2060, 0x20, "utf32le"); + rz_meta_set_with_subtype (anal, RZ_META_TYPE_STRING, RZ_STRING_ENC_UTF16BE, 0x2080, 0x20, "utf16be"); + rz_meta_set_with_subtype (anal, RZ_META_TYPE_STRING, RZ_STRING_ENC_UTF32BE, 0x20a0, 0x20, "utf32be"); + rz_meta_set_with_subtype (anal, RZ_META_TYPE_STRING, RZ_STRING_ENC_GUESS, 0x20c0, 0x20, "guess"); + + rz_spaces_push (&anal->meta_spaces, "myspace"); + rz_meta_set (anal, RZ_META_TYPE_COMMENT, 0x1337, 1, "comment in space"); + rz_spaces_pop (&anal->meta_spaces); + + Sdb *db = sdb_new0 (); + rz_serialize_anal_meta_save (db, anal); + + Sdb *expected = meta_ref_db (); + assert_sdb_eq (db, expected, "meta save"); + sdb_free (db); + sdb_free (expected); + rz_anal_free (anal); + mu_end; +} + +bool test_anal_meta_load() { + RzAnal *anal = rz_anal_new (); + + Sdb *db = meta_ref_db (); + + bool succ = rz_serialize_anal_meta_load (db, anal, NULL); + mu_assert ("load success", succ); + + size_t count = 0; + RzAnalMetaItem *meta; + RIntervalTreeIter it; + rz_interval_tree_foreach (&anal->meta, it, meta) { + (void)meta; + count++; + } + mu_assert_eq (count, 18, "meta count"); + + ut64 size; + meta = rz_meta_get_at (anal, 0x1337, RZ_META_TYPE_DATA, &size); + mu_assert_notnull (meta, "meta item"); + mu_assert_eq (size, 0x10, "meta item size"); + mu_assert_eq (meta->subtype, 0, "meta item subtype"); + mu_assert_null (meta->str, "meta item string"); + meta = rz_meta_get_at (anal, 0x1337, RZ_META_TYPE_CODE, &size); + mu_assert_notnull (meta, "meta item"); + mu_assert_eq (size, 0x11, "meta item size"); + mu_assert_eq (meta->subtype, 0, "meta item subtype"); + mu_assert_null (meta->str, "meta item string"); + meta = rz_meta_get_at (anal, 0x1337, RZ_META_TYPE_STRING, &size); + mu_assert_notnull (meta, "meta item"); + mu_assert_eq (size, 0x12, "meta item size"); + mu_assert_eq (meta->subtype, 0, "meta item subtype"); + mu_assert_streq (meta->str, "some string", "meta item string"); + meta = rz_meta_get_at (anal, 0x1337, RZ_META_TYPE_FORMAT, &size); + mu_assert_notnull (meta, "meta item"); + mu_assert_eq (size, 0x13, "meta item size"); + mu_assert_eq (meta->subtype, 0, "meta item subtype"); + mu_assert_null (meta->str, "meta item string"); + meta = rz_meta_get_at (anal, 0x1337, RZ_META_TYPE_MAGIC, &size); + mu_assert_notnull (meta, "meta item"); + mu_assert_eq (size, 0x14, "meta item size"); + mu_assert_eq (meta->subtype, 0, "meta item subtype"); + mu_assert_null (meta->str, "meta item string"); + meta = rz_meta_get_at (anal, 0x1337, RZ_META_TYPE_HIDE, &size); + mu_assert_notnull (meta, "meta item"); + mu_assert_eq (size, 0x15, "meta item size"); + mu_assert_eq (meta->subtype, 0, "meta item subtype"); + mu_assert_null (meta->str, "meta item string"); + meta = rz_meta_get_at (anal, 0x1337, RZ_META_TYPE_COMMENT, &size); + mu_assert_notnull (meta, "meta item"); + mu_assert_eq (size, 1, "meta item size"); + mu_assert_eq (meta->subtype, 0, "meta item subtype"); + mu_assert_streq (meta->str, "some comment here", "meta item string"); + meta = rz_meta_get_at (anal, 0x1337, RZ_META_TYPE_RUN, &size); + mu_assert_notnull (meta, "meta item"); + mu_assert_eq (size, 0x16, "meta item size"); + mu_assert_eq (meta->subtype, 0, "meta item subtype"); + mu_assert_null (meta->str, "meta item string"); + meta = rz_meta_get_at (anal, 0x1337, RZ_META_TYPE_HIGHLIGHT, &size); + mu_assert_notnull (meta, "meta item"); + mu_assert_eq (size, 0x17, "meta item size"); + mu_assert_eq (meta->subtype, 0, "meta item subtype"); + mu_assert_null (meta->str, "meta item string"); + meta = rz_meta_get_at (anal, 0x1337, RZ_META_TYPE_VARTYPE, &size); + mu_assert_notnull (meta, "meta item"); + mu_assert_eq (size, 0x18, "meta item size"); + mu_assert_eq (meta->subtype, 0, "meta item subtype"); + mu_assert_null (meta->str, "meta item string"); + + rz_spaces_push (&anal->meta_spaces, "myspace"); + meta = rz_meta_get_at (anal, 0x1337, RZ_META_TYPE_COMMENT, &size); + mu_assert_notnull (meta, "meta item"); + mu_assert_eq (size, 1, "meta item size"); + mu_assert_eq (meta->subtype, 0, "meta item subtype"); + mu_assert_streq (meta->str, "comment in space", "meta item string"); + rz_spaces_pop (&anal->meta_spaces); + + meta = rz_meta_get_at (anal, 0x2000, RZ_META_TYPE_STRING, &size); + mu_assert_notnull (meta, "meta item"); + mu_assert_eq (size, 0x20, "meta item size"); + mu_assert_eq (meta->subtype, RZ_STRING_ENC_LATIN1, "meta item subtype"); + mu_assert_streq (meta->str, "latin1", "meta item string"); + meta = rz_meta_get_at (anal, 0x2020, RZ_META_TYPE_STRING, &size); + mu_assert_notnull (meta, "meta item"); + mu_assert_eq (size, 0x20, "meta item size"); + mu_assert_eq (meta->subtype, RZ_STRING_ENC_UTF8, "meta item subtype"); + mu_assert_streq (meta->str, "utf8", "meta item string"); + meta = rz_meta_get_at (anal, 0x2040, RZ_META_TYPE_STRING, &size); + mu_assert_notnull (meta, "meta item"); + mu_assert_eq (size, 0x20, "meta item size"); + mu_assert_eq (meta->subtype, RZ_STRING_ENC_UTF16LE, "meta item subtype"); + mu_assert_streq (meta->str, "utf16le", "meta item string"); + meta = rz_meta_get_at (anal, 0x2060, RZ_META_TYPE_STRING, &size); + mu_assert_notnull (meta, "meta item"); + mu_assert_eq (size, 0x20, "meta item size"); + mu_assert_eq (meta->subtype, RZ_STRING_ENC_UTF32LE, "meta item subtype"); + mu_assert_streq (meta->str, "utf32le", "meta item string"); + meta = rz_meta_get_at (anal, 0x2080, RZ_META_TYPE_STRING, &size); + mu_assert_notnull (meta, "meta item"); + mu_assert_eq (size, 0x20, "meta item size"); + mu_assert_eq (meta->subtype, RZ_STRING_ENC_UTF16BE, "meta item subtype"); + mu_assert_streq (meta->str, "utf16be", "meta item string"); + meta = rz_meta_get_at (anal, 0x20a0, RZ_META_TYPE_STRING, &size); + mu_assert_notnull (meta, "meta item"); + mu_assert_eq (size, 0x20, "meta item size"); + mu_assert_eq (meta->subtype, RZ_STRING_ENC_UTF32BE, "meta item subtype"); + mu_assert_streq (meta->str, "utf32be", "meta item string"); + meta = rz_meta_get_at (anal, 0x20c0, RZ_META_TYPE_STRING, &size); + mu_assert_notnull (meta, "meta item"); + mu_assert_eq (size, 0x20, "meta item size"); + mu_assert_eq (meta->subtype, RZ_STRING_ENC_GUESS, "meta item subtype"); + mu_assert_streq (meta->str, "guess", "meta item string"); + + sdb_free (db); + rz_anal_free (anal); + mu_end; +} + +Sdb *hints_ref_db() { + Sdb *db = sdb_new0 (); + sdb_set (db, "0x1000", "{\"optype\":-2147483648}", 0); + sdb_set (db, "0x1001", "{\"optype\":1073741824}", 0); + sdb_set (db, "0x1002", "{\"optype\":536870912}", 0); + sdb_set (db, "0x1003", "{\"optype\":268435456}", 0); + sdb_set (db, "0x1004", "{\"optype\":134217728}", 0); + sdb_set (db, "0x1005", "{\"optype\":0}", 0); + sdb_set (db, "0x1006", "{\"optype\":1}", 0); + sdb_set (db, "0x1007", "{\"optype\":2}", 0); + sdb_set (db, "0x1008", "{\"optype\":268435458}", 0); + sdb_set (db, "0x1009", "{\"optype\":134217730}", 0); + sdb_set (db, "0x100a", "{\"optype\":402653186}", 0); + sdb_set (db, "0x100b", "{\"optype\":-2147483647}", 0); + sdb_set (db, "0x100c", "{\"optype\":-1879048191}", 0); + sdb_set (db, "0x100d", "{\"optype\":536870913}", 0); + sdb_set (db, "0x100e", "{\"optype\":-1610612735}", 0); + sdb_set (db, "0x100f", "{\"optype\":-2147483646}", 0); + sdb_set (db, "0x1010", "{\"optype\":3}", 0); + sdb_set (db, "0x1011", "{\"optype\":4}", 0); + sdb_set (db, "0x1012", "{\"optype\":268435460}", 0); + sdb_set (db, "0x1013", "{\"optype\":134217732}", 0); + sdb_set (db, "0x1014", "{\"optype\":402653188}", 0); + sdb_set (db, "0x1015", "{\"optype\":-2147483645}", 0); + sdb_set (db, "0x1016", "{\"optype\":-2147483644}", 0); + sdb_set (db, "0x1017", "{\"optype\":5}", 0); + sdb_set (db, "0x1018", "{\"optype\":-2147483643}", 0); + sdb_set (db, "0x1019", "{\"optype\":6}", 0); + sdb_set (db, "0x101a", "{\"optype\":7}", 0); + sdb_set (db, "0x101b", "{\"optype\":8}", 0); + sdb_set (db, "0x101c", "{\"optype\":9}", 0); + sdb_set (db, "0x101d", "{\"optype\":-2147483639}", 0); + sdb_set (db, "0x101e", "{\"optype\":10}", 0); + sdb_set (db, "0x101f", "{\"optype\":11}", 0); + sdb_set (db, "0x1020", "{\"optype\":-2147483637}", 0); + sdb_set (db, "0x1021", "{\"optype\":12}", 0); + sdb_set (db, "0x1022", "{\"optype\":268435468}", 0); + sdb_set (db, "0x1023", "{\"optype\":13}", 0); + sdb_set (db, "0x1024", "{\"optype\":14}", 0); + sdb_set (db, "0x1025", "{\"optype\":15}", 0); + sdb_set (db, "0x1026", "{\"optype\":16}", 0); + sdb_set (db, "0x1027", "{\"optype\":17}", 0); + sdb_set (db, "0x1028", "{\"optype\":18}", 0); + sdb_set (db, "0x1029", "{\"optype\":19}", 0); + sdb_set (db, "0x102a", "{\"optype\":20}", 0); + sdb_set (db, "0x102b", "{\"optype\":21}", 0); + sdb_set (db, "0x102c", "{\"optype\":22}", 0); + sdb_set (db, "0x102d", "{\"optype\":23}", 0); + sdb_set (db, "0x102e", "{\"optype\":24}", 0); + sdb_set (db, "0x102f", "{\"optype\":25}", 0); + sdb_set (db, "0x1030", "{\"optype\":26}", 0); + sdb_set (db, "0x1031", "{\"optype\":27}", 0); + sdb_set (db, "0x1032", "{\"optype\":28}", 0); + sdb_set (db, "0x1033", "{\"optype\":29}", 0); + sdb_set (db, "0x1034", "{\"optype\":30}", 0); + sdb_set (db, "0x1035", "{\"optype\":31}", 0); + sdb_set (db, "0x1036", "{\"optype\":32}", 0); + sdb_set (db, "0x1037", "{\"optype\":33}", 0); + sdb_set (db, "0x1038", "{\"optype\":34}", 0); + sdb_set (db, "0x1039", "{\"optype\":35}", 0); + sdb_set (db, "0x103a", "{\"optype\":36}", 0); + sdb_set (db, "0x103b", "{\"optype\":37}", 0); + sdb_set (db, "0x103c", "{\"optype\":38}", 0); + sdb_set (db, "0x103d", "{\"optype\":39}", 0); + sdb_set (db, "0x103e", "{\"optype\":40}", 0); + sdb_set (db, "0x103f", "{\"optype\":41}", 0); + sdb_set (db, "0x1040", "{\"optype\":42}", 0); + sdb_set (db, "0x1041", "{\"optype\":43}", 0); + sdb_set (db, "0x1042", "{\"optype\":44}", 0); + sdb_set (db, "0x1043", "{\"optype\":45}", 0); + sdb_set (db, "0x1044", "{\"optype\":46}", 0); + sdb_set (db, "0x1045", "{\"optype\":47}", 0); + sdb_set (db, "0x100", "{\"arch\":\"arm\",\"bits\":16}", 0); + sdb_set (db, "0x120", "{\"arch\":null}", 0); + sdb_set (db, "0x130", "{\"bits\":0}", 0); + sdb_set (db, "0x200", "{\"immbase\":10}", 0); + sdb_set (db, "0x210", "{\"jump\":1337,\"fail\":1234}", 0); + sdb_set (db, "0x220", "{\"syntax\":\"intel\"}", 0); + sdb_set (db, "0x230", "{\"frame\":48}", 0); + sdb_set (db, "0x240", "{\"ptr\":4321}", 0); + sdb_set (db, "0x250", "{\"nword\":3}", 0); + sdb_set (db, "0x260", "{\"ret\":666}", 0); + sdb_set (db, "0x270", "{\"newbits\":32}", 0); + sdb_set (db, "0x280", "{\"size\":7}", 0); + sdb_set (db, "0x290", "{\"opcode\":\"mov\"}", 0); + sdb_set (db, "0x2a0", "{\"toff\":\"sometype\"}", 0); + sdb_set (db, "0x2b0", "{\"esil\":\"13,29,+\"}", 0); + sdb_set (db, "0x2c0", "{\"high\":true}", 0); + sdb_set (db, "0x2d0", "{\"val\":54323}", 0); + return db; +} + +// All of these optypes need to be correctly loaded from potentially older projects +// So changing anything here will require a migration pass! +static int all_optypes[] = { + RZ_ANAL_OP_TYPE_COND, RZ_ANAL_OP_TYPE_REP, RZ_ANAL_OP_TYPE_MEM, RZ_ANAL_OP_TYPE_REG, RZ_ANAL_OP_TYPE_IND, + RZ_ANAL_OP_TYPE_NULL, RZ_ANAL_OP_TYPE_JMP, RZ_ANAL_OP_TYPE_UJMP, RZ_ANAL_OP_TYPE_RJMP, RZ_ANAL_OP_TYPE_IJMP, + RZ_ANAL_OP_TYPE_IRJMP, RZ_ANAL_OP_TYPE_CJMP, RZ_ANAL_OP_TYPE_RCJMP, RZ_ANAL_OP_TYPE_MJMP, RZ_ANAL_OP_TYPE_MCJMP, + RZ_ANAL_OP_TYPE_UCJMP, RZ_ANAL_OP_TYPE_CALL, RZ_ANAL_OP_TYPE_UCALL, RZ_ANAL_OP_TYPE_RCALL, RZ_ANAL_OP_TYPE_ICALL, + RZ_ANAL_OP_TYPE_IRCALL, RZ_ANAL_OP_TYPE_CCALL, RZ_ANAL_OP_TYPE_UCCALL, RZ_ANAL_OP_TYPE_RET, RZ_ANAL_OP_TYPE_CRET, + RZ_ANAL_OP_TYPE_ILL, RZ_ANAL_OP_TYPE_UNK, RZ_ANAL_OP_TYPE_NOP, RZ_ANAL_OP_TYPE_MOV, RZ_ANAL_OP_TYPE_CMOV, + RZ_ANAL_OP_TYPE_TRAP, RZ_ANAL_OP_TYPE_SWI, RZ_ANAL_OP_TYPE_CSWI, RZ_ANAL_OP_TYPE_UPUSH, RZ_ANAL_OP_TYPE_RPUSH, + RZ_ANAL_OP_TYPE_PUSH, RZ_ANAL_OP_TYPE_POP, RZ_ANAL_OP_TYPE_CMP, RZ_ANAL_OP_TYPE_ACMP, RZ_ANAL_OP_TYPE_ADD, + RZ_ANAL_OP_TYPE_SUB, RZ_ANAL_OP_TYPE_IO, RZ_ANAL_OP_TYPE_MUL, RZ_ANAL_OP_TYPE_DIV, RZ_ANAL_OP_TYPE_SHR, + RZ_ANAL_OP_TYPE_SHL, RZ_ANAL_OP_TYPE_SAL, RZ_ANAL_OP_TYPE_SAR, RZ_ANAL_OP_TYPE_OR, RZ_ANAL_OP_TYPE_AND, + RZ_ANAL_OP_TYPE_XOR, RZ_ANAL_OP_TYPE_NOR, RZ_ANAL_OP_TYPE_NOT, RZ_ANAL_OP_TYPE_STORE, RZ_ANAL_OP_TYPE_LOAD, + RZ_ANAL_OP_TYPE_LEA, RZ_ANAL_OP_TYPE_LEAVE, RZ_ANAL_OP_TYPE_ROR, RZ_ANAL_OP_TYPE_ROL, RZ_ANAL_OP_TYPE_XCHG, + RZ_ANAL_OP_TYPE_MOD, RZ_ANAL_OP_TYPE_SWITCH, RZ_ANAL_OP_TYPE_CASE, RZ_ANAL_OP_TYPE_LENGTH, RZ_ANAL_OP_TYPE_CAST, + RZ_ANAL_OP_TYPE_NEW, RZ_ANAL_OP_TYPE_ABS, RZ_ANAL_OP_TYPE_CPL, RZ_ANAL_OP_TYPE_CRYPTO, RZ_ANAL_OP_TYPE_SYNC +}; + +#define ALL_OPTYPES_COUNT (sizeof(all_optypes) / sizeof(int)) + +bool test_anal_hints_save() { + RzAnal *anal = rz_anal_new (); + + rz_anal_hint_set_arch (anal, 0x100, "arm"); + rz_anal_hint_set_bits (anal, 0x100, 16); + rz_anal_hint_set_arch (anal, 0x120, NULL); + rz_anal_hint_set_bits (anal, 0x130, 0); + + rz_anal_hint_set_immbase (anal, 0x200, 10); + rz_anal_hint_set_jump (anal, 0x210, 1337); + rz_anal_hint_set_fail (anal, 0x210, 1234); + rz_anal_hint_set_stackframe (anal, 0x230, 0x30); + rz_anal_hint_set_pointer (anal, 0x240, 4321); + rz_anal_hint_set_nword (anal, 0x250, 3); + rz_anal_hint_set_ret (anal, 0x260, 666); + rz_anal_hint_set_newbits (anal, 0x270, 32); + rz_anal_hint_set_size (anal, 0x280, 7); + rz_anal_hint_set_syntax (anal, 0x220, "intel"); + rz_anal_hint_set_opcode (anal, 0x290, "mov"); + rz_anal_hint_set_offset (anal, 0x2a0, "sometype"); + rz_anal_hint_set_esil (anal, 0x2b0, "13,29,+"); + rz_anal_hint_set_high (anal, 0x2c0); + rz_anal_hint_set_val (anal, 0x2d0, 54323); + + size_t i; + for (i = 0; i < ALL_OPTYPES_COUNT; i++) { + rz_anal_hint_set_type (anal, 0x1000 + i, all_optypes[i]); + } + + Sdb *db = sdb_new0 (); + rz_serialize_anal_hints_save (db, anal); + + Sdb *expected = hints_ref_db (); + assert_sdb_eq (db, expected, "hints save"); + sdb_free (db); + sdb_free (expected); + rz_anal_free (anal); + mu_end; +} + +static bool addr_hints_count_cb(ut64 addr, const RzVector/**/ *records, void *user) { + (*(size_t *)user) += records->len; + return true; +} + +static bool arch_hints_count_cb(ut64 addr, RZ_NULLABLE const char *arch, void *user) { + (*(size_t *)user)++; + return true; +} + +static bool bits_hints_count_cb(ut64 addr, int bits, void *user) { + (*(size_t *)user)++; + return true; +} + +bool test_anal_hints_load() { + RzAnal *anal = rz_anal_new (); + + Sdb *db = hints_ref_db (); + + bool succ = rz_serialize_anal_hints_load (db, anal, NULL); + mu_assert ("load success", succ); + + size_t count = 0; + rz_anal_addr_hints_foreach (anal, addr_hints_count_cb, &count); + rz_anal_arch_hints_foreach (anal, arch_hints_count_cb, &count); + rz_anal_bits_hints_foreach (anal, bits_hints_count_cb, &count); + mu_assert_eq (count, 19 + ALL_OPTYPES_COUNT, "hints count"); + + ut64 addr; + const char *arch = rz_anal_hint_arch_at (anal, 0x100, &addr); + mu_assert_streq (arch, "arm", "arch hint"); + mu_assert_eq (addr, 0x100, "arch hint addr"); + int bits = rz_anal_hint_bits_at(anal, 0x100, &addr); + mu_assert_eq (bits, 16, "bits hint"); + mu_assert_eq (addr, 0x100, "bits hint addr"); + arch = rz_anal_hint_arch_at (anal, 0x120, &addr); + mu_assert_null (arch, "arch hint"); + mu_assert_eq (addr, 0x120, "arch hint addr"); + bits = rz_anal_hint_bits_at(anal, 0x100, &addr); + mu_assert_eq (bits, 16, "bits hint"); + mu_assert_eq (addr, 0x100, "bits hint addr"); + +#define assert_addr_hint(addr, tp, check) do { \ + const RzVector/**/ *hints = rz_anal_addr_hints_at(anal, addr); \ + const RzAnalAddrHintRecord *record; \ + bool found = false; \ + rz_vector_foreach (hints, record) { \ + if (record->type == RZ_ANAL_ADDR_HINT_TYPE_##tp) { \ + check; \ + found = true; \ + break; \ + } \ + } \ + mu_assert ("addr hint", found); \ + } while(0) + + assert_addr_hint (0x200, IMMBASE, mu_assert_eq (record->immbase, 10, "immbase hint")); + assert_addr_hint (0x210, JUMP, mu_assert_eq (record->jump, 1337, "jump hint")); + assert_addr_hint (0x210, FAIL, mu_assert_eq (record->fail, 1234, "fail hint")); + assert_addr_hint (0x230, STACKFRAME, mu_assert_eq (record->stackframe, 0x30, "stackframe hint")); + assert_addr_hint (0x240, PTR, mu_assert_eq (record->ptr, 4321, "ptr hint")); + assert_addr_hint (0x250, NWORD, mu_assert_eq (record->nword, 3, "nword hint")); + assert_addr_hint (0x260, RET, mu_assert_eq (record->retval, 666, "ret hint")); + assert_addr_hint (0x270, NEW_BITS, mu_assert_eq (record->newbits, 32, "newbits hint")); + assert_addr_hint (0x280, SIZE, mu_assert_eq (record->size, 7, "size hint")); + assert_addr_hint (0x220, SYNTAX, mu_assert_streq (record->syntax, "intel", "syntax hint")); + assert_addr_hint (0x290, OPCODE, mu_assert_streq (record->opcode, "mov", "opcode hint")); + assert_addr_hint (0x2a0, TYPE_OFFSET, mu_assert_streq (record->type_offset, "sometype", "type offset hint")); + assert_addr_hint (0x2b0, ESIL, mu_assert_streq (record->esil, "13,29,+", "esil hint")); + assert_addr_hint (0x2c0, HIGH,); + assert_addr_hint (0x2d0, VAL, mu_assert_eq (record->val, 54323, "val hint")); + + size_t i; + for (i = 0; i < ALL_OPTYPES_COUNT; i++) { + assert_addr_hint (0x1000 + i, OPTYPE, mu_assert_eq (record->optype, all_optypes[i], "optype hint")); + } + + sdb_free (db); + rz_anal_free (anal); + mu_end; +} + +Sdb *classes_ref_db() { + Sdb *db = sdb_new0 (); + sdb_set (db, "Aeropause", "c", 0); + sdb_set (db, "Bright", "c", 0); + Sdb *attrs_db = sdb_ns (db, "attrs", true); + sdb_set (attrs_db, "attrtypes.Bright", "base", 0); + sdb_set (attrs_db, "attr.Aeropause.vtable.0", "0x1000,4,80", 0); + sdb_set (attrs_db, "attrtypes.Aeropause", "method,vtable", 0); + sdb_set (attrs_db, "attr.Aeropause.method", "some_meth,some_other_meth", 0); + sdb_set (attrs_db, "attr.Bright.base", "0", 0); + sdb_set (attrs_db, "attr.Aeropause.vtable", "0", 0); + sdb_set (attrs_db, "attr.Bright.base.0", "Aeropause,8", 0); + sdb_set (attrs_db, "attr.Aeropause.method.some_meth", "4919,42", 0); + sdb_set (attrs_db, "attr.Aeropause.method.some_other_meth", "4660,32", 0); + return db; +} + +bool test_anal_classes_save() { + RzAnal *anal = rz_anal_new (); + + rz_anal_class_create (anal, "Aeropause"); + RzAnalMethod crystal = { + .name = strdup ("some_meth"), + .addr = 0x1337, + .vtable_offset = 42 + }; + rz_anal_class_method_set (anal, "Aeropause", &crystal); + rz_anal_class_method_fini (&crystal); + + RzAnalMethod meth = { + .name = strdup ("some_other_meth"), + .addr = 0x1234, + .vtable_offset = 0x20 + }; + rz_anal_class_method_set (anal, "Aeropause", &meth); + rz_anal_class_method_fini (&meth); + + rz_anal_class_create (anal, "Bright"); + RzAnalBaseClass base = { + .id = NULL, + .offset = 8, + .class_name = strdup ("Aeropause") + }; + rz_anal_class_base_set (anal, "Bright", &base); + rz_anal_class_base_fini (&base); + + RzAnalVTable vt = { + .id = NULL, + .offset = 4, + .addr = 0x1000, + .size = 0x50 + }; + rz_anal_class_vtable_set (anal, "Aeropause", &vt); + rz_anal_class_vtable_fini (&vt); + + Sdb *db = sdb_new0 (); + rz_serialize_anal_classes_save (db, anal); + + Sdb *expected = classes_ref_db (); + assert_sdb_eq (db, expected, "classes save"); + sdb_free (db); + sdb_free (expected); + rz_anal_free (anal); + mu_end; +} + +bool test_anal_classes_load() { + RzAnal *anal = rz_anal_new (); + Sdb *db = classes_ref_db (); + bool succ = rz_serialize_anal_classes_load (db, anal, NULL); + sdb_free (db); + mu_assert ("load success", succ); + + SdbList *classes = rz_anal_class_get_all (anal, true); + mu_assert_eq (classes->length, 2, "classes count"); + SdbListIter *iter = ls_head (classes); + SdbKv *kv = ls_iter_get (iter); + mu_assert_streq (sdbkv_key (kv), "Aeropause", "class"); + kv = ls_iter_get (iter); + mu_assert_streq (sdbkv_key (kv), "Bright", "class"); + ls_free (classes); + + RzVector *vals = rz_anal_class_method_get_all (anal, "Aeropause"); + mu_assert_eq (vals->len, 2, "method count"); + RzAnalMethod *meth = rz_vector_index_ptr (vals, 0); + mu_assert_streq (meth->name, "some_meth", "method name"); + mu_assert_eq (meth->addr, 0x1337, "method addr"); + mu_assert_eq (meth->vtable_offset, 42, "method vtable offset"); + meth = rz_vector_index_ptr (vals, 1); + mu_assert_streq (meth->name, "some_other_meth", "method name"); + mu_assert_eq (meth->addr, 0x1234, "method addr"); + mu_assert_eq (meth->vtable_offset, 0x20, "method vtable offset"); + rz_vector_free (vals); + + vals = rz_anal_class_base_get_all (anal, "Aeropause"); + mu_assert_eq (vals->len, 0, "base count"); + rz_vector_free (vals); + + vals = rz_anal_class_vtable_get_all (anal, "Aeropause"); + mu_assert_eq (vals->len, 1, "vtable count"); + RzAnalVTable *vt = rz_vector_index_ptr (vals, 0); + mu_assert_eq (vt->offset, 4, "vtable offset"); + mu_assert_eq (vt->addr, 0x1000, "vtable addr"); + mu_assert_eq (vt->size, 0x50, "vtable size"); + rz_vector_free (vals); + + vals = rz_anal_class_method_get_all (anal, "Bright"); + mu_assert_eq (vals->len, 0, "method count"); + rz_vector_free (vals); + + vals = rz_anal_class_base_get_all (anal, "Bright"); + mu_assert_eq (vals->len, 1, "base count"); + RzAnalBaseClass *base = rz_vector_index_ptr (vals, 0); + mu_assert_eq (base->offset, 8, "base class offset"); + mu_assert_streq (base->class_name, "Aeropause", "base class name"); + rz_vector_free (vals); + + vals = rz_anal_class_vtable_get_all (anal, "Bright"); + mu_assert_eq (vals->len, 0, "vtable count"); + rz_vector_free (vals); + + rz_anal_free (anal); + mu_end; +} + +Sdb *types_ref_db() { + Sdb *db = sdb_new0 (); + sdb_set (db, "snatcher", "union", 0); + sdb_set (db, "struct.junker.gillian", "char *,0,0", 0); + sdb_set (db, "junker", "struct", 0); + sdb_set (db, "typedef.human", "union snatcher", 0); + sdb_set (db, "union.snatcher.random", "int,0,0", 0); + sdb_set (db, "human", "typedef", 0); + sdb_set (db, "struct.junker.seed", "uint64_t,8,0", 0); + sdb_set (db, "union.snatcher", "random,hajile", 0); + sdb_set (db, "struct.junker", "gillian,seed", 0); + sdb_set (db, "union.snatcher.hajile", "uint32_t,0,0", 0); + sdb_set (db, "badchar", "type", 0); + sdb_set (db, "type.badchar.size", "16", 0); + sdb_set (db, "type.badchar", "c", 0); + sdb_set (db, "enum.mika", "ELIJAH,MODNAR", 0); + sdb_set (db, "enum.mika.MODNAR", "0x539", 0); + sdb_set (db, "enum.mika.ELIJAH", "0x2a", 0); + sdb_set (db, "enum.mika.0x2a", "ELIJAH", 0); + sdb_set (db, "mika", "enum", 0); + sdb_set (db, "enum.mika.0x539", "MODNAR", 0); + return db; +} + +bool test_anal_types_save() { + RzAnal *anal = rz_anal_new (); + + // struct + RzAnalBaseType *type = rz_anal_base_type_new (RZ_ANAL_BASE_TYPE_KIND_STRUCT); + type->name = strdup ("junker"); + + RzAnalStructMember member; + member.name = strdup ("gillian"); + member.offset = 0; + member.type = strdup ("char *"); + rz_vector_push (&type->struct_data.members, &member); + + member.name = strdup ("seed"); + member.offset = 8; + member.type = strdup ("uint64_t"); + rz_vector_push (&type->struct_data.members, &member); + + rz_anal_save_base_type (anal, type); + rz_anal_base_type_free (type); + + // union + type = rz_anal_base_type_new (RZ_ANAL_BASE_TYPE_KIND_UNION); + type->name = strdup ("snatcher"); + + RzAnalUnionMember mumber; + mumber.name = strdup ("random"); + mumber.offset = 0; + mumber.type = strdup ("int"); + rz_vector_push (&type->union_data.members, &mumber); + + mumber.name = strdup ("hajile"); + mumber.offset = 0; + mumber.type = strdup ("uint32_t"); + rz_vector_push (&type->union_data.members, &mumber); + + rz_anal_save_base_type (anal, type); + rz_anal_base_type_free (type); + + // enum + type = rz_anal_base_type_new (RZ_ANAL_BASE_TYPE_KIND_ENUM); + type->name = strdup ("mika"); + + RzAnalEnumCase cas; + cas.name = strdup ("ELIJAH"); + cas.val = 42; + rz_vector_push (&type->enum_data.cases, &cas); + + cas.name = strdup ("MODNAR"); + cas.val = 1337; + rz_vector_push (&type->enum_data.cases, &cas); + + rz_anal_save_base_type (anal, type); + rz_anal_base_type_free (type); + + // typedef + type = rz_anal_base_type_new (RZ_ANAL_BASE_TYPE_KIND_TYPEDEF); + type->name = strdup ("human"); + type->type = strdup ("union snatcher"); + rz_anal_save_base_type (anal, type); + rz_anal_base_type_free (type); + + // atomic + type = rz_anal_base_type_new (RZ_ANAL_BASE_TYPE_KIND_ATOMIC); + type->name = strdup ("badchar"); + type->size = 16; + type->type = strdup ("c"); + rz_anal_save_base_type (anal, type); + rz_anal_base_type_free (type); + + Sdb *db = sdb_new0 (); + rz_serialize_anal_types_save (db, anal); + + Sdb *expected = types_ref_db (); + assert_sdb_eq (db, expected, "types save"); + sdb_free (db); + sdb_free (expected); + rz_anal_free (anal); + mu_end; +} + +bool test_anal_types_load() { + RzAnal *anal = rz_anal_new (); + Sdb *db = types_ref_db (); + bool succ = rz_serialize_anal_types_load (db, anal, NULL); + sdb_free (db); + mu_assert ("load success", succ); + + // struct + RzAnalBaseType *type = rz_anal_get_base_type (anal, "junker"); + mu_assert_notnull (type, "get type"); + mu_assert_eq (type->kind, RZ_ANAL_BASE_TYPE_KIND_STRUCT, "type kind"); + mu_assert_eq (type->struct_data.members.len, 2, "members count"); + + RzAnalStructMember *member = rz_vector_index_ptr (&type->struct_data.members, 0); + mu_assert_streq (member->name, "gillian", "member name"); + mu_assert_eq (member->offset, 0, "member offset"); + mu_assert_streq (member->type, "char *", "member type"); + + member = rz_vector_index_ptr (&type->struct_data.members, 1); + mu_assert_streq (member->name, "seed", "member name"); + mu_assert_eq (member->offset, 8, "member offset"); + mu_assert_streq (member->type, "uint64_t", "member type"); + + rz_anal_base_type_free (type); + + // union + type = rz_anal_get_base_type (anal, "snatcher"); + mu_assert_notnull (type, "get type"); + mu_assert_eq (type->kind, RZ_ANAL_BASE_TYPE_KIND_UNION, "type kind"); + mu_assert_eq (type->union_data.members.len, 2, "members count"); + + RzAnalUnionMember *mumber = rz_vector_index_ptr (&type->union_data.members, 0); + mu_assert_streq (mumber->name, "random", "member name"); + mu_assert_streq (mumber->type, "int", "member type"); + + mumber = rz_vector_index_ptr (&type->union_data.members, 1); + mu_assert_streq (mumber->name, "hajile", "member name"); + mu_assert_streq (mumber->type, "uint32_t", "member type"); + + rz_anal_base_type_free (type); + + // enum + type = rz_anal_get_base_type (anal, "mika"); + mu_assert_notnull (type, "get type"); + mu_assert_eq (type->kind, RZ_ANAL_BASE_TYPE_KIND_ENUM, "type kind"); + mu_assert_eq (type->enum_data.cases.len, 2, "cases count"); + + RzAnalEnumCase *cas = rz_vector_index_ptr (&type->enum_data.cases, 0); + mu_assert_streq (cas->name, "ELIJAH", "case name"); + mu_assert_eq (cas->val, 42, "case value"); + + cas = rz_vector_index_ptr (&type->enum_data.cases, 1); + mu_assert_streq (cas->name, "MODNAR", "case name"); + mu_assert_eq (cas->val, 1337, "case value"); + + rz_anal_base_type_free (type); + + // typedef + type = rz_anal_get_base_type (anal, "human"); + mu_assert_notnull (type, "get type"); + mu_assert_eq (type->kind, RZ_ANAL_BASE_TYPE_KIND_TYPEDEF, "type kind"); + mu_assert_streq (type->type, "union snatcher", "typedefd type"); + rz_anal_base_type_free (type); + + // atomic + type = rz_anal_get_base_type (anal, "badchar"); + mu_assert_notnull (type, "get type"); + mu_assert_eq (type->kind, RZ_ANAL_BASE_TYPE_KIND_ATOMIC, "type kind"); + mu_assert_eq (type->size, 16, "atomic type size"); + mu_assert_streq (type->type, "c", "atomic type"); + rz_anal_base_type_free (type); + + rz_anal_free (anal); + mu_end; +} + +Sdb *sign_ref_db() { + Sdb *db = sdb_new0 (); + Sdb *spaces = sdb_ns (db, "spaces", true); + sdb_set (spaces, "spacestack", "[\"koridai\"]", 0); + sdb_set (spaces, "name", "zs", 0); + Sdb *spaces_spaces = sdb_ns (spaces, "spaces", true); + sdb_set (spaces_spaces, "koridai", "s", 0); + sdb_set (db, "zign|*|sym.mahboi", "|s:4|b:deadbeef|m:c0ffee42|o:4919|g:7b0000000b0000000c0000000d0000002a000000|r:gwonam,link|x:king,ganon|v:r16,s42,b13|t:func.sym.mahboi.ret=char *,func.sym.mahboi.args=2,func.sym.mahboi.arg.0=\"int,arg0\",func.sym.mahboi.arg.1=\"uint32_t,die\"|c:This peace is what all true warriors strive for|n:sym.Mah.Boi|h:7bfa1358c427e26bc03c2384f41de7be6ebc01958a57e9a6deda5bdba9768851", 0); + sdb_set (db, "zign|koridai|sym.boring", "|c:gee it sure is boring around here", 0); + return db; +} + +bool test_anal_sign_save() { + RzAnal *anal = rz_anal_new (); + + RzSignItem *item = rz_sign_item_new (); + item->name = strdup ("sym.mahboi"); + item->realname = strdup ("sym.Mah.Boi"); + item->comment = strdup ("This peace is what all true warriors strive for"); + + item->bytes = RZ_NEW0 (RzSignBytes); + item->bytes->size = 4; + item->bytes->bytes = (ut8 *)strdup ("\xde\xad\xbe\xef"); + item->bytes->mask = (ut8 *)strdup ("\xc0\xff\xee\x42"); + + item->graph = RZ_NEW0 (RzSignGraph); + item->graph->bbsum = 42; + item->graph->cc = 123; + item->graph->ebbs = 13; + item->graph->edges = 12; + item->graph->nbbs = 11; + + item->addr = 0x1337; + + item->refs = rz_list_newf (free); + rz_list_append (item->refs, strdup ("gwonam")); + rz_list_append (item->refs, strdup ("link")); + + item->xrefs = rz_list_newf (free); + rz_list_append (item->xrefs, strdup ("king")); + rz_list_append (item->xrefs, strdup ("ganon")); + + item->vars = rz_list_newf (free); + rz_list_append (item->vars, strdup ("r16")); + rz_list_append (item->vars, strdup ("s42")); + rz_list_append (item->vars, strdup ("b13")); + + item->types = rz_list_newf (free); + rz_list_append (item->types, strdup ("func.sym.mahboi.ret=char *")); + rz_list_append (item->types, strdup ("func.sym.mahboi.args=2")); + rz_list_append (item->types, strdup ("func.sym.mahboi.arg.0=\"int,arg0\"")); + rz_list_append (item->types, strdup ("func.sym.mahboi.arg.1=\"uint32_t,die\"")); + + item->hash = RZ_NEW0 (RzSignHash); + item->hash->bbhash = strdup ("7bfa1358c427e26bc03c2384f41de7be6ebc01958a57e9a6deda5bdba9768851"); + + rz_sign_add_item (anal, item); + rz_sign_item_free (item); + + rz_spaces_set (&anal->zign_spaces, "koridai"); + rz_sign_add_comment (anal, "sym.boring", "gee it sure is boring around here"); + + Sdb *db = sdb_new0 (); + rz_serialize_anal_sign_save (db, anal); + + Sdb *expected = sign_ref_db (); + assert_sdb_eq (db, expected, "zignatures save"); + sdb_free (db); + sdb_free (expected); + rz_anal_free (anal); + mu_end; +} + +bool test_anal_sign_load() { + RzAnal *anal = rz_anal_new (); + Sdb *db = sign_ref_db (); + bool succ = rz_serialize_anal_sign_load (db, anal, NULL); + sdb_free (db); + mu_assert ("load success", succ); + + rz_spaces_set (&anal->zign_spaces, NULL); + RzSignItem *item = rz_sign_get_item (anal, "sym.mahboi"); + mu_assert_notnull (item, "get item"); + + mu_assert_streq (item->name, "sym.mahboi", "name"); + mu_assert_streq (item->realname, "sym.Mah.Boi", "realname"); + mu_assert_streq (item->comment, "This peace is what all true warriors strive for", "comment"); + mu_assert_notnull (item->bytes, "bytes"); + mu_assert_eq (item->bytes->size, 4, "bytes size"); + mu_assert_memeq (item->bytes->bytes, (ut8 *)"\xde\xad\xbe\xef", 4, "bytes bytes"); + mu_assert_memeq (item->bytes->mask, (ut8 *)"\xc0\xff\xee\x42", 4, "bytes mask"); + mu_assert_notnull (item->graph, "graph"); + mu_assert_eq (item->graph->bbsum, 42, "graph bbsum"); + mu_assert_eq (item->graph->cc, 123, "graph cc"); + mu_assert_eq (item->graph->ebbs, 13, "graph ebbs"); + mu_assert_eq (item->graph->edges, 12, "graph edges"); + mu_assert_eq (item->graph->nbbs, 11, "graph nbbs"); + mu_assert_eq (item->addr, 0x1337, "addr"); + mu_assert_notnull (item->refs, "refs"); + mu_assert_eq (rz_list_length (item->refs), 2, "refs count"); + mu_assert_streq (rz_list_get_n (item->refs, 0), "gwonam", "ref"); + mu_assert_streq (rz_list_get_n (item->refs, 1), "link", "ref"); + mu_assert_notnull (item->xrefs, "xrefs"); + mu_assert_eq (rz_list_length (item->xrefs), 2, "xrefs count"); + mu_assert_streq (rz_list_get_n (item->xrefs, 0), "king", "xref"); + mu_assert_streq (rz_list_get_n (item->xrefs, 1), "ganon", "xref"); + mu_assert_notnull (item->vars, "vars"); + mu_assert_eq (rz_list_length (item->vars), 3, "vars count"); + mu_assert_streq (rz_list_get_n (item->vars, 0), "r16", "var"); + mu_assert_streq (rz_list_get_n (item->vars, 1), "s42", "var"); + mu_assert_streq (rz_list_get_n (item->vars, 2), "b13", "var"); + mu_assert_notnull (item->types, "types"); + mu_assert_eq (rz_list_length (item->types), 4, "types count"); + mu_assert_streq (rz_list_get_n (item->types, 0), "func.sym.mahboi.ret=char *", "type"); + mu_assert_streq (rz_list_get_n (item->types, 1), "func.sym.mahboi.args=2", "type"); + mu_assert_streq (rz_list_get_n (item->types, 2), "func.sym.mahboi.arg.0=\"int,arg0\"", "type"); + mu_assert_streq (rz_list_get_n (item->types, 3), "func.sym.mahboi.arg.1=\"uint32_t,die\"", "type"); + mu_assert_notnull (item->hash, "hash"); + mu_assert_streq (item->hash->bbhash, "7bfa1358c427e26bc03c2384f41de7be6ebc01958a57e9a6deda5bdba9768851", "hash val"); + rz_sign_item_free (item); + + rz_spaces_set (&anal->zign_spaces, "koridai"); + item = rz_sign_get_item (anal, "sym.boring"); + mu_assert_notnull (item, "get item in space"); + mu_assert_streq (item->comment, "gee it sure is boring around here", "item in space comment"); + rz_sign_item_free (item); + + rz_anal_free (anal); + mu_end; +} + +static Sdb *cc_ref_db() { + Sdb *db = sdb_new0 (); + sdb_set (db, "cc.sectarian.ret", "rax", 0); + sdb_set (db, "cc.sectarian.self", "rsi", 0); + sdb_set (db, "cc.sectarian.error", "rdi", 0); + sdb_set (db, "cc.sectarian.arg1", "rcx", 0); + sdb_set (db, "cc.sectarian.arg0", "rdx", 0); + sdb_set (db, "cc.sectarian.argn", "stack", 0); + sdb_set (db, "sectarian", "cc", 0); + return db; +} + +bool test_anal_cc_save() { + RzAnal *anal = rz_anal_new (); + + rz_anal_cc_set (anal, "rax sectarian(rdx, rcx, stack)"); + rz_anal_cc_set_self (anal, "sectarian", "rsi"); + rz_anal_cc_set_error (anal, "sectarian", "rdi"); + + Sdb *db = sdb_new0 (); + rz_serialize_anal_cc_save (db, anal); + + Sdb *expected = cc_ref_db (); + assert_sdb_eq (db, expected, "cc save"); + sdb_free (db); + sdb_free (expected); + rz_anal_free (anal); + mu_end; +} + +bool test_anal_cc_load() { + RzAnal *anal = rz_anal_new (); + Sdb *db = cc_ref_db (); + bool succ = rz_serialize_anal_cc_load (db, anal, NULL); + sdb_free (db); + mu_assert ("load success", succ); + + char *v = rz_anal_cc_get (anal, "sectarian"); + mu_assert_streq (v, "rax rsi.sectarian (rdx, rcx, stack) rdi;", "get cc"); + free (v); + const char *vv = rz_anal_cc_self (anal, "sectarian"); + mu_assert_streq (vv, "rsi", "get self"); + vv = rz_anal_cc_error (anal, "sectarian"); + mu_assert_streq (vv, "rdi", "get error"); + + rz_anal_free (anal); + mu_end; +} + +Sdb *anal_ref_db() { + Sdb *db = sdb_new0 (); + + Sdb *blocks = sdb_ns (db, "blocks", true); + sdb_set (blocks, "0x4d2", "{\"size\":32}", 0); + sdb_set (blocks, "0x539", "{\"size\":42}", 0); + + Sdb *functions = sdb_ns (db, "functions", true); + sdb_set (functions, "0x4d2", "{\"name\":\"effekt\",\"bits\":32,\"type\":1,\"stack\":0,\"maxstack\":0,\"ninstr\":0,\"bp_frame\":true,\"diff\":{},\"bbs\":[1337]}", 0); + sdb_set (functions, "0x539", "{\"name\":\"hirsch\",\"bits\":32,\"type\":0,\"stack\":0,\"maxstack\":0,\"ninstr\":0,\"bp_frame\":true,\"diff\":{},\"bbs\":[1337,1234]}", 0); + + Sdb *xrefs = sdb_ns (db, "xrefs", true); + sdb_set (xrefs, "0x42", "[{\"to\":1337,\"type\":\"C\"}]", 0); + sdb_set (xrefs, "0x539", "[{\"to\":12648430,\"type\":\"d\"}]", 0); + + Sdb *meta = sdb_ns (db, "meta", true); + Sdb *meta_spaces = sdb_ns (meta, "spaces", true); + sdb_ns (meta_spaces, "spaces", true); + sdb_set (meta_spaces, "spacestack", "[\"*\"]", 0); + sdb_set (meta_spaces, "name", "CS", 0); + sdb_set (meta, "0x1337", "[{\"type\":\"C\",\"str\":\"some comment\"}]", 0); + + Sdb *hints = sdb_ns (db, "hints", true); + sdb_set (hints, "0x10e1", "{\"arch\":\"arm\"}", 0); + + Sdb *classes = sdb_ns (db, "classes", true); + sdb_set (classes, "Aeropause", "c", 0); + Sdb *class_attrs = sdb_ns (classes, "attrs", true); + sdb_set (class_attrs, "attrtypes.Aeropause", "method", 0); + sdb_set (class_attrs, "attr.Aeropause.method", "some_meth", 0); + sdb_set (class_attrs, "attr.Aeropause.method.some_meth", "4919,42", 0); + + Sdb *types = sdb_ns (db, "types", true); + sdb_set (types, "badchar", "type", 0); + sdb_set (types, "type.badchar.size", "16", 0); + sdb_set (types, "type.badchar", "c", 0); + + Sdb *zigns = sdb_ns (db, "zigns", true); + Sdb *zign_spaces = sdb_ns (zigns, "spaces", true); + sdb_set (zign_spaces, "spacestack", "[\"koridai\"]", 0); + sdb_set (zign_spaces, "name", "zs", 0); + Sdb *zign_spaces_spaces = sdb_ns (zign_spaces, "spaces", true); + sdb_set (zign_spaces_spaces, "koridai", "s", 0); + sdb_set (zigns, "zign|koridai|sym.boring", "|c:gee it sure is boring around here", 0); + + Sdb *imports = sdb_ns (db, "imports", true); + sdb_set (imports, "pigs", "i", 0); + sdb_set (imports, "dogs", "i", 0); + sdb_set (imports, "sheep", "i", 0); + + Sdb *pins = sdb_ns (db, "pins", true); + sdb_set (pins, "0x1337", "!sudo rm -rf /", 0); + sdb_set (pins, "0xc0ffee", "pd 42", 0); + + Sdb *cc = sdb_ns (db, "cc", true); + sdb_set (cc, "cc.sectarian.ret", "rax", 0); + sdb_set (cc, "cc.sectarian.arg1", "rcx", 0); + sdb_set (cc, "cc.sectarian.arg0", "rdx", 0); + sdb_set (cc, "cc.sectarian.argn", "stack", 0); + sdb_set (cc, "sectarian", "cc", 0); + + return db; +} + +bool test_anal_save() { + RzAnal *anal = rz_anal_new (); + + RzAnalBlock *ba = rz_anal_create_block (anal, 1337, 42); + RzAnalBlock *bb = rz_anal_create_block (anal, 1234, 32); + + RzAnalFunction *f = rz_anal_create_function (anal, "hirsch", 1337, RZ_ANAL_FCN_TYPE_NULL, NULL); + rz_anal_function_add_block (f, ba); + rz_anal_function_add_block (f, bb); + + f = rz_anal_create_function (anal, "effekt", 1234, RZ_ANAL_FCN_TYPE_FCN, NULL); + rz_anal_function_add_block (f, ba); + + rz_anal_block_unref (ba); + rz_anal_block_unref (bb); + + rz_anal_xrefs_set (anal, 0x42, 1337, RZ_ANAL_REF_TYPE_CALL); + rz_anal_xrefs_set (anal, 1337, 0xc0ffee, RZ_ANAL_REF_TYPE_DATA); + + rz_meta_set_string (anal, RZ_META_TYPE_COMMENT, 0x1337, "some comment"); + + rz_anal_hint_set_arch (anal, 4321, "arm"); + + rz_anal_class_create (anal, "Aeropause"); + RzAnalMethod crystal = { + .name = strdup ("some_meth"), + .addr = 0x1337, + .vtable_offset = 42 + }; + rz_anal_class_method_set (anal, "Aeropause", &crystal); + rz_anal_class_method_fini (&crystal); + + RzAnalBaseType *type = rz_anal_base_type_new (RZ_ANAL_BASE_TYPE_KIND_ATOMIC); + type->name = strdup ("badchar"); + type->size = 16; + type->type = strdup ("c"); + rz_anal_save_base_type (anal, type); + rz_anal_base_type_free (type); + + rz_spaces_set (&anal->zign_spaces, "koridai"); + rz_sign_add_comment (anal, "sym.boring", "gee it sure is boring around here"); + + rz_anal_add_import (anal, "pigs"); + rz_anal_add_import (anal, "dogs"); + rz_anal_add_import (anal, "sheep"); + + rz_anal_pin (anal, 0x1337, "!sudo rm -rf /"); + rz_anal_pin (anal, 0xc0ffee, "pd 42"); + + rz_anal_cc_set (anal, "rax sectarian(rdx, rcx, stack)"); + + Sdb *db = sdb_new0 (); + rz_serialize_anal_save (db, anal); + + Sdb *expected = anal_ref_db (); + assert_sdb_eq (db, expected, "anal save"); + sdb_free (db); + sdb_free (expected); + rz_anal_free (anal); + mu_end; +} + +bool test_anal_load() { + RzAnal *anal = rz_anal_new (); + + Sdb *db = anal_ref_db (); + bool succ = rz_serialize_anal_load (db, anal, NULL); + sdb_free (db); + mu_assert ("load success", succ); + + // all tested in detail by dedicated tests, we only check here + // if the things are loaded at all when loading a whole anal. + size_t blocks_count = 0; + RBIter iter; + RzAnalBlock *block; + rz_rbtree_foreach (anal->bb_tree, iter, block, RzAnalBlock, _rb) { + (void)block; + blocks_count++; + } + + mu_assert_eq (blocks_count, 2, "blocks loaded"); + mu_assert_eq (rz_list_length (anal->fcns), 2, "functions loaded"); + mu_assert_eq (rz_anal_xrefs_count (anal), 2, "xrefs loaded"); + + const char *cmt = rz_meta_get_string(anal, RZ_META_TYPE_COMMENT, 0x1337); + mu_assert_streq (cmt, "some comment", "meta"); + + const char *hint = rz_anal_hint_arch_at (anal, 4321, NULL); + mu_assert_streq (hint, "arm", "hint"); + + SdbList *classes = rz_anal_class_get_all (anal, true); + mu_assert_eq (classes->length, 1, "classes count"); + SdbListIter *siter = ls_head (classes); + SdbKv *kv = ls_iter_get (siter); + mu_assert_streq (sdbkv_key (kv), "Aeropause", "class"); + ls_free (classes); + RzVector *vals = rz_anal_class_method_get_all (anal, "Aeropause"); + mu_assert_eq (vals->len, 1, "method count"); + RzAnalMethod *meth = rz_vector_index_ptr (vals, 0); + mu_assert_streq (meth->name, "some_meth", "method name"); + rz_vector_free (vals); + + RzAnalBaseType *type = rz_anal_get_base_type (anal, "badchar"); + mu_assert_notnull (type, "get type"); + mu_assert_eq (type->kind, RZ_ANAL_BASE_TYPE_KIND_ATOMIC, "type kind"); + mu_assert_eq (type->size, 16, "atomic type size"); + mu_assert_streq (type->type, "c", "atomic type"); + rz_anal_base_type_free (type); + + rz_spaces_set (&anal->zign_spaces, "koridai"); + RzSignItem *item = rz_sign_get_item (anal, "sym.boring"); + mu_assert_notnull (item, "get item in space"); + mu_assert_streq (item->comment, "gee it sure is boring around here", "item in space comment"); + rz_sign_item_free (item); + + mu_assert_eq (rz_list_length (anal->imports), 3, "imports count"); + mu_assert_notnull (rz_list_find (anal->imports, "pigs", (RzListComparator)strcmp), "import"); + mu_assert_notnull (rz_list_find (anal->imports, "dogs", (RzListComparator)strcmp), "import"); + mu_assert_notnull (rz_list_find (anal->imports, "sheep", (RzListComparator)strcmp), "import"); + + size_t pin_count = sdb_count (anal->sdb_pins); + mu_assert_eq (pin_count, 2, "pins count"); + const char *pin = rz_anal_pin_call (anal, 0x1337); + mu_assert_streq (pin, "!sudo rm -rf /", "pin"); + pin = rz_anal_pin_call (anal, 0xc0ffee); + mu_assert_streq (pin, "pd 42", "pin"); + + char *cc = rz_anal_cc_get (anal, "sectarian"); + mu_assert_streq (cc, "rax sectarian (rdx, rcx, stack);", "get cc"); + free (cc); + + rz_anal_free (anal); + mu_end; +} + +int all_tests() { + mu_run_test (test_anal_diff_save); + mu_run_test (test_anal_diff_load); + mu_run_test (test_anal_switch_op_save); + mu_run_test (test_anal_switch_op_load); + mu_run_test (test_anal_block_save); + mu_run_test (test_anal_block_load); + mu_run_test (test_anal_function_save); + mu_run_test (test_anal_function_load); + mu_run_test (test_anal_var_save); + mu_run_test (test_anal_var_load); + mu_run_test (test_anal_xrefs_save); + mu_run_test (test_anal_xrefs_load); + mu_run_test (test_anal_meta_save); + mu_run_test (test_anal_meta_load); + mu_run_test (test_anal_hints_save); + mu_run_test (test_anal_hints_load); + mu_run_test (test_anal_classes_save); + mu_run_test (test_anal_classes_load); + mu_run_test (test_anal_types_save); + mu_run_test (test_anal_types_load); + mu_run_test (test_anal_sign_save); + mu_run_test (test_anal_sign_load); + mu_run_test (test_anal_cc_save); + mu_run_test (test_anal_cc_load); + mu_run_test (test_anal_save); + mu_run_test (test_anal_load); + return tests_passed != tests_run; +} + +int main(int argc, char **argv) { + return all_tests(); +} diff --git a/test/unit/test_serialize_config.c b/test/unit/test_serialize_config.c new file mode 100644 index 0000000000..8100d2e20f --- /dev/null +++ b/test/unit/test_serialize_config.c @@ -0,0 +1,61 @@ + +#include +#include "minunit.h" +#include "test_sdb.h" + +Sdb *ref_db() { + Sdb *db = sdb_new0 (); + sdb_set (db, "somestring", "somevalue", 0); + sdb_set (db, "someint", "42", 0); + sdb_set (db, "somebiggerint", "0x00001337", 0); + return db; +} + +bool test_config_save() { + RzConfig *config = rz_config_new (NULL); + rz_config_set (config, "somestring", "somevalue"); + rz_config_set_i (config, "someint", 42); + rz_config_set_i (config, "somebiggerint", 0x1337); + rz_config_lock (config, true); + + Sdb *db = sdb_new0 (); + rz_serialize_config_save (db, config); + rz_config_free (config); + + Sdb *expected = ref_db (); + assert_sdb_eq (db, expected, "config save"); + sdb_free (db); + sdb_free (expected); + mu_end; +} + +bool test_config_load() { + RzConfig *config = rz_config_new (NULL); + rz_config_set (config, "somestring", "someoldvalue"); + rz_config_set_i (config, "someint", 0); + rz_config_set_i (config, "somebiggerint", 0); + rz_config_lock (config, true); + + Sdb *db = ref_db (); + sdb_set (db, "sneaky", "not part of config", 0); + bool suck = rz_serialize_config_load (db, config, NULL); + sdb_free (db); + mu_assert ("load success", suck); + + mu_assert_eq (rz_list_length (config->nodes), 3, "count after load"); + mu_assert_streq (rz_config_get (config, "somestring"), "somevalue", "loaded config string"); + mu_assert_eq_fmt (rz_config_get_i (config, "someint"), (ut64)42, "loaded config int", "%"PFMT64u); + mu_assert_eq_fmt (rz_config_get_i (config, "somebiggerint"), (ut64)0x1337, "loaded config bigger int", "0x%"PFMT64x); + rz_config_free (config); + mu_end; +} + +int all_tests() { + mu_run_test (test_config_save); + mu_run_test (test_config_load); + return tests_passed != tests_run; +} + +int main(int argc, char **argv) { + return all_tests(); +} diff --git a/test/unit/test_serialize_flag.c b/test/unit/test_serialize_flag.c new file mode 100644 index 0000000000..cb661cfadb --- /dev/null +++ b/test/unit/test_serialize_flag.c @@ -0,0 +1,241 @@ + +#include +#include "minunit.h" +#include "test_sdb.h" + +Sdb *ref_0_db() { + Sdb *db = sdb_new0 (); + + sdb_set (db, "base", "-1337", 0); + sdb_set (db, "realnames", "1", 0); + + Sdb *spaces_db = sdb_ns (db, "spaces", true); + sdb_set (spaces_db, "name", "fs", 0); + sdb_set (spaces_db, "spacestack", "[\"reveries\"]", 0); + Sdb *spaces_spaces_db = sdb_ns (spaces_db, "spaces", true); + sdb_set (spaces_spaces_db, "ghost", "s", 0); + sdb_set (spaces_spaces_db, "reveries", "s", 0); + + Sdb *tags_db = sdb_ns (db, "tags", true); + sdb_set (tags_db, "tag."PERTURBATOR, PERTURBATOR, 0); + sdb_set (tags_db, "tag.lotus", "eater", 0); + + Sdb *zones_db = sdb_ns (db, "zones", true); + sdb_set (zones_db, "blackwater park", "{\"from\":12345,\"to\":12648243}", 0); + sdb_set (zones_db, PERTURBATOR, "{\"from\":3735928559,\"to\":18446744073709551614}", 0); + + Sdb *flags_db = sdb_ns (db, "flags", true); + sdb_set (flags_db, "damnation", "{\"realname\":\"Damnation\",\"demangled\":true,\"offset\":3582,\"size\":16,\"space\":\"reveries\",\"color\":\"white\",\"comment\":\"windowpane\",\"alias\":\"d4mn4t10n\"}", 0); + sdb_set (flags_db, "d4mn4t10n", "{\"realname\":\"d4mn4t10n\",\"demangled\":false,\"offset\":3582,\"size\":1}", 0); + sdb_set (flags_db, "deliverance", "{\"realname\":\"deliverance\",\"demangled\":false,\"offset\":66,\"size\":19}", 0); + + return db; +} + +RzFlag *ref_0_flag() { + RzFlag *flag = rz_flag_new (); + + flag->base = -1337; + flag->realnames = true; + + rz_flag_set (flag, "deliverance", 0x42 + 1337, 0x13); + rz_flag_set (flag, "d4mn4t10n", 0x1337, 1); + + rz_flag_space_set (flag, "ghost"); + rz_flag_space_set (flag, "reveries"); + + RzFlagItem *damnation = rz_flag_set (flag, "damnation", 0x1337, 0x10); + damnation->demangled = true; + rz_flag_item_set_realname (damnation, "Damnation"); + rz_flag_item_set_color (damnation, "white"); + rz_flag_item_set_comment (damnation, "windowpane"); + rz_flag_item_set_alias (damnation, "d4mn4t10n"); + + rz_flag_tags_set (flag, "lotus", "eater"); + rz_flag_tags_set (flag, PERTURBATOR, PERTURBATOR); + + rz_flag_zone_add (flag, PERTURBATOR, 0xdeadbeef); + rz_flag_zone_add (flag, PERTURBATOR, UT64_MAX - 1); + rz_flag_zone_add (flag, "blackwater park", 0xc0ff33); + rz_flag_zone_add (flag, "blackwater park", 12345); + + return flag; +} + +Sdb *ref_1_db() { + Sdb *db = sdb_new0 (); + + sdb_set (db, "base", "0", 0); + sdb_set (db, "realnames", "0", 0); + + Sdb *spaces_db = sdb_ns (db, "spaces", true); + sdb_set (spaces_db, "name", "fs", 0); + sdb_set (spaces_db, "spacestack", "[\"*\"]", 0); + sdb_ns (spaces_db, "spaces", true); + sdb_ns (db, "tags", true); + sdb_ns (db, "zones", true); + sdb_ns (db, "flags", true); + + return db; +} + +RzFlag *ref_1_flag() { + RzFlag *flag = rz_flag_new (); + flag->base = 0; + flag->realnames = false; + return flag; +} + +static bool test_save(RzFlag *flag, Sdb *ref) { + Sdb *db = sdb_new0 (); + rz_serialize_flag_save (db, flag); + assert_sdb_eq (db, ref, "save"); + sdb_free (db); + sdb_free (ref); + rz_flag_free (flag); + return true; +} + +static bool space_eq(RSpace *actual, RSpace *expected) { + mu_assert ("space null", (!actual) == (!expected)); + if (expected != NULL) { + mu_assert_streq (actual->name, expected->name, "space name"); + } + return true; +} + +static bool spaces_eq(RSpaces *actual, RSpaces *expected) { + assert_streq_null (actual->name, expected->name, "spaces name"); + + RBIter actual_iter = rz_rbtree_first (actual->spaces); + RBIter expected_iter = rz_rbtree_first (expected->spaces); + while (rz_rbtree_iter_has (&actual_iter) && rz_rbtree_iter_has (&expected_iter)) { + RSpace *actual_space = rz_rbtree_iter_get (&actual_iter, RSpace, rb); + RSpace *expected_space = rz_rbtree_iter_get (&expected_iter, RSpace, rb); + if (!space_eq (actual_space, expected_space)) { + return false; + } + rz_rbtree_iter_next (&actual_iter); + rz_rbtree_iter_next (&expected_iter); + } + mu_assert ("spaces count", !rz_rbtree_iter_has (&actual_iter) && !rz_rbtree_iter_has (&expected_iter)); + + if (!space_eq (actual->current, expected->current)) { + return false; + } + + RzListIter *actual_stack_iter = rz_list_iterator (actual->spacestack); + RzListIter *expected_stack_iter = rz_list_iterator (expected->spacestack); + while (actual_stack_iter && expected_stack_iter) { + RSpace *actual_space = rz_list_iter_get (actual_stack_iter); + RSpace *expected_space = rz_list_iter_get (expected_stack_iter); + if (!space_eq (actual_space, expected_space)) { + return false; + } + } + mu_assert ("spacestack count", !actual_stack_iter && !expected_stack_iter); + + return true; +} + +typedef struct { + bool equal; + RzFlag *other; +} FlagCmpCtx; + +static bool flag_cmp(RzFlagItem *actual, RzFlagItem *expected) { + mu_assert_notnull (expected, "flag"); + assert_streq_null (actual->realname, expected->realname, "flag realname"); + mu_assert_eq (actual->demangled, expected->demangled, "flag demangled"); + mu_assert_eq_fmt (actual->offset, expected->offset, "flag offset", "0x%"PFMT64x); + mu_assert_eq_fmt (actual->size, expected->size, "flag size", "0x%"PFMT64x); + mu_assert_eq (!actual->space, !expected->space, "flag space null"); + if (expected->space) { + mu_assert_streq (actual->space->name, expected->space->name, "flag space"); + } + assert_streq_null (actual->color, expected->color, "flag color"); + assert_streq_null (actual->comment, expected->comment, "flag comment"); + assert_streq_null (actual->alias, expected->alias, "flag alias"); + return true; +} + +static bool flag_cmp_cb(RzFlagItem *fi, void *user) { + FlagCmpCtx *ctx = user; + RzFlagItem *fo = rz_flag_get (ctx->other, fi->name); + if(!flag_cmp (fi, fo)) { + ctx->equal = false; + return false; + } + return true; +} + +static bool test_load(Sdb *db, RzFlag *ref) { + RzFlag *flag = rz_flag_new (); + + bool suck = rz_serialize_flag_load (db, flag, NULL); + sdb_free (db); + mu_assert ("load success", suck); + + if (!spaces_eq (&flag->spaces, &ref->spaces)) { + return false; + } + + size_t zones_length_actual = flag->zones ? rz_list_length (flag->zones) : 0; + size_t zones_length_expect = ref->zones ? rz_list_length (ref->zones) : 0; + mu_assert_eq (zones_length_actual, zones_length_expect, "zones count"); + RzListIter *actual_iter; + RzFlagZoneItem *actual_zone; + rz_list_foreach (flag->zones, actual_iter, actual_zone) { + RzListIter *expected_iter; + RzFlagZoneItem *expected_zone; + rz_list_foreach (ref->zones, expected_iter, expected_zone) { + if (strcmp (actual_zone->name, expected_zone->name) != 0) { + continue; + } + mu_assert_streq (actual_zone->name, expected_zone->name, "zone name"); + mu_assert_eq_fmt (actual_zone->from, expected_zone->from, "zone from", "0x%"PFMT64x); + mu_assert_eq_fmt (actual_zone->to, expected_zone->to, "zone from", "0x%"PFMT64x); + goto kontinju; + } + mu_assert ("zone", false); +kontinju: + continue; + } + + mu_assert_eq_fmt (flag->base, ref->base, "base", "0x%"PFMT64x); + mu_assert_eq (flag->realnames, ref->realnames, "realnames"); + assert_sdb_eq (flag->tags, ref->tags, "tags"); + + mu_assert_eq (rz_flag_count (flag, NULL), rz_flag_count (ref, NULL), "flags count"); + FlagCmpCtx cmp_ctx = { true, ref }; + rz_flag_foreach (flag, flag_cmp_cb, &cmp_ctx); + + rz_flag_free (flag); + rz_flag_free (ref); + return true; +} + +#define TEST_CALL(name, call) \ +bool name() { \ + if (!(call)) { \ + return false; \ + } \ + mu_end; \ +} + +TEST_CALL (test_flag_0_save, test_save(ref_0_flag (), ref_0_db ())); +TEST_CALL (test_flag_1_save, test_save(ref_1_flag (), ref_1_db ())); +TEST_CALL (test_flag_0_load, test_load(ref_0_db (), ref_0_flag ())); +TEST_CALL (test_flag_1_load, test_load(ref_1_db (), ref_1_flag ())); + +int all_tests() { + mu_run_test (test_flag_0_save); + mu_run_test (test_flag_1_save); + mu_run_test (test_flag_0_load); + mu_run_test (test_flag_1_load); + return tests_passed != tests_run; +} + +int main(int argc, char **argv) { + return all_tests(); +} diff --git a/test/unit/test_serialize_spaces.c b/test/unit/test_serialize_spaces.c new file mode 100644 index 0000000000..e6c5ee18b9 --- /dev/null +++ b/test/unit/test_serialize_spaces.c @@ -0,0 +1,155 @@ + +#include +#include "minunit.h" +#include "test_sdb.h" + +bool test_spaces_save(void) { + RSpaces *spaces = rz_spaces_new ("myspaces"); + rz_spaces_add (spaces, "a"); + rz_spaces_add (spaces, "b"); + rz_spaces_add (spaces, "c"); + rz_spaces_add (spaces, PERTURBATOR); + + Sdb *db = sdb_new0 (); + rz_serialize_spaces_save (db, spaces); + + Sdb *expected = sdb_new0 (); + sdb_set (expected, "name", "myspaces", 0); + sdb_set (expected, "spacestack", "[\"*\"]", 0); + Sdb *expected_spaces = sdb_ns (expected, "spaces", true); + sdb_set (expected_spaces, "a", "s", 0); + sdb_set (expected_spaces, "b", "s", 0); + sdb_set (expected_spaces, "c", "s", 0); + sdb_set (expected_spaces, PERTURBATOR, "s", 0); + + assert_sdb_eq (db, expected, "spaces save (no current, empty stack)"); + sdb_free (db); + + rz_spaces_set (spaces, PERTURBATOR); + db = sdb_new0 (); + rz_serialize_spaces_save (db, spaces); + + sdb_set (expected, "spacestack", "[\""PERTURBATOR_JSON"\"]", 0); + + assert_sdb_eq (db, expected, "spaces save (current, empty stack)"); + sdb_free (db); + + rz_spaces_push (spaces, "a"); + rz_spaces_push (spaces, "b"); + db = sdb_new0 (); + rz_serialize_spaces_save (db, spaces); + + sdb_set (expected, "spacestack", "[\""PERTURBATOR_JSON"\",\"a\",\"b\"]", 0); + assert_sdb_eq (db, expected, "spaces save (current, stack)"); + + sdb_free (db); + sdb_free (expected); + rz_spaces_free (spaces); + mu_end; +} + +bool test_spaces_load_noname_nostack(void) { + Sdb *db = sdb_new0 (); + sdb_set (db, "name", "myspaces", 0); + sdb_set (db, "spacestack", "[\"*\"]", 0); + Sdb *db_spaces = sdb_ns (db, "spaces", true); + sdb_set (db_spaces, "a", "s", 0); + sdb_set (db_spaces, "b", "s", 0); + sdb_set (db_spaces, "c", "s", 0); + sdb_set (db_spaces, PERTURBATOR, "s", 0); + + RSpaces *spaces = rz_spaces_new ("fixed name"); + bool suck = rz_serialize_spaces_load (db, spaces, false, NULL); + mu_assert ("load success", suck); + mu_assert_streq (spaces->name, "fixed name", "spaces load without name"); + mu_assert_null (spaces->current, "spaces load no current"); + mu_assert_eq (rz_list_length (spaces->spacestack), 0, "empty spacestack"); + RBIter rbiter; + RSpace *space; + int i = 0; + rz_rbtree_foreach (spaces->spaces, rbiter, space, RSpace, rb) { + switch (i) { + case 0: + mu_assert_streq (space->name, PERTURBATOR, "loaded spaces"); + break; + case 1: + mu_assert_streq (space->name, "a", "loaded spaces"); + break; + case 2: + mu_assert_streq (space->name, "b", "loaded spaces"); + break; + case 3: + mu_assert_streq (space->name, "c", "loaded spaces"); + break; + default: + break; + } + i++; + } + mu_assert_eq (i, 4, "loaded spaces count"); + rz_spaces_free (spaces); + + sdb_free (db); + mu_end; +} + +bool test_spaces_load_name_stack(void) { + Sdb *db = sdb_new0 (); + sdb_set (db, "name", "myspaces", 0); + sdb_set (db, "spacestack", "[\"a\",\"*\",\""PERTURBATOR_JSON"\",\"b\",\""PERTURBATOR_JSON"\"]", 0); + Sdb *db_spaces = sdb_ns (db, "spaces", true); + sdb_set (db_spaces, "a", "s", 0); + sdb_set (db_spaces, "b", "s", 0); + sdb_set (db_spaces, "c", "s", 0); + sdb_set (db_spaces, PERTURBATOR, "s", 0); + + RSpaces *spaces = rz_spaces_new (""); + bool suck = rz_serialize_spaces_load (db, spaces, true, NULL); + mu_assert ("load success", suck); + mu_assert_streq (spaces->name, "myspaces", "loaded name"); + mu_assert_notnull (spaces->current, "current non-null"); + mu_assert_streq (spaces->current->name, PERTURBATOR, "current"); + mu_assert_eq (rz_list_length (spaces->spacestack), 4, "spacestack size"); + mu_assert_streq ((const char *)rz_list_get_n (spaces->spacestack, 0), "a", "spacestack"); + mu_assert_streq ((const char *)rz_list_get_n (spaces->spacestack, 1), "*", "spacestack"); + mu_assert_streq ((const char *)rz_list_get_n (spaces->spacestack, 2), PERTURBATOR, "spacestack"); + mu_assert_streq ((const char *)rz_list_get_n (spaces->spacestack, 3), "b", "spacestack"); + RBIter rbiter; + RSpace *space; + int i = 0; + rz_rbtree_foreach (spaces->spaces, rbiter, space, RSpace, rb) { + switch (i) { + case 0: + mu_assert_streq (space->name, PERTURBATOR, "loaded spaces"); + break; + case 1: + mu_assert_streq (space->name, "a", "loaded spaces"); + break; + case 2: + mu_assert_streq (space->name, "b", "loaded spaces"); + break; + case 3: + mu_assert_streq (space->name, "c", "loaded spaces"); + break; + default: + break; + } + i++; + } + mu_assert_eq (i, 4, "loaded spaces count"); + rz_spaces_free (spaces); + + sdb_free (db); + mu_end; +} + +int all_tests() { + mu_run_test (test_spaces_save); + mu_run_test (test_spaces_load_noname_nostack); + mu_run_test (test_spaces_load_name_stack); + return tests_passed != tests_run; +} + +int main(int argc, char **argv) { + return all_tests(); +}