Skip to content

Commit

Permalink
Add API to request function variables without arguments (#3597)
Browse files Browse the repository at this point in the history
* Add Doxygen to variables API

* Add Doxygen to function API

* Improve arguments and variables API
  • Loading branch information
XVilka committed Jun 21, 2023
1 parent c9ef508 commit 33c056e
Show file tree
Hide file tree
Showing 3 changed files with 125 additions and 7 deletions.
97 changes: 90 additions & 7 deletions librz/analysis/fcn.c
Original file line number Diff line number Diff line change
Expand Up @@ -1718,6 +1718,9 @@ RZ_DEPRECATE RZ_API RzAnalysisFunction *rz_analysis_get_fcn_in_bounds(RzAnalysis
return ret;
}

/**
* \brief Returns function if exists given the \p name
*/
RZ_API RzAnalysisFunction *rz_analysis_get_function_byname(RzAnalysis *a, const char *name) {
bool found = false;
RzAnalysisFunction *f = ht_pp_find(a->ht_name_fun, name, &found);
Expand Down Expand Up @@ -1760,6 +1763,9 @@ RZ_API bool rz_analysis_fcn_add_bb(RzAnalysis *a, RzAnalysisFunction *fcn, ut64
return true;
}

/**
* \brief Returns the amount of loops located in the \p fcn function
*/
RZ_API int rz_analysis_function_loops(RzAnalysisFunction *fcn) {
RzListIter *iter;
RzAnalysisBlock *bb;
Expand All @@ -1775,13 +1781,19 @@ RZ_API int rz_analysis_function_loops(RzAnalysisFunction *fcn) {
return loops;
}

RZ_API int rz_analysis_function_complexity(RzAnalysisFunction *fcn) {
/*
CC = E - N + 2P
E = the number of edges of the graph.
N = the number of nodes of the graph.
P = the number of connected components (exit nodes).
/**
* \brief Returns cyclomatic complexity of the function
*
* It calculated using this formula:
*
* CC = E - N + 2P
* where
* E is the number of edges of the graph.
* N is the number of nodes of the graph.
* P is the number of connected components (exit nodes).
*
*/
RZ_API int rz_analysis_function_complexity(RzAnalysisFunction *fcn) {
RzAnalysis *analysis = fcn->analysis;
int E = 0, N = 0, P = 0;
RzListIter *iter;
Expand Down Expand Up @@ -1874,6 +1886,12 @@ RZ_API char *rz_analysis_function_get_json(RzAnalysisFunction *function) {
return pj_drain(pj);
}

/**
* \brief Returns type signature (prototype) of the function
*
* If the type is presented in the type database it uses it,
* otherwise it tries to derive the type from the analysis data
*/
RZ_API RZ_OWN char *rz_analysis_function_get_signature(RZ_NONNULL RzAnalysisFunction *function) {
rz_return_val_if_fail(function, NULL);
RzAnalysis *a = function->analysis;
Expand Down Expand Up @@ -2099,6 +2117,9 @@ RZ_API int rz_analysis_function_count_edges(const RzAnalysisFunction *fcn, RZ_NU
return edges;
}

/**
* \brief Returns if the function pure - accesses any external resources or not
*/
RZ_API bool rz_analysis_function_purity(RzAnalysisFunction *fcn) {
if (fcn->has_changed) {
HtUP *ht = ht_up_new(NULL, NULL, NULL);
Expand Down Expand Up @@ -2190,6 +2211,11 @@ static void __analysis_fcn_check_bp_use(RzAnalysis *analysis, RzAnalysisFunction
}
}

/**
* \brief This function checks whether any operation in a given function may change BP
*
* Excludes pattern like "mov bp, sp" and "pop sp, bp" for saving stack pointer value
*/
RZ_API void rz_analysis_function_check_bp_use(RzAnalysisFunction *fcn) {
rz_return_if_fail(fcn);
__analysis_fcn_check_bp_use(fcn->analysis, fcn);
Expand Down Expand Up @@ -2407,7 +2433,7 @@ RZ_API void rz_analysis_function_update_analysis(RzAnalysisFunction *fcn) {
* \brief Returns vector of all function arguments
*
* \param a RzAnalysis instance
* \param f Function
* \param fcn Function
*/
RZ_API RZ_OWN RzPVector /*<RzAnalysisVar *>*/ *rz_analysis_function_args(RzAnalysis *a, RzAnalysisFunction *fcn) {
if (!a || !fcn) {
Expand Down Expand Up @@ -2467,12 +2493,69 @@ RZ_API RZ_OWN RzPVector /*<RzAnalysisVar *>*/ *rz_analysis_function_args(RzAnaly
return args;
}

/**
* \brief Returns vector of all function variables without arguments
*
* \param a RzAnalysis instance
* \param fcn Function
*/
RZ_API RZ_OWN RzPVector /*<RzAnalysisVar *>*/ *rz_analysis_function_vars(RZ_NONNULL RzAnalysis *a, RZ_NONNULL RzAnalysisFunction *fcn) {
rz_return_val_if_fail(a && fcn, NULL);
RzAnalysisVar *var;
void **it;
RzPVector *vars = rz_pvector_new(NULL);
if (!vars) {
return NULL;
}
rz_pvector_foreach (&fcn->vars, it) {
var = *it;
if (!rz_analysis_var_is_arg(var)) {
rz_pvector_push(vars, var);
}
}
return vars;
}

/**
* \brief Gets the argument given its index
*
* \param analysis RzAnalysis instance
* \param f Function to update
*/
RZ_API RZ_BORROW RzAnalysisVar *rz_analysis_function_get_arg_idx(RZ_NONNULL RzAnalysis *analysis, RZ_NONNULL RzAnalysisFunction *f, size_t index) {
rz_return_val_if_fail(analysis && f, NULL);
int argnum = rz_analysis_function_get_arg_count(analysis, f);
if (argnum < 1) {
return NULL;
}
if (index >= argnum) {
RZ_LOG_VERBOSE("Function %s has less arguments (%d) than requested (%zu)\n",
f->name, argnum, index);
}
RzPVector *args = rz_analysis_function_args(analysis, f);
if (!args) {
RZ_LOG_VERBOSE("Function %s has no arguments\n", f->name);
return NULL;
}
if (rz_pvector_len(args) < index) {
RZ_LOG_VERBOSE("Function %s has less arguments (%zu) than requested (%zu)\n",
f->name, rz_pvector_len(args), index);
return NULL;
}
return rz_pvector_at(args, index);
}

static int typecmp(const void *a, const void *b) {
const RzType *t1 = a;
const RzType *t2 = b;
return !rz_types_equal(t1, t2);
}

/**
* \brief Returns vector of all unique types used in a function
*
* Accounts for all types used in both arguments and variables, excluding return value type
*/
RZ_API RZ_OWN RzList /*<RzType *>*/ *rz_analysis_types_from_fcn(RzAnalysis *analysis, RzAnalysisFunction *fcn) {
RzList *type_used = rz_list_new();
void **it;
Expand Down
33 changes: 33 additions & 0 deletions librz/analysis/var.c
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,17 @@ RZ_IPI RZ_BORROW RzAnalysisVar *rz_analysis_function_add_var_dwarf(RzAnalysisFun
return out;
}

/**
* \brief Updates the type of the variable
*
* Frees the old type of the variable and sets a new one.
* After that it checks if the size of the variable has changed
* and tries to resolve overlaps if \p resolve_overlaps is set
*
* \param var variable to check
* \param type new type of the variable
* \param resolve_overlaps resolves overlaps if set
*/
RZ_API void rz_analysis_var_set_type(RzAnalysisVar *var, RZ_OWN RzType *type, bool resolve_overlaps) {
rz_return_if_fail(var && type);
rz_type_free(var->type);
Expand Down Expand Up @@ -283,11 +294,17 @@ RZ_API void rz_analysis_function_delete_arg_vars(RzAnalysisFunction *fcn) {
}
}

/**
* Delete all variables from \p fcn function - both arguments and local variables
*/
RZ_API void rz_analysis_function_delete_all_vars(RzAnalysisFunction *fcn) {
rz_pvector_fini(&fcn->vars);
fcn->argnum = 0;
}

/**
* Delete all unused (without any read or write access) variables from \p fcn function
*/
RZ_API void rz_analysis_function_delete_unused_vars(RzAnalysisFunction *fcn) {
void **v;
RzPVector *vars_clone = rz_pvector_clone(&fcn->vars);
Expand All @@ -300,12 +317,18 @@ RZ_API void rz_analysis_function_delete_unused_vars(RzAnalysisFunction *fcn) {
rz_pvector_free(vars_clone);
}

/**
* Delete variable \p var from \p fcn
*/
RZ_API void rz_analysis_function_delete_var(RzAnalysisFunction *fcn, RzAnalysisVar *var) {
rz_return_if_fail(fcn && var);
rz_pvector_remove_data(&fcn->vars, var);
rz_analysis_var_free(var);
}

/**
* Search variable by \p name in \p fcn, return first match
*/
RZ_API RZ_BORROW RzAnalysisVar *rz_analysis_function_get_var_byname(RzAnalysisFunction *fcn, const char *name) {
rz_return_val_if_fail(fcn && name, NULL);
void **it;
Expand Down Expand Up @@ -529,6 +552,10 @@ RZ_API bool rz_analysis_var_rename(RzAnalysisVar *var, const char *new_name, boo
return true;
}

/**
* Returns the argument number if the variable \p is argument of some function
* and matches the calling convention
*/
RZ_API int rz_analysis_var_get_argnum(RzAnalysisVar *var) {
rz_return_val_if_fail(var, -1);
RzAnalysis *analysis = var->fcn->analysis;
Expand Down Expand Up @@ -681,10 +708,16 @@ RZ_API RzAnalysisVarAccess *rz_analysis_var_get_access_at(RzAnalysisVar *var, ut
return NULL;
}

/**
* \brief Adds a type constraint \p constraint to a \p var variables
*/
RZ_API void rz_analysis_var_add_constraint(RzAnalysisVar *var, RZ_BORROW RzTypeConstraint *constraint) {
rz_vector_push(&var->constraints, constraint);
}

/**
* \brief Get all type constraints of a \p var variable in the text form
*/
RZ_API char *rz_analysis_var_get_constraints_readable(RzAnalysisVar *var) {
size_t n = var->constraints.len;
if (!n) {
Expand Down
2 changes: 2 additions & 0 deletions librz/include/rz_analysis.h
Original file line number Diff line number Diff line change
Expand Up @@ -2145,6 +2145,8 @@ RZ_API RzAnalysisClassErr rz_analysis_class_vtable_delete(RzAnalysis *analysis,
RZ_API RzGraph /*<RzGraphNodeInfo *>*/ *rz_analysis_class_get_inheritance_graph(RzAnalysis *analysis);

RZ_API RZ_OWN RzPVector /*<RzAnalysisVar *>*/ *rz_analysis_function_args(RzAnalysis *a, RzAnalysisFunction *fcn);
RZ_API RZ_OWN RzPVector /*<RzAnalysisVar *>*/ *rz_analysis_function_vars(RZ_NONNULL RzAnalysis *a, RZ_NONNULL RzAnalysisFunction *fcn);
RZ_API RZ_BORROW RzAnalysisVar *rz_analysis_function_get_arg_idx(RZ_NONNULL RzAnalysis *analysis, RZ_NONNULL RzAnalysisFunction *f, size_t index);
RZ_API RZ_OWN RzList /*<RzType *>*/ *rz_analysis_types_from_fcn(RzAnalysis *analysis, RzAnalysisFunction *fcn);
RZ_API RZ_OWN RzCallable *rz_analysis_function_derive_type(RzAnalysis *analysis, RzAnalysisFunction *f);

Expand Down

0 comments on commit 33c056e

Please sign in to comment.