* Save/load seek history ##projects * Add unit test for seek history serialization * Bump project version to 15 for seek history serialization
640 lines
21 KiB
C
640 lines
21 KiB
C
// SPDX-FileCopyrightText: 2021 Florian Märkl <info@florianmaerkl.de>
|
|
// SPDX-License-Identifier: LGPL-3.0-only
|
|
|
|
#include "sdb.h"
|
|
#include <rz_project.h>
|
|
#include <rz_util/rz_pj.h>
|
|
|
|
/**
|
|
* \file
|
|
* About Project Migrations:
|
|
* The project format is versioned, which means that a project saved with a certain version
|
|
* will always follow the exact same format.
|
|
* The current version is defined as `RZ_PROJECT_VERSION` and is completely independent of
|
|
* Rizin release versions. Do not confuse them!
|
|
* When any change in Rizin happens that changes the project format, this version must be raised
|
|
* and a migration must be implemented, which converts a given project from the previous version
|
|
* to the current and will be executed when loading an old project with new Rizin.
|
|
* These migrations always bring a project from version x to version x+1 and they may be executed
|
|
* sequentially if multiple versions are skipped. For example loading a project with version 1 in
|
|
* Rizin with version 3 will execute (1->2), then (2->3) and finally load the project just like
|
|
* any regular project of version 3.
|
|
*
|
|
* After introducing format changes in Rizin, do the following:
|
|
* * Raise RZ_PROJECT_VERSION by exactly 1.
|
|
* * Implement a function like `bool rz_project_migrate_migrate_v1_v2(RzProject *prj, RzSerializeResultInfo *res)`
|
|
* which edits prj in-place and converts it from the previous to the current version.
|
|
* * Append this function to the `migrations` array below.
|
|
* * Implement tests in `test/unit/test_project_migrate.c` that cover all changes (see the documentation there).
|
|
*/
|
|
|
|
// --
|
|
// Migration 1 -> 2
|
|
//
|
|
// Changes from 788fdb3d8ef98f50d61cdee72e0b57c74c814022:
|
|
// Information which addresses are marked as noreturn was previosly saved in "/core/analysis/types" as records like:
|
|
// addr.1337.noreturn=true
|
|
// Now it is available in "/core/analysis/noreturn" while actual type-related info stays in "/core/analysis/types".
|
|
|
|
typedef struct {
|
|
RzList /*<char *>*/ *moved_keys; ///< deferred for deletion from the old sdb
|
|
Sdb *noreturn_db;
|
|
} V1V2TypesCtx;
|
|
|
|
bool v1_v2_types_foreach_cb(void *user, const char *k, const char *v) {
|
|
if (!rz_str_startswith(k, "addr.") || !rz_str_endswith(k, ".noreturn")) {
|
|
return true;
|
|
}
|
|
V1V2TypesCtx *ctx = user;
|
|
sdb_set(ctx->noreturn_db, k, v, 0);
|
|
rz_list_push(ctx->moved_keys, strdup(k));
|
|
return true;
|
|
}
|
|
|
|
RZ_API bool rz_project_migrate_v1_v2(RzProject *prj, RzSerializeResultInfo *res) {
|
|
Sdb *core_db;
|
|
RZ_SERIALIZE_SUB(prj, core_db, res, "core", return false;);
|
|
Sdb *analysis_db;
|
|
RZ_SERIALIZE_SUB(core_db, analysis_db, res, "analysis", return false;);
|
|
Sdb *types_db;
|
|
RZ_SERIALIZE_SUB(analysis_db, types_db, res, "types", return false;);
|
|
V1V2TypesCtx ctx = {
|
|
.moved_keys = rz_list_newf(free),
|
|
.noreturn_db = sdb_ns(analysis_db, "noreturn", true)
|
|
};
|
|
if (!ctx.moved_keys || !ctx.noreturn_db) {
|
|
return false;
|
|
}
|
|
sdb_foreach(types_db, v1_v2_types_foreach_cb, &ctx);
|
|
RzListIter *it;
|
|
char *s;
|
|
rz_list_foreach (ctx.moved_keys, it, s) {
|
|
sdb_unset(types_db, s, 0);
|
|
}
|
|
rz_list_free(ctx.moved_keys);
|
|
return true;
|
|
}
|
|
|
|
// --
|
|
// Migration 2 -> 3
|
|
//
|
|
// Changes from 788fdb3d8ef98f50d61cdee72e0b57c74c814022:
|
|
// Types database "analysis/types" was converted into two
|
|
// separate SDBs - types and callables
|
|
// Thus all "func.*" keys from "/core/analysis/types" should be moved to
|
|
// "/core/analysis/callables"
|
|
// Type links information is also separated now from
|
|
// "/core/analysis/types" to "/core/analysis/typelinks"
|
|
|
|
typedef struct {
|
|
RzList /*<char *>*/ *moved_keys; ///< deferred for deletion from the old sdb
|
|
Sdb *callables_db;
|
|
Sdb *typelinks_db;
|
|
} V2V3TypesCtx;
|
|
|
|
bool v2_v3_types_foreach_cb(void *user, const char *k, const char *v) {
|
|
V2V3TypesCtx *ctx = user;
|
|
if (rz_str_startswith(k, "func.") || !strcmp(v, "func")) {
|
|
sdb_set(ctx->callables_db, k, v, 0);
|
|
rz_list_push(ctx->moved_keys, strdup(k));
|
|
} else if (rz_str_startswith(k, "link.")) {
|
|
// Old addresses were stored as hexadecimal numbers without `0x` part
|
|
// New addresses have them
|
|
char *tl_key = rz_str_newf("0x%s", k + strlen("link."));
|
|
sdb_set(ctx->typelinks_db, tl_key, v, 0);
|
|
free(tl_key);
|
|
rz_list_push(ctx->moved_keys, strdup(k));
|
|
}
|
|
return true;
|
|
}
|
|
|
|
RZ_API bool rz_project_migrate_v2_v3(RzProject *prj, RzSerializeResultInfo *res) {
|
|
Sdb *core_db;
|
|
RZ_SERIALIZE_SUB(prj, core_db, res, "core", return false;);
|
|
Sdb *analysis_db;
|
|
RZ_SERIALIZE_SUB(core_db, analysis_db, res, "analysis", return false;);
|
|
Sdb *types_db;
|
|
RZ_SERIALIZE_SUB(analysis_db, types_db, res, "types", return false;);
|
|
V2V3TypesCtx ctx = {
|
|
.moved_keys = rz_list_newf(free),
|
|
.callables_db = sdb_ns(analysis_db, "callables", true),
|
|
.typelinks_db = sdb_ns(analysis_db, "typelinks", true)
|
|
};
|
|
if (!ctx.moved_keys || !ctx.callables_db || !ctx.typelinks_db) {
|
|
return false;
|
|
}
|
|
sdb_foreach(types_db, v2_v3_types_foreach_cb, &ctx);
|
|
RzListIter *it;
|
|
char *s;
|
|
rz_list_foreach (ctx.moved_keys, it, s) {
|
|
sdb_unset(types_db, s, 0);
|
|
}
|
|
rz_list_free(ctx.moved_keys);
|
|
return true;
|
|
}
|
|
|
|
// --
|
|
// Migration 3 -> 4
|
|
//
|
|
// Changes from 8d54707ccf8b3492b999dfa42057da0847acb952:
|
|
// Added new global variables in "/core/analysis/vars"
|
|
|
|
RZ_API bool rz_project_migrate_v3_v4(RzProject *prj, RzSerializeResultInfo *res) {
|
|
Sdb *core_db;
|
|
RZ_SERIALIZE_SUB(prj, core_db, res, "core", return false;);
|
|
Sdb *analysis_db;
|
|
RZ_SERIALIZE_SUB(core_db, analysis_db, res, "analysis", return false;);
|
|
sdb_ns(analysis_db, "vars", true);
|
|
// Typelinks currently still exist. When they will be removed, the following
|
|
// code can be enabled and moved to the respective migration.
|
|
#if 0
|
|
V3V4TypesCtx ctx = {
|
|
.moved_keys = rz_list_newf(free),
|
|
.global_vars_db = sdb_ns(analysis_db, "vars", true)
|
|
};
|
|
|
|
if (!ctx.moved_keys || !ctx.global_vars_db) {
|
|
return false;
|
|
}
|
|
Sdb *typelinks_db = sdb_ns(analysis_db, "typelinks", true);
|
|
sdb_foreach(typelinks_db, v3_v4_types_foreach_cb, &ctx);
|
|
RzListIter *it;
|
|
char *s;
|
|
rz_list_foreach (ctx.moved_keys, it, s) {
|
|
sdb_unset(typelinks_db, s, 0);
|
|
}
|
|
rz_list_free(ctx.moved_keys);
|
|
#endif
|
|
return true;
|
|
}
|
|
|
|
// --
|
|
// Migration 4 -> 5
|
|
//
|
|
// Changes from a523314fa7c8fde0c2c0d116e82aa77f62991d37
|
|
// Added new type called `unknown_t` in "/core/analysis/types"
|
|
// It is used for all unknown types during the project loading and saving
|
|
|
|
RZ_API bool rz_project_migrate_v4_v5(RzProject *prj, RzSerializeResultInfo *res) {
|
|
Sdb *core_db;
|
|
RZ_SERIALIZE_SUB(prj, core_db, res, "core", return false;);
|
|
Sdb *analysis_db;
|
|
RZ_SERIALIZE_SUB(core_db, analysis_db, res, "analysis", return false;);
|
|
Sdb *config_db;
|
|
RZ_SERIALIZE_SUB(core_db, config_db, res, "config", return false;);
|
|
Sdb *types_db;
|
|
RZ_SERIALIZE_SUB(analysis_db, types_db, res, "types", return false;);
|
|
// Common keys:
|
|
// unknown_t=type
|
|
// type.unknown_t.typeclass=Integral
|
|
sdb_set(types_db, "unknown_t", "type", 0);
|
|
sdb_set(types_db, "type.unknown_t.typeclass", "Integral", 0);
|
|
// Now we read the bits value from "asm.bits=XX" in "/core/config"
|
|
int bits = sdb_num_get(config_db, "asm.bits", 0);
|
|
switch (bits) {
|
|
case 16:
|
|
// type.unknown_t=w
|
|
// type.unknown_t.size=16
|
|
sdb_set(types_db, "type.unknown_t", "w", 0);
|
|
sdb_set(types_db, "type.unknown_t.size", "16", 0);
|
|
break;
|
|
case 64:
|
|
// type.unknown_t=q
|
|
// type.unknown_t.size=64
|
|
sdb_set(types_db, "type.unknown_t", "q", 0);
|
|
sdb_set(types_db, "type.unknown_t.size", "64", 0);
|
|
break;
|
|
case 32:
|
|
default:
|
|
// type.unknown_t=d
|
|
// type.unknown_t.size=32
|
|
sdb_set(types_db, "type.unknown_t", "d", 0);
|
|
sdb_set(types_db, "type.unknown_t.size", "32", 0);
|
|
break;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// --
|
|
// Migration 5 -> 6
|
|
//
|
|
// Changes from 2c48a91d1332daede8d0640ce407c3abcf0abfb4
|
|
// Added serialization functionality for debug (only for breakpoints as of now)
|
|
// Used to save and load current RzDebug instance (only breakpoints) using serialization
|
|
// New namespaces: /core/debug, /core/debug/breakpoints
|
|
|
|
RZ_API bool rz_project_migrate_v5_v6(RzProject *prj, RzSerializeResultInfo *res) {
|
|
Sdb *core_db;
|
|
RZ_SERIALIZE_SUB(prj, core_db, res, "core", return false;);
|
|
Sdb *debug_db = sdb_ns(core_db, "debug", true);
|
|
sdb_ns(debug_db, "breakpoints", true);
|
|
|
|
return true;
|
|
}
|
|
|
|
// --
|
|
// Migration 6 -> 7
|
|
//
|
|
// Changes from 96a85e573e766e3870a01482f36c92df403cc4cd
|
|
// Removed esil pin feature. Namespace deleted: /core/analysis/pins
|
|
|
|
RZ_API bool rz_project_migrate_v6_v7(RzProject *prj, RzSerializeResultInfo *res) {
|
|
Sdb *core_db;
|
|
RZ_SERIALIZE_SUB(prj, core_db, res, "core", return false;);
|
|
Sdb *analysis_db;
|
|
RZ_SERIALIZE_SUB(core_db, analysis_db, res, "analysis", return false;);
|
|
sdb_ns_unset(analysis_db, "pins", NULL);
|
|
return true;
|
|
}
|
|
|
|
// --
|
|
// Migration 7 -> 8
|
|
//
|
|
// Changes from ea02b0d25f48bb17bc6578259485549cf5c74a20
|
|
// Removed zignature feature. Namespace deleted: /core/analysis/zigns
|
|
// Also removed configs zign.* options.
|
|
|
|
RZ_API bool rz_project_migrate_v7_v8(RzProject *prj, RzSerializeResultInfo *res) {
|
|
Sdb *core_db;
|
|
RZ_SERIALIZE_SUB(prj, core_db, res, "core", return false;);
|
|
Sdb *analysis_db;
|
|
RZ_SERIALIZE_SUB(core_db, analysis_db, res, "analysis", return false;);
|
|
sdb_ns_unset(analysis_db, "zigns", NULL);
|
|
Sdb *config_db;
|
|
RZ_SERIALIZE_SUB(core_db, config_db, res, "config", return false;);
|
|
sdb_unset(config_db, "zign.autoload", 0);
|
|
sdb_unset(config_db, "zign.diff.bthresh", 0);
|
|
sdb_unset(config_db, "zign.diff.gthresh", 0);
|
|
sdb_unset(config_db, "zign.match.bytes", 0);
|
|
sdb_unset(config_db, "zign.match.graph", 0);
|
|
sdb_unset(config_db, "zign.match.hash", 0);
|
|
sdb_unset(config_db, "zign.match.offset", 0);
|
|
sdb_unset(config_db, "zign.match.refs", 0);
|
|
sdb_unset(config_db, "zign.match.types", 0);
|
|
sdb_unset(config_db, "zign.maxsz", 0);
|
|
sdb_unset(config_db, "zign.mincc", 0);
|
|
sdb_unset(config_db, "zign.minsz", 0);
|
|
sdb_unset(config_db, "zign.prefix", 0);
|
|
sdb_unset(config_db, "zign.threshold", 0);
|
|
return true;
|
|
}
|
|
|
|
// --
|
|
// Migration 8 -> 9
|
|
//
|
|
// Changes from fbad0b4859802a62dcc96002c2710e696809a0c3
|
|
// Removed fingerprint from the serialized RzAnalysisFunction & RzAnalysisBlock
|
|
|
|
RZ_API bool rz_project_migrate_v8_v9(RzProject *prj, RzSerializeResultInfo *res) {
|
|
// there is nothing to be done since the deserializer will ignore the original serialized data
|
|
return true;
|
|
}
|
|
|
|
// --
|
|
// Migration 9 -> 10
|
|
//
|
|
// Changes from 13cd3942d12b61911e27ab82baef045adf57d77c
|
|
// Removed stackptr and parent_stackptr from the serialized RzAnalysisBlock
|
|
// Added sp_entry and sp_delta to serialized RzAnalysisBlock
|
|
|
|
RZ_API bool rz_project_migrate_v9_v10(RzProject *prj, RzSerializeResultInfo *res) {
|
|
// There is nothing to be done since the deserializer will ignore the original serialized data
|
|
// and missing sp_entry and sp_delta are valid for unknown values.
|
|
// The previous stackptr/parent_stackptr are too nonsensical to be converted to sp_entry/sp_delta
|
|
// unfortunately.
|
|
return true;
|
|
}
|
|
|
|
// --
|
|
// Migration 10 -> 11
|
|
//
|
|
// Changes from d9950f74792c1dfb565ac491cc7ef706b80e6044
|
|
// - Removed analysis.vars.stackname config var
|
|
// - In RzAnalysisVar JSON, "kind", "arg" and "delta" are removed. Instead, there is either a "stack"
|
|
// or a "reg" key, but never both.
|
|
// - {name:<str>, type:<str>, kind:"s|b|r", arg?:<bool>, delta?:<st64>, reg?:<str>, cmt?:<str>,...
|
|
// + {name:<str>, type:<str>, stack?:<st64>, reg?:<str>, cmt?:<str>,...
|
|
// - In RzAnalysisVar.accs JSON, "sp" value is now actually signed in json instead of being casted
|
|
// to and from its unsigned representation (pj_kn before, pj_kN now). The loader should be able
|
|
// to handle both just fine, but we still convert it so we do not have to make this assumption.
|
|
//
|
|
|
|
typedef struct {
|
|
Sdb *db_new;
|
|
RzSerializeResultInfo *res;
|
|
} V10V11FunctionsCtx;
|
|
|
|
bool v10_v11_migrate_variable(const RzJson *var, st64 maxstack, PJ *pj, RzSerializeResultInfo *res) {
|
|
if (var->type != RZ_JSON_OBJECT) {
|
|
goto invalid;
|
|
}
|
|
// read necessary info
|
|
const RzJson *kind = rz_json_get(var, "kind");
|
|
if (!kind || kind->type != RZ_JSON_STRING) {
|
|
goto invalid;
|
|
}
|
|
bool is_reg;
|
|
st64 stack_addr;
|
|
if (!strcmp(kind->str_value, "r")) {
|
|
is_reg = true;
|
|
// reg vars did not change, they just don't have the "delta" anymore because it was redundant.
|
|
} else if (!strcmp(kind->str_value, "b") || !strcmp(kind->str_value, "s")) {
|
|
is_reg = false;
|
|
const RzJson *delta = rz_json_get(var, "delta");
|
|
if (delta && delta->type != RZ_JSON_INTEGER) {
|
|
goto invalid;
|
|
}
|
|
// Despite variables being represented as sp/bp+offset, the delta value
|
|
// already matches our notion of stack addresses.
|
|
stack_addr = delta ? delta->num.s_value : 0;
|
|
} else {
|
|
goto invalid;
|
|
}
|
|
// write new json
|
|
pj_o(pj);
|
|
for (const RzJson *var_member = var->children.first; var_member; var_member = var_member->next) {
|
|
if (!strcmp(var_member->key, "arg") || !strcmp(var_member->key, "delta") || !strcmp(var_member->key, "kind")) {
|
|
// removed keys
|
|
continue;
|
|
}
|
|
// The accesses' "sp" key will be converted to sgned in the call below
|
|
// as a somewhat unexpected side effect. But this is exactly what we want.
|
|
rz_json_to_pj(var_member, pj, true);
|
|
}
|
|
if (!is_reg) {
|
|
pj_kN(pj, "stack", stack_addr);
|
|
}
|
|
pj_end(pj);
|
|
return true;
|
|
invalid:
|
|
RZ_SERIALIZE_ERR(res, "invalid json contents for variable");
|
|
return false;
|
|
}
|
|
|
|
bool v10_v11_functions_foreach_cb(void *user, const char *k, const char *v) {
|
|
V10V11FunctionsCtx *ctx = user;
|
|
char *json_str = strdup(v);
|
|
RzJson *j = rz_json_parse(json_str);
|
|
bool ret = false;
|
|
if (!j || j->type != RZ_JSON_OBJECT) {
|
|
RZ_SERIALIZE_ERR(ctx->res, "invalid json in function key %s", k);
|
|
goto end;
|
|
}
|
|
const RzJson *tmp = rz_json_get(j, "maxstack"); // maxstack is mandatory in v10
|
|
if (!tmp || tmp->type != RZ_JSON_INTEGER) {
|
|
RZ_SERIALIZE_ERR(ctx->res, "missing or invalid maxstack in function key %s", k);
|
|
goto end;
|
|
}
|
|
st64 maxstack = tmp->num.s_value;
|
|
PJ *pj = pj_new();
|
|
if (!pj) {
|
|
goto end;
|
|
}
|
|
pj_o(pj);
|
|
for (RzJson *func_member = j->children.first; func_member; func_member = func_member->next) {
|
|
if (!strcmp(func_member->key, "vars")) {
|
|
if (func_member->type != RZ_JSON_ARRAY) {
|
|
RZ_SERIALIZE_ERR(ctx->res, "invalid json contents for function -> vars");
|
|
pj_free(pj);
|
|
goto end;
|
|
}
|
|
pj_ka(pj, "vars");
|
|
for (RzJson *var = func_member->children.first; var; var = var->next) {
|
|
v10_v11_migrate_variable(var, maxstack, pj, ctx->res);
|
|
}
|
|
pj_end(pj);
|
|
continue;
|
|
}
|
|
rz_json_to_pj(func_member, pj, true);
|
|
}
|
|
pj_end(pj);
|
|
char *res = pj_drain(pj);
|
|
if (!res) {
|
|
goto end;
|
|
}
|
|
sdb_set_owned(ctx->db_new, k, res, 0);
|
|
ret = true;
|
|
end:
|
|
rz_json_free(j);
|
|
free(json_str);
|
|
return ret;
|
|
}
|
|
|
|
RZ_API bool rz_project_migrate_v10_v11(RzProject *prj, RzSerializeResultInfo *res) {
|
|
Sdb *core_db;
|
|
RZ_SERIALIZE_SUB(prj, core_db, res, "core", return false;);
|
|
Sdb *config_db;
|
|
RZ_SERIALIZE_SUB(core_db, config_db, res, "config", return false;);
|
|
sdb_unset(config_db, "analysis.vars.stackname", 0);
|
|
|
|
Sdb *analysis_db;
|
|
RZ_SERIALIZE_SUB(core_db, analysis_db, res, "analysis", return false;);
|
|
Sdb *functions_db_old;
|
|
RZ_SERIALIZE_SUB(analysis_db, functions_db_old, res, "functions", return false;);
|
|
functions_db_old->refs++;
|
|
sdb_ns_unset(analysis_db, "functions", NULL);
|
|
V10V11FunctionsCtx ctx = {
|
|
.db_new = sdb_ns(analysis_db, "functions", true),
|
|
.res = res
|
|
};
|
|
bool ret = sdb_foreach(functions_db_old, v10_v11_functions_foreach_cb, &ctx);
|
|
sdb_free(functions_db_old);
|
|
|
|
return ret;
|
|
}
|
|
|
|
// --
|
|
// Migration 11 -> 12
|
|
//
|
|
// Changes from 59f32b6db89c09c16fadbda6a098e326b73e03d8
|
|
// - Rename config var `asm.dwarf` to `asm.debuginfo`
|
|
// - Rename config var `asm.dwarf.abspath` to `asm.debuginfo.abspath`
|
|
// - Rename config var `asm.dwarf.file` to `asm.debuginfo.file`
|
|
// - Rename config var `asm.dwarf.lines` to `asm.debuginfo.lines`
|
|
//
|
|
|
|
static inline bool sdb_rename(Sdb *db, const char *old_key, const char *new_key) {
|
|
char *val = sdb_get(db, old_key, 0);
|
|
if (!val) {
|
|
return false;
|
|
}
|
|
sdb_unset(db, old_key, 0);
|
|
sdb_set_owned(db, new_key, val, 0);
|
|
return true;
|
|
}
|
|
|
|
RZ_API bool rz_project_migrate_v11_v12(RzProject *prj, RzSerializeResultInfo *res) {
|
|
Sdb *core_db;
|
|
RZ_SERIALIZE_SUB(prj, core_db, res, "core", return false;);
|
|
Sdb *config_db;
|
|
RZ_SERIALIZE_SUB(core_db, config_db, res, "config", return false;);
|
|
sdb_rename(config_db, "asm.dwarf.abspath", "asm.debuginfo.abspath");
|
|
sdb_rename(config_db, "asm.dwarf.file", "asm.debuginfo.file");
|
|
sdb_rename(config_db, "asm.dwarf.lines", "asm.debuginfo.lines");
|
|
sdb_rename(config_db, "asm.dwarf", "asm.debuginfo");
|
|
return true;
|
|
}
|
|
|
|
// --
|
|
// Migration 12 -> 13
|
|
//
|
|
// Changes from 366dfcfbf0ac2eb4c09c49b0a9e43117864750b7:
|
|
// Removed typelinks from "/core/analysis/typelinks"
|
|
// and converted into global variables in "/core/analysis/vars"
|
|
|
|
typedef struct {
|
|
RzList /*<char *>*/ *moved_keys; ///< deferred for deletion from the old sdb
|
|
Sdb *global_vars_db;
|
|
} V12V13TypesCtx;
|
|
|
|
bool v12_v13_types_foreach_cb(void *user, const char *k, const char *v) {
|
|
V12V13TypesCtx *ctx = user;
|
|
if (rz_str_startswith(k, "0x")) {
|
|
char name[32];
|
|
PJ *j = pj_new();
|
|
pj_o(j);
|
|
pj_ks(j, "name", rz_strf(name, "gvar_%s", k));
|
|
pj_ks(j, "type", v);
|
|
pj_ks(j, "addr", k);
|
|
// We don't have constraints for typelink here.
|
|
pj_end(j);
|
|
sdb_set(ctx->global_vars_db, k, pj_string(j), 0);
|
|
pj_free(j);
|
|
rz_list_push(ctx->moved_keys, strdup(k));
|
|
}
|
|
return true;
|
|
}
|
|
|
|
RZ_API bool rz_project_migrate_v12_v13(RzProject *prj, RzSerializeResultInfo *res) {
|
|
Sdb *core_db;
|
|
RZ_SERIALIZE_SUB(prj, core_db, res, "core", return false;);
|
|
Sdb *analysis_db;
|
|
RZ_SERIALIZE_SUB(core_db, analysis_db, res, "analysis", return false;);
|
|
sdb_ns(analysis_db, "vars", true);
|
|
V12V13TypesCtx ctx = {
|
|
.moved_keys = rz_list_newf(free),
|
|
.global_vars_db = sdb_ns(analysis_db, "vars", true)
|
|
};
|
|
|
|
if (!ctx.moved_keys || !ctx.global_vars_db) {
|
|
return false;
|
|
}
|
|
Sdb *typelinks_db = sdb_ns(analysis_db, "typelinks", true);
|
|
sdb_foreach(typelinks_db, v12_v13_types_foreach_cb, &ctx);
|
|
RzListIter *it;
|
|
char *s;
|
|
rz_list_foreach (ctx.moved_keys, it, s) {
|
|
sdb_unset(typelinks_db, s, 0);
|
|
}
|
|
rz_list_free(ctx.moved_keys);
|
|
return true;
|
|
}
|
|
|
|
// --
|
|
// Migration 13 -> 14
|
|
//
|
|
// Changes from 8e29b959b86a35bbbfed599989f077dba6e0ebd5:
|
|
// Removed {stack,reg} from "/core/analysis/functions/vars"
|
|
// and converted into storage object { ..., storage: { type: ... } }
|
|
|
|
bool v13_v14_foreach_cb(void *user, const char *k, const char *v) {
|
|
static const char *types[] = { "stack", "reg" };
|
|
Sdb *fn_db = user;
|
|
if (rz_str_startswith(k, "0x")) {
|
|
RzJson *fn_j = rz_json_parse((char *)v);
|
|
assert(fn_j->type == RZ_JSON_OBJECT);
|
|
|
|
PJ *j = pj_new();
|
|
pj_o(j);
|
|
|
|
for (RzJson *body = fn_j->children.first; body; body = body->next) {
|
|
bool filtered = false;
|
|
for (int i = 0; i < RZ_ARRAY_SIZE(types); ++i) {
|
|
const char *type = types[i];
|
|
if (rz_str_cmp(body->key, type, -1) != 0) {
|
|
continue;
|
|
}
|
|
filtered = true;
|
|
pj_ko(j, "storage");
|
|
pj_ks(j, "type", types[i]);
|
|
switch (body->type) {
|
|
case RZ_JSON_INTEGER:
|
|
pj_kn(j, types[i], body->num.s_value);
|
|
break;
|
|
case RZ_JSON_STRING:
|
|
pj_ks(j, types[i], body->str_value);
|
|
break;
|
|
default: rz_warn_if_reached();
|
|
}
|
|
pj_end(j);
|
|
}
|
|
|
|
if (!filtered) {
|
|
rz_json_to_pj(body, j, true);
|
|
}
|
|
}
|
|
|
|
pj_end(j);
|
|
sdb_set(fn_db, k, pj_string(j), 0);
|
|
pj_free(j);
|
|
rz_json_free(fn_j);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
RZ_API bool rz_project_migrate_v13_v14(RzProject *prj, RzSerializeResultInfo *res) {
|
|
Sdb *core_db;
|
|
RZ_SERIALIZE_SUB(prj, core_db, res, "core", return false;);
|
|
Sdb *analysis_db;
|
|
RZ_SERIALIZE_SUB(core_db, analysis_db, res, "analysis", return false;);
|
|
Sdb *fn_db = sdb_ns(analysis_db, "functions", true);
|
|
sdb_foreach(fn_db, v13_v14_foreach_cb, fn_db);
|
|
return true;
|
|
}
|
|
|
|
// --
|
|
// Migration 14 -> 15
|
|
//
|
|
// Changes from <commit hash not yet known>:
|
|
// Added serialization functionality for seek history
|
|
// New namespace: /core/seek
|
|
|
|
RZ_API bool rz_project_migrate_v14_v15(RzProject *prj, RzSerializeResultInfo *res) {
|
|
Sdb *core_db;
|
|
RZ_SERIALIZE_SUB(prj, core_db, res, "core", return false;);
|
|
sdb_ns(core_db, "seek", true);
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool (*const migrations[])(RzProject *prj, RzSerializeResultInfo *res) = {
|
|
rz_project_migrate_v1_v2,
|
|
rz_project_migrate_v2_v3,
|
|
rz_project_migrate_v3_v4,
|
|
rz_project_migrate_v4_v5,
|
|
rz_project_migrate_v5_v6,
|
|
rz_project_migrate_v6_v7,
|
|
rz_project_migrate_v7_v8,
|
|
rz_project_migrate_v8_v9,
|
|
rz_project_migrate_v9_v10,
|
|
rz_project_migrate_v10_v11,
|
|
rz_project_migrate_v11_v12,
|
|
rz_project_migrate_v12_v13,
|
|
rz_project_migrate_v13_v14,
|
|
rz_project_migrate_v14_v15,
|
|
};
|
|
|
|
/// Migrate the given project to the current version in-place
|
|
RZ_API bool rz_project_migrate(RzProject *prj, unsigned long version, RzSerializeResultInfo *res) {
|
|
RZ_STATIC_ASSERT(RZ_ARRAY_SIZE(migrations) + 1 == RZ_PROJECT_VERSION);
|
|
while (version < RZ_PROJECT_VERSION) {
|
|
bool succ = migrations[version - 1](prj, res);
|
|
if (!succ) {
|
|
rz_list_push(res, rz_str_newf("project migration from version %lu to %lu failed.", version, version + 1));
|
|
return false;
|
|
}
|
|
rz_list_push(res, rz_str_newf("project migrated from version %lu to %lu.", version, version + 1));
|
|
version++;
|
|
}
|
|
return true;
|
|
}
|