416 lines
12 KiB
C
416 lines
12 KiB
C
// SPDX-FileCopyrightText: 2021 Basstorm <basstorm@nyist.edu.cn>
|
|
// SPDX-License-Identifier: LGPL-3.0-only
|
|
|
|
#include "analysis_private.h"
|
|
|
|
/**
|
|
* \brief Create a new instance of global variable
|
|
*
|
|
* \param name variable name
|
|
* \param addr variable address
|
|
* \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 = rz_str_dup(name);
|
|
glob->addr = addr;
|
|
glob->analysis = NULL;
|
|
glob->coord.decl_file = NULL;
|
|
glob->coord.decl_line = UT32_MAX;
|
|
glob->coord.decl_col = UT32_MAX;
|
|
rz_vector_init(&glob->constraints, sizeof(RzTypeConstraint), NULL, NULL);
|
|
|
|
return glob;
|
|
}
|
|
|
|
static 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;
|
|
}
|
|
|
|
static void global_var_set_type(RzAnalysisVarGlobal *glob, RzType *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 the global variable into hashtable
|
|
*
|
|
* \param analysis RzAnalysis
|
|
* \param global_var Global variable instance
|
|
* \return true if succeed
|
|
*/
|
|
RZ_API 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_sp_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,
|
|
global_var->type ? rz_type_db_get_bitsize(global_var->analysis->typedb, global_var->type) / 8 : 0);
|
|
rz_flag_space_pop(global_var->analysis->flb.f);
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* \brief Create the global variable and add into hashtable
|
|
*
|
|
* \param analysis RzAnalysis
|
|
* \param name Global variable name
|
|
* \param type Global variable type
|
|
* \param addr Global variable address
|
|
* \param file File containing source declaration
|
|
* \param line Line number of source declaration
|
|
* \param colum Column position of source declaration
|
|
* \return true if succeed
|
|
*/
|
|
RZ_API bool rz_analysis_var_global_create_with_sourceline(RzAnalysis *analysis,
|
|
RZ_NONNULL const char *name, RZ_NONNULL RZ_BORROW RzType *type, ut64 addr,
|
|
RZ_NULLABLE const char *file, ut32 line, ut32 colum) {
|
|
rz_return_val_if_fail(analysis && name && type, false);
|
|
|
|
RzAnalysisVarGlobal *glob = rz_analysis_var_global_new(name, addr);
|
|
if (!glob) {
|
|
return false;
|
|
}
|
|
|
|
global_var_set_type(glob, type);
|
|
|
|
if (!rz_analysis_var_global_add(analysis, glob)) {
|
|
rz_analysis_var_global_free(glob);
|
|
return false;
|
|
}
|
|
|
|
glob->coord.decl_file = rz_str_constpool_get(&analysis->constpool, file);
|
|
glob->coord.decl_line = line;
|
|
glob->coord.decl_col = colum;
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* \brief Create the global variable and add into hashtable
|
|
*
|
|
* \param analysis RzAnalysis
|
|
* \param name Global variable name
|
|
* \param type Global variable type
|
|
* \param addr Global variable address
|
|
* \return true if succeed
|
|
*/
|
|
RZ_API bool rz_analysis_var_global_create(RzAnalysis *analysis,
|
|
RZ_NONNULL const char *name, RZ_NONNULL RZ_BORROW RzType *type, ut64 addr) {
|
|
rz_return_val_if_fail(analysis && name && type, false);
|
|
return rz_analysis_var_global_create_with_sourceline(
|
|
analysis, name, type, addr, NULL, UT32_MAX, UT32_MAX);
|
|
}
|
|
|
|
/**
|
|
* \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_sp_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_sp_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 = var->type ? rz_type_db_get_bitsize(analysis->typedb, var->type) / 8 : 0;
|
|
if (addr >= var->addr + size) {
|
|
return NULL;
|
|
}
|
|
return var;
|
|
}
|
|
|
|
static bool global_var_collect_cb(void *user, RZ_UNUSED const char *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_sp_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 = rz_str_dup(newname);
|
|
return ht_sp_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);
|
|
|
|
global_var_set_type(glob, type);
|
|
}
|
|
|
|
/**
|
|
* \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(RZ_NONNULL RzAnalysisVarGlobal *glob, RZ_NONNULL RzTypeConstraint *constraint) {
|
|
rz_return_if_fail(glob && constraint);
|
|
rz_vector_push(&glob->constraints, constraint);
|
|
}
|
|
|
|
/**
|
|
* \brief Removes all type constraints from a \p glob global variable
|
|
*
|
|
* \param glob Global variable instance
|
|
*/
|
|
RZ_API void rz_analysis_var_global_clear_constraints(RZ_NONNULL RzAnalysisVarGlobal *glob) {
|
|
rz_return_if_fail(glob);
|
|
rz_vector_clear(&glob->constraints);
|
|
}
|
|
|
|
/**
|
|
* \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(RZ_NONNULL RzAnalysisVarGlobal *glob) {
|
|
rz_return_val_if_fail(glob, NULL);
|
|
return rz_type_interval_constraints_as_string(&glob->constraints);
|
|
}
|
|
|
|
/**
|
|
* \brief Get the list of x-references to the global variable
|
|
*
|
|
* \param analysis RzAnalysis
|
|
* \param glob Global variable
|
|
* \return RzList *
|
|
*/
|
|
RZ_API RZ_OWN RzList /*<RzAnalysisXRef *>*/ *rz_analysis_var_global_xrefs(RzAnalysis *analysis, RZ_NONNULL const RzAnalysisVarGlobal *glob) {
|
|
rz_return_val_if_fail(analysis && glob, NULL);
|
|
return rz_analysis_xrefs_get_to(analysis, glob->addr);
|
|
}
|