Adds /*<type>*/ comments and a linter check from rz-bindgen to enforce their existence and consistency Also includes the following fixes made when adding the annotations: * removed unused intern_table arguments in pyc_dis.c, pyc_dis.h, asm_pyc.c * removed unused classes argument from place_nodes in agraph.c * removed unused recurse and recurse_bb functions in canalysis.c * removed unused vars field from RzPrint struct * removed unused RzAnalysisType* structs from rz_analysis.h * removed unused list field from RzEgg struct * fixed bug in bp_plugin.c where duplication-checking logic iterates over the wrong list * removed unused q_regs field from RzDebug struct * removed unused backtrace field from RzDebugPlugin struct * removed unused classes_list field from RzBinNXOObj struct * removed unused methods_list and classes_list fields from RzBinZimgObj struct
375 lines
11 KiB
C
375 lines
11 KiB
C
// SPDX-FileCopyrightText: 2021 Basstorm <basstorm@nyist.edu.cn>
|
|
// SPDX-License-Identifier: LGPL-3.0-only
|
|
|
|
#include <rz_analysis.h>
|
|
#include <rz_list.h>
|
|
|
|
/**
|
|
* \brief Create a new instance of global variable
|
|
*
|
|
* \param name variable name
|
|
* \param addr variable address
|
|
* \param comment variable comment
|
|
* \return RzAnalysisVarGlobal *
|
|
*/
|
|
RZ_API RZ_OWN RzAnalysisVarGlobal *rz_analysis_var_global_new(RZ_NONNULL const char *name, ut64 addr) {
|
|
rz_return_val_if_fail(name, NULL);
|
|
RzAnalysisVarGlobal *glob = RZ_NEW0(RzAnalysisVarGlobal);
|
|
if (!glob) {
|
|
return NULL;
|
|
}
|
|
glob->name = strdup(name);
|
|
glob->addr = addr;
|
|
glob->analysis = NULL;
|
|
|
|
return glob;
|
|
}
|
|
|
|
int global_var_node_cmp(const void *incoming, const RBNode *in_tree, void *user) {
|
|
ut64 ia = *(ut64 *)incoming;
|
|
ut64 ta = container_of(in_tree, const RzAnalysisVarGlobal, rb)->addr;
|
|
if (ia < ta) {
|
|
return -1;
|
|
} else if (ia > ta) {
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* \brief Add the global variable into hashtable
|
|
*
|
|
* \param analysis RzAnalysis
|
|
* \param global_var Global variable instance
|
|
* \return true if succeed
|
|
*/
|
|
RZ_API RZ_OWN bool rz_analysis_var_global_add(RzAnalysis *analysis, RZ_NONNULL RzAnalysisVarGlobal *global_var) {
|
|
rz_return_val_if_fail(analysis && global_var, false);
|
|
|
|
RzAnalysisVarGlobal *existing_glob = NULL;
|
|
if ((existing_glob = rz_analysis_var_global_get_byaddr_in(analysis, global_var->addr))) {
|
|
RZ_LOG_ERROR("Global variable %s at 0x%" PFMT64x " already exists!\n", existing_glob->name, existing_glob->addr);
|
|
return false;
|
|
}
|
|
if ((existing_glob = rz_analysis_var_global_get_byname(analysis, global_var->name))) {
|
|
RZ_LOG_ERROR("Global variable %s at 0x%" PFMT64x " already exists!\n", existing_glob->name, existing_glob->addr);
|
|
return false;
|
|
}
|
|
if (!ht_pp_insert(analysis->ht_global_var, global_var->name, global_var)) {
|
|
return false;
|
|
}
|
|
if (!rz_rbtree_aug_insert(&analysis->global_var_tree, &global_var->addr, &global_var->rb, global_var_node_cmp, NULL, NULL)) {
|
|
return false;
|
|
}
|
|
|
|
global_var->analysis = analysis;
|
|
rz_flag_space_push(global_var->analysis->flb.f, "globals");
|
|
rz_flag_set(global_var->analysis->flb.f, global_var->name, global_var->addr, rz_type_db_get_bitsize(global_var->analysis->typedb, global_var->type) / 8);
|
|
rz_flag_space_pop(global_var->analysis->flb.f);
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* \brief Free the global variable instance
|
|
*
|
|
* \param glob Global variable instance
|
|
* \return void
|
|
*/
|
|
RZ_API void rz_analysis_var_global_free(RzAnalysisVarGlobal *glob) {
|
|
if (!glob) {
|
|
return;
|
|
}
|
|
RzFlagItem *flag = rz_analysis_var_global_get_flag_item(glob);
|
|
if (flag) {
|
|
rz_flag_unset(glob->analysis->flb.f, flag);
|
|
}
|
|
RZ_FREE(glob->name);
|
|
rz_type_free(glob->type);
|
|
rz_vector_fini(&glob->constraints);
|
|
RZ_FREE(glob);
|
|
}
|
|
|
|
/**
|
|
* \brief Get the flag item corresponding to the given variable
|
|
*
|
|
* This will search for the matching flag that has been created along with the global variable.
|
|
* It can happen that the flag has manually been deleted, in which case this returns NULL.
|
|
*
|
|
* \return a flag item or NULL
|
|
*/
|
|
RZ_API RZ_NULLABLE RzFlagItem *rz_analysis_var_global_get_flag_item(RzAnalysisVarGlobal *glob) {
|
|
rz_return_val_if_fail(glob, NULL);
|
|
RzAnalysis *a = glob->analysis;
|
|
if (!a) {
|
|
return NULL;
|
|
}
|
|
RzFlagItem *r = rz_flag_get(a->flb.f, glob->name);
|
|
if (r && r->offset != glob->addr) {
|
|
return NULL;
|
|
}
|
|
return r;
|
|
}
|
|
|
|
/**
|
|
* \brief Delete and free the global variable
|
|
*
|
|
* \param analysis RzAnalysis
|
|
* \param glob global variable to be deleted
|
|
* \return true if succeed
|
|
*/
|
|
RZ_API bool rz_analysis_var_global_delete(RZ_NONNULL RzAnalysis *analysis, RZ_NONNULL RzAnalysisVarGlobal *glob) {
|
|
rz_return_val_if_fail(analysis && glob, false);
|
|
|
|
// We need to delete RBTree first because ht_pp_delete will free its member
|
|
bool deleted = rz_rbtree_delete(&analysis->global_var_tree, &glob->addr, global_var_node_cmp, NULL, NULL, NULL);
|
|
return deleted ? ht_pp_delete(analysis->ht_global_var, glob->name) : deleted;
|
|
}
|
|
|
|
/**
|
|
* \brief Delete and free the global variable by its name
|
|
*
|
|
* \param analysis RzAnalysis
|
|
* \param name Global Variable name
|
|
* \return true if succeed
|
|
*/
|
|
RZ_API bool rz_analysis_var_global_delete_byname(RzAnalysis *analysis, RZ_NONNULL const char *name) {
|
|
rz_return_val_if_fail(analysis && name, false);
|
|
RzAnalysisVarGlobal *glob = rz_analysis_var_global_get_byname(analysis, name);
|
|
if (!glob) {
|
|
RZ_LOG_ERROR("No global variable found having name %s\n", name);
|
|
return false;
|
|
}
|
|
|
|
return rz_analysis_var_global_delete(analysis, glob);
|
|
}
|
|
|
|
/**
|
|
* \brief Same as rz_analysis_var_global_delete_byname at the address
|
|
*
|
|
* \param analysis RzAnalysis
|
|
* \param addr Global Variable address
|
|
* \return true if succeed
|
|
*/
|
|
RZ_API bool rz_analysis_var_global_delete_byaddr_at(RzAnalysis *analysis, ut64 addr) {
|
|
rz_return_val_if_fail(analysis, false);
|
|
RzAnalysisVarGlobal *glob = rz_analysis_var_global_get_byaddr_at(analysis, addr);
|
|
if (!glob) {
|
|
RZ_LOG_ERROR("No global variable found at 0x%" PFMT64x "\n", addr);
|
|
return false;
|
|
}
|
|
|
|
return rz_analysis_var_global_delete(analysis, glob);
|
|
}
|
|
|
|
/**
|
|
* \brief Same as rz_analysis_var_global_delete_byname in the address
|
|
*
|
|
* \param analysis RzAnalysis
|
|
* \param addr Global Variable address
|
|
* \return true if succeed
|
|
*/
|
|
RZ_API bool rz_analysis_var_global_delete_byaddr_in(RzAnalysis *analysis, ut64 addr) {
|
|
rz_return_val_if_fail(analysis, false);
|
|
RzAnalysisVarGlobal *glob = rz_analysis_var_global_get_byaddr_in(analysis, addr);
|
|
if (!glob) {
|
|
RZ_LOG_ERROR("No global variable found in 0x%" PFMT64x "\n", addr);
|
|
return false;
|
|
}
|
|
|
|
return rz_analysis_var_global_delete(analysis, glob);
|
|
}
|
|
|
|
/**
|
|
* \brief Get the instance of global variable by its name
|
|
*
|
|
* \param analysis RzAnalysis
|
|
* \param name Global variable name
|
|
* \return RzAnalysisVarGlobal *
|
|
*/
|
|
RZ_API RZ_BORROW RzAnalysisVarGlobal *rz_analysis_var_global_get_byname(RzAnalysis *analysis, RZ_NONNULL const char *name) {
|
|
rz_return_val_if_fail(analysis && name, NULL);
|
|
return (RzAnalysisVarGlobal *)ht_pp_find(analysis->ht_global_var, name, NULL);
|
|
}
|
|
|
|
struct list_addr {
|
|
RzList /*<RzAnalysisVarGlobal *>*/ *list;
|
|
ut64 addr;
|
|
};
|
|
|
|
/**
|
|
* \brief Get the instance of global variable at the address
|
|
*
|
|
* \param analysis RzAnalysis
|
|
* \param addr Global variable address
|
|
* \return RzAnalysisVarGlobal *
|
|
*/
|
|
RZ_API RZ_BORROW RzAnalysisVarGlobal *rz_analysis_var_global_get_byaddr_at(RzAnalysis *analysis, ut64 addr) {
|
|
rz_return_val_if_fail(analysis, NULL);
|
|
|
|
RBNode *node = rz_rbtree_find(analysis->global_var_tree, &addr, global_var_node_cmp, NULL);
|
|
if (!node) {
|
|
return NULL;
|
|
}
|
|
RzAnalysisVarGlobal *var = (RzAnalysisVarGlobal *)container_of(node, RzAnalysisVarGlobal, rb);
|
|
if (!var) {
|
|
return NULL;
|
|
}
|
|
return var;
|
|
}
|
|
|
|
/**
|
|
* \brief Get the instance of global variable contains the address
|
|
*
|
|
* \param analysis RzAnalysis
|
|
* \param addr Global variable address
|
|
* \return RzAnalysisVarGlobal *
|
|
*/
|
|
RZ_API RZ_BORROW RzAnalysisVarGlobal *rz_analysis_var_global_get_byaddr_in(RzAnalysis *analysis, ut64 addr) {
|
|
rz_return_val_if_fail(analysis, NULL);
|
|
|
|
RBNode *node = rz_rbtree_upper_bound(analysis->global_var_tree, &addr, global_var_node_cmp, NULL);
|
|
if (!node) {
|
|
return NULL;
|
|
}
|
|
RzAnalysisVarGlobal *var = (RzAnalysisVarGlobal *)container_of(node, RzAnalysisVarGlobal, rb);
|
|
if (!var) {
|
|
return NULL;
|
|
}
|
|
ut64 size = rz_type_db_get_bitsize(analysis->typedb, var->type) / 8;
|
|
if (addr >= var->addr + size) {
|
|
return NULL;
|
|
}
|
|
return var;
|
|
}
|
|
|
|
static bool global_var_collect_cb(void *user, const void *k, const void *v) {
|
|
RzList *l = user;
|
|
RzAnalysisVarGlobal *glob = (RzAnalysisVarGlobal *)v;
|
|
rz_list_append(l, glob);
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* \brief Get all of the added global variables
|
|
*
|
|
* \param analysis RzAnalysis
|
|
* \return RzList *
|
|
*/
|
|
RZ_API RZ_OWN RzList /*<RzAnalysisVarGlobal *>*/ *rz_analysis_var_global_get_all(RzAnalysis *analysis) {
|
|
rz_return_val_if_fail(analysis, NULL);
|
|
RzList *globals = rz_list_new();
|
|
if (!globals) {
|
|
return NULL;
|
|
}
|
|
ht_pp_foreach(analysis->ht_global_var, global_var_collect_cb, globals);
|
|
return globals;
|
|
}
|
|
|
|
/**
|
|
* \brief Rename the global variable
|
|
*
|
|
* \param analysis RzAnalysis
|
|
* \param old_name The old name of the global variable
|
|
* \param newname The new name of the global variable
|
|
* \return true if succeed
|
|
*/
|
|
RZ_API bool rz_analysis_var_global_rename(RzAnalysis *analysis, RZ_NONNULL const char *old_name, RZ_NONNULL const char *newname) {
|
|
rz_return_val_if_fail(analysis && old_name && newname, false);
|
|
RzAnalysisVarGlobal *glob = rz_analysis_var_global_get_byname(analysis, old_name);
|
|
if (!glob) {
|
|
RZ_LOG_ERROR("Global variable '%s' does not exist!\n", old_name);
|
|
return false;
|
|
}
|
|
|
|
RzFlagItem *flag = rz_analysis_var_global_get_flag_item(glob);
|
|
if (flag) {
|
|
rz_flag_rename(analysis->flb.f, flag, newname);
|
|
}
|
|
|
|
RZ_FREE(glob->name);
|
|
glob->name = strdup(newname);
|
|
return ht_pp_update_key(analysis->ht_global_var, old_name, newname);
|
|
}
|
|
|
|
/**
|
|
* \brief Set the type of the global variable
|
|
*
|
|
* \param glob Global variable instance
|
|
* \param type The type to set. RzType*
|
|
* \return void
|
|
*/
|
|
RZ_API void rz_analysis_var_global_set_type(RzAnalysisVarGlobal *glob, RZ_NONNULL RZ_BORROW RzType *type) {
|
|
rz_return_if_fail(glob && type);
|
|
rz_type_free(glob->type);
|
|
glob->type = type;
|
|
|
|
RzFlagItem *flag = rz_analysis_var_global_get_flag_item(glob);
|
|
if (flag) {
|
|
flag->size = rz_type_db_get_bitsize(glob->analysis->typedb, glob->type) / 8;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* \brief Add a constaint to global variable
|
|
*
|
|
* \param glob Global variable instance
|
|
* \param constraint RzTypeConstraint
|
|
* \return void
|
|
*/
|
|
RZ_API void rz_analysis_var_global_add_constraint(RzAnalysisVarGlobal *glob, RzTypeConstraint *constraint) {
|
|
rz_return_if_fail(glob && constraint);
|
|
rz_vector_push(&glob->constraints, constraint);
|
|
}
|
|
|
|
/**
|
|
* \brief Get the pritable string of global variable constraints
|
|
*
|
|
* \param glob Global variable instance
|
|
* \return char *
|
|
*/
|
|
RZ_API RZ_OWN char *rz_analysis_var_global_get_constraints_readable(RzAnalysisVarGlobal *glob) {
|
|
size_t n = glob->constraints.len;
|
|
if (!n) {
|
|
return NULL;
|
|
}
|
|
bool low = false, high = false;
|
|
RzStrBuf sb;
|
|
rz_strbuf_init(&sb);
|
|
size_t i;
|
|
for (i = 0; i < n; i += 1) {
|
|
RzTypeConstraint *constr = rz_vector_index_ptr(&glob->constraints, i);
|
|
switch (constr->cond) {
|
|
case RZ_TYPE_COND_LE:
|
|
if (high) {
|
|
rz_strbuf_append(&sb, " && ");
|
|
}
|
|
rz_strbuf_appendf(&sb, "<= 0x%" PFMT64x "", constr->val);
|
|
low = true;
|
|
break;
|
|
case RZ_TYPE_COND_LT:
|
|
if (high) {
|
|
rz_strbuf_append(&sb, " && ");
|
|
}
|
|
rz_strbuf_appendf(&sb, "< 0x%" PFMT64x "", constr->val);
|
|
low = true;
|
|
break;
|
|
case RZ_TYPE_COND_GE:
|
|
rz_strbuf_appendf(&sb, ">= 0x%" PFMT64x "", constr->val);
|
|
high = true;
|
|
break;
|
|
case RZ_TYPE_COND_GT:
|
|
rz_strbuf_appendf(&sb, "> 0x%" PFMT64x "", constr->val);
|
|
high = true;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
if (low && high && i != n - 1) {
|
|
rz_strbuf_append(&sb, " || ");
|
|
low = false;
|
|
high = false;
|
|
}
|
|
}
|
|
return rz_strbuf_drain_nofree(&sb);
|
|
}
|