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
518 lines
13 KiB
C
518 lines
13 KiB
C
// SPDX-FileCopyrightText: 2009-2020 pancake <pancake@nopcode.org>
|
|
// SPDX-License-Identifier: LGPL-3.0-only
|
|
|
|
#include <rz_list.h>
|
|
#include <rz_reg.h>
|
|
#include <rz_util.h>
|
|
|
|
RZ_LIB_VERSION(rz_reg);
|
|
|
|
static const char *types[RZ_REG_TYPE_LAST + 1] = {
|
|
"gpr", "drx", "fpu", "mmx", "xmm", "ymm", "flg", "seg", "sys", "sec", "vc", "vcc", "ctr", NULL
|
|
};
|
|
|
|
// Take the 32bits name of a register, and return the 64 bit name of it.
|
|
// If there is no equivalent 64 bit register return NULL.
|
|
// SLOW
|
|
RZ_API const char *rz_reg_32_to_64(RzReg *reg, const char *rreg32) {
|
|
int i, j = -1;
|
|
RzListIter *iter;
|
|
RzRegItem *item;
|
|
for (i = 0; i < RZ_REG_TYPE_LAST; i++) {
|
|
rz_list_foreach (reg->regset[i].regs, iter, item) {
|
|
if (item->size == 32 && !rz_str_casecmp(rreg32, item->name)) {
|
|
j = item->offset;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (j != -1) {
|
|
for (i = 0; i < RZ_REG_TYPE_LAST; i++) {
|
|
rz_list_foreach (reg->regset[i].regs, iter, item) {
|
|
if (item->offset == j && item->size == 64) {
|
|
return item->name;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
// Take the 64 bits name of a register, and return the 32 bit name of it.
|
|
// If there is no equivalent 32 bit register return NULL.
|
|
// SLOW
|
|
RZ_API const char *rz_reg_64_to_32(RzReg *reg, const char *rreg64) {
|
|
int i, j = -1;
|
|
RzListIter *iter;
|
|
RzRegItem *item;
|
|
for (i = 0; i < RZ_REG_TYPE_LAST; i++) {
|
|
rz_list_foreach (reg->regset[i].regs, iter, item) {
|
|
if (item->size == 64 && !rz_str_casecmp(rreg64, item->name)) {
|
|
j = item->offset;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (j != -1) {
|
|
for (i = 0; i < RZ_REG_TYPE_LAST; i++) {
|
|
rz_list_foreach (reg->regset[i].regs, iter, item) {
|
|
if (item->offset == j && item->size == 32) {
|
|
return item->name;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
RZ_API const char *rz_reg_get_type(int idx) {
|
|
return (idx >= 0 && idx < RZ_REG_TYPE_LAST) ? types[idx] : NULL;
|
|
}
|
|
|
|
RZ_API const char *rz_reg_get_name_by_type(RzReg *reg, const char *alias_name) {
|
|
const int n = rz_reg_get_name_idx(alias_name);
|
|
return (n != -1) ? rz_reg_get_name(reg, n) : NULL;
|
|
}
|
|
|
|
/**
|
|
* \brief Returns the register type for the given type abbreviation.
|
|
*
|
|
* \param str The type abbreviation (gpr, flg, sys etc.)
|
|
* \return int The register type or -1 on failure.
|
|
*/
|
|
RZ_API int rz_reg_type_by_name(const char *str) {
|
|
rz_return_val_if_fail(str, -1);
|
|
for (ut32 i = 0; i < RZ_REG_TYPE_LAST && types[i]; i++) {
|
|
if (!strcmp(types[i], str)) {
|
|
return i;
|
|
}
|
|
}
|
|
if (!strcmp(str, "all")) {
|
|
return RZ_REG_TYPE_ANY;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
RZ_API void rz_reg_item_free(RzRegItem *item) {
|
|
free(item->name);
|
|
free(item->flags);
|
|
free(item);
|
|
}
|
|
|
|
RZ_API int rz_reg_get_name_idx(const char *type) {
|
|
rz_return_val_if_fail(type, -1);
|
|
if (type[0] && type[1] && !type[2])
|
|
switch (*type | (type[1] << 8)) {
|
|
/* flags */
|
|
case 'Z' + ('F' << 8): return RZ_REG_NAME_ZF;
|
|
case 'S' + ('F' << 8): return RZ_REG_NAME_SF;
|
|
case 'C' + ('F' << 8): return RZ_REG_NAME_CF;
|
|
case 'O' + ('F' << 8): return RZ_REG_NAME_OF;
|
|
/* gpr */
|
|
case 'P' + ('C' << 8): return RZ_REG_NAME_PC;
|
|
case 'S' + ('R' << 8): return RZ_REG_NAME_SR;
|
|
case 'L' + ('R' << 8): return RZ_REG_NAME_LR;
|
|
case 'S' + ('P' << 8): return RZ_REG_NAME_SP;
|
|
case 'B' + ('P' << 8): return RZ_REG_NAME_BP;
|
|
case 'S' + ('N' << 8): return RZ_REG_NAME_SN;
|
|
/* args */
|
|
case 'A' + ('0' << 8): return RZ_REG_NAME_A0;
|
|
case 'A' + ('1' << 8): return RZ_REG_NAME_A1;
|
|
case 'A' + ('2' << 8): return RZ_REG_NAME_A2;
|
|
case 'A' + ('3' << 8): return RZ_REG_NAME_A3;
|
|
case 'A' + ('4' << 8): return RZ_REG_NAME_A4;
|
|
case 'A' + ('5' << 8): return RZ_REG_NAME_A5;
|
|
case 'A' + ('6' << 8): return RZ_REG_NAME_A6;
|
|
case 'A' + ('7' << 8): return RZ_REG_NAME_A7;
|
|
case 'A' + ('8' << 8): return RZ_REG_NAME_A8;
|
|
case 'A' + ('9' << 8): return RZ_REG_NAME_A9;
|
|
/* return values */
|
|
case 'R' + ('0' << 8): return RZ_REG_NAME_R0;
|
|
case 'R' + ('1' << 8): return RZ_REG_NAME_R1;
|
|
case 'R' + ('2' << 8): return RZ_REG_NAME_R2;
|
|
case 'R' + ('3' << 8): return RZ_REG_NAME_R3;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
RZ_API bool rz_reg_set_name(RZ_NONNULL RzReg *reg, RzRegisterId role, RZ_NONNULL const char *name) {
|
|
rz_return_val_if_fail(reg && name, false);
|
|
if (role >= 0 && role < RZ_REG_NAME_LAST) {
|
|
reg->name[role] = rz_str_dup(reg->name[role], name);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
RZ_API const char *rz_reg_get_name(RzReg *reg, int role) {
|
|
if (reg && role >= 0 && role < RZ_REG_NAME_LAST) {
|
|
return reg->name[role];
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
RZ_API RzRegItem *rz_reg_get_by_role(RzReg *reg, RzRegisterId role) {
|
|
rz_return_val_if_fail(reg, NULL);
|
|
const char *name = rz_reg_get_name(reg, role);
|
|
if (!name) {
|
|
return NULL;
|
|
}
|
|
return rz_reg_get(reg, name, RZ_REG_TYPE_ANY);
|
|
}
|
|
|
|
static const char *roles[RZ_REG_NAME_LAST + 1] = {
|
|
"PC", "SP", "SR", "BP", "LR",
|
|
"A0", "A1", "A2", "A3", "A4", "A5", "A6", "A7", "A8", "A9",
|
|
"R0", "R1", "R2", "R3",
|
|
"ZF", "SF", "CF", "OF",
|
|
"SN",
|
|
NULL
|
|
};
|
|
|
|
RZ_API const char *rz_reg_get_role(int role) {
|
|
if (role >= 0 && role < RZ_REG_NAME_LAST) {
|
|
return roles[role];
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/// Get the RzRegisterId with the given name or -1
|
|
RZ_API int rz_reg_role_by_name(RZ_NONNULL const char *str) {
|
|
rz_return_val_if_fail(str, -1);
|
|
int i;
|
|
for (i = 0; i < RZ_REG_NAME_LAST && roles[i]; i++) {
|
|
if (!strcmp(roles[i], str)) {
|
|
return i;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
RZ_API void rz_reg_free_internal(RzReg *reg, bool init) {
|
|
rz_return_if_fail(reg);
|
|
ut32 i;
|
|
|
|
rz_list_free(reg->roregs);
|
|
reg->roregs = NULL;
|
|
RZ_FREE(reg->reg_profile_str);
|
|
RZ_FREE(reg->reg_profile_cmt);
|
|
rz_list_free(reg->reg_profile.alias);
|
|
rz_list_free(reg->reg_profile.defs);
|
|
reg->reg_profile.alias = 0;
|
|
reg->reg_profile.defs = 0;
|
|
|
|
for (i = 0; i < RZ_REG_NAME_LAST; i++) {
|
|
if (reg->name[i]) {
|
|
RZ_FREE(reg->name[i]);
|
|
}
|
|
}
|
|
for (i = 0; i < RZ_REG_TYPE_LAST; i++) {
|
|
ht_pp_free(reg->regset[i].ht_regs);
|
|
reg->regset[i].ht_regs = NULL;
|
|
if (!reg->regset[i].pool) {
|
|
continue;
|
|
}
|
|
if (init) {
|
|
rz_list_free(reg->regset[i].regs);
|
|
reg->regset[i].regs = rz_list_newf((RzListFree)rz_reg_item_free);
|
|
} else {
|
|
rz_list_free(reg->regset[i].regs);
|
|
reg->regset[i].regs = NULL;
|
|
// Ensure arena is freed and its registered in the pool
|
|
if (!rz_list_delete_data(reg->regset[i].pool, reg->regset[i].arena)) {
|
|
rz_reg_arena_free(reg->regset[i].arena);
|
|
}
|
|
reg->regset[i].arena = NULL;
|
|
rz_list_free(reg->regset[i].pool);
|
|
reg->regset[i].pool = NULL;
|
|
}
|
|
}
|
|
if (!init) {
|
|
rz_list_free(reg->allregs);
|
|
reg->allregs = NULL;
|
|
}
|
|
reg->size = 0;
|
|
}
|
|
|
|
static int regcmp(RzRegItem *a, RzRegItem *b) {
|
|
int offa = (a->offset * 16) + a->size;
|
|
int offb = (b->offset * 16) + b->size;
|
|
return (offa > offb) - (offa < offb);
|
|
}
|
|
|
|
RZ_API void rz_reg_reindex(RzReg *reg) {
|
|
int i, index;
|
|
RzListIter *iter;
|
|
RzRegItem *r;
|
|
RzList *all = rz_list_newf(NULL);
|
|
for (i = 0; i < RZ_REG_TYPE_LAST; i++) {
|
|
rz_list_foreach (reg->regset[i].regs, iter, r) {
|
|
rz_list_append(all, r);
|
|
}
|
|
}
|
|
rz_list_sort(all, (RzListComparator)regcmp);
|
|
index = 0;
|
|
rz_list_foreach (all, iter, r) {
|
|
r->index = index++;
|
|
}
|
|
rz_list_free(reg->allregs);
|
|
reg->allregs = all;
|
|
}
|
|
|
|
RZ_API RzRegItem *rz_reg_index_get(RzReg *reg, int idx) {
|
|
RzRegItem *r;
|
|
RzListIter *iter;
|
|
if (idx < 0) {
|
|
return NULL;
|
|
}
|
|
if (!reg->allregs) {
|
|
rz_reg_reindex(reg);
|
|
}
|
|
rz_list_foreach (reg->allregs, iter, r) {
|
|
if (r->index == idx) {
|
|
return r;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
RZ_API void rz_reg_free(RzReg *reg) {
|
|
if (reg) {
|
|
rz_reg_free_internal(reg, false);
|
|
free(reg);
|
|
}
|
|
}
|
|
|
|
RZ_API RzReg *rz_reg_new(void) {
|
|
RzRegArena *arena;
|
|
RzReg *reg = RZ_NEW0(RzReg);
|
|
int i;
|
|
if (!reg) {
|
|
return NULL;
|
|
}
|
|
for (i = 0; i < RZ_REG_TYPE_LAST; i++) {
|
|
arena = rz_reg_arena_new(0);
|
|
if (!arena) {
|
|
free(reg);
|
|
return NULL;
|
|
}
|
|
reg->regset[i].pool = rz_list_newf((RzListFree)rz_reg_arena_free);
|
|
reg->regset[i].regs = rz_list_newf((RzListFree)rz_reg_item_free);
|
|
rz_list_push(reg->regset[i].pool, arena);
|
|
reg->regset[i].arena = arena;
|
|
}
|
|
rz_reg_arena_push(reg);
|
|
for (i = 0; i < RZ_REG_TYPE_LAST; i++) {
|
|
reg->regset[i].cur = rz_list_tail(reg->regset[i].pool);
|
|
}
|
|
return reg;
|
|
}
|
|
|
|
RZ_API bool rz_reg_is_readonly(RzReg *reg, RzRegItem *item) {
|
|
const char *name;
|
|
RzListIter *iter;
|
|
if (!reg->roregs) {
|
|
return false;
|
|
}
|
|
// XXX O(n)
|
|
rz_list_foreach (reg->roregs, iter, name) {
|
|
if (!strcmp(item->name, name)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
RZ_API ut64 rz_reg_setv(RzReg *reg, const char *name, ut64 val) {
|
|
rz_return_val_if_fail(reg && name, UT64_MAX);
|
|
RzRegItem *ri = rz_reg_get(reg, name, -1);
|
|
return ri ? rz_reg_set_value(reg, ri, val) : UT64_MAX;
|
|
}
|
|
|
|
RZ_API ut64 rz_reg_getv(RzReg *reg, const char *name) {
|
|
rz_return_val_if_fail(reg && name, UT64_MAX);
|
|
RzRegItem *ri = rz_reg_get(reg, name, -1);
|
|
return ri ? rz_reg_get_value(reg, ri) : UT64_MAX;
|
|
}
|
|
|
|
RZ_API ut64 rz_reg_getv_by_role_or_name(RzReg *reg, const char *name) {
|
|
rz_return_val_if_fail(reg && name, UT64_MAX);
|
|
RzRegItem *ri = rz_reg_get_by_role_or_name(reg, name);
|
|
return ri ? rz_reg_get_value(reg, ri) : UT64_MAX;
|
|
}
|
|
|
|
RZ_API RzRegItem *rz_reg_get(RzReg *reg, const char *name, int type) {
|
|
int i, e;
|
|
rz_return_val_if_fail(reg && name, NULL);
|
|
// TODO: define flag register as RZ_REG_TYPE_FLG
|
|
if (type == RZ_REG_TYPE_FLG) {
|
|
type = RZ_REG_TYPE_GPR;
|
|
}
|
|
if (type == -1) {
|
|
i = 0;
|
|
e = RZ_REG_TYPE_LAST;
|
|
int alias = rz_reg_get_name_idx(name);
|
|
if (alias != -1) {
|
|
const char *nname = rz_reg_get_name(reg, alias);
|
|
if (nname) {
|
|
name = nname;
|
|
}
|
|
}
|
|
} else {
|
|
i = type;
|
|
e = type + 1;
|
|
}
|
|
for (; i < e; i++) {
|
|
HtPP *pp = reg->regset[i].ht_regs;
|
|
if (pp) {
|
|
bool found = false;
|
|
RzRegItem *item = ht_pp_find(pp, name, &found);
|
|
if (found) {
|
|
return item;
|
|
}
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
RZ_API RzRegItem *rz_reg_get_by_role_or_name(RzReg *reg, const char *name) {
|
|
int role = rz_reg_get_name_idx(name);
|
|
if (role != -1) {
|
|
RzRegItem *r = rz_reg_get_by_role(reg, role);
|
|
if (r) {
|
|
return r;
|
|
}
|
|
}
|
|
return rz_reg_get(reg, name, RZ_REG_TYPE_ANY);
|
|
}
|
|
|
|
RZ_API const RzList /*<RzRegItem *>*/ *rz_reg_get_list(RzReg *reg, int type) {
|
|
if (type == RZ_REG_TYPE_ANY) {
|
|
return reg->allregs;
|
|
}
|
|
|
|
RzList *regs;
|
|
int i, mask;
|
|
if (type < 0 || type > (RZ_REG_TYPE_LAST - 1)) {
|
|
return NULL;
|
|
}
|
|
|
|
regs = reg->regset[type].regs;
|
|
if (rz_list_length(regs) == 0) {
|
|
mask = ((int)1 << type);
|
|
for (i = 0; i < RZ_REG_TYPE_LAST; i++) {
|
|
if (reg->regset[i].maskregstype & mask) {
|
|
regs = reg->regset[i].regs;
|
|
}
|
|
}
|
|
}
|
|
|
|
return regs;
|
|
}
|
|
|
|
// TODO regsize is in bits, delta in bytes, maybe we should standarize this..
|
|
RZ_API RzRegItem *rz_reg_get_at(RzReg *reg, int type, int regsize, int delta) {
|
|
rz_return_val_if_fail(reg, NULL);
|
|
const RzList *list = rz_reg_get_list(reg, type);
|
|
RzRegItem *ri;
|
|
RzListIter *iter;
|
|
rz_list_foreach (list, iter, ri) {
|
|
if (ri->size == regsize) {
|
|
if (BITS2BYTES(ri->offset) == delta) {
|
|
return ri;
|
|
}
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/* return the next register in the current regset that differs from */
|
|
RZ_API RzRegItem *rz_reg_next_diff(RzReg *reg, int type, const ut8 *buf, int buflen, RzRegItem *prev_ri, int regsize) {
|
|
rz_return_val_if_fail(reg && buf, NULL);
|
|
if (type < 0 || type > (RZ_REG_TYPE_LAST - 1)) {
|
|
return NULL;
|
|
}
|
|
RzRegArena *arena = reg->regset[type].arena;
|
|
int prev_offset = prev_ri ? (prev_ri->offset / 8) + (prev_ri->size / 8) : 0;
|
|
RzList *list = reg->regset[type].regs;
|
|
RzRegItem *ri;
|
|
RzListIter *iter;
|
|
int offset;
|
|
rz_list_foreach (list, iter, ri) {
|
|
offset = ri->offset / 8;
|
|
if (offset > prev_offset) {
|
|
if (memcmp(arena->bytes + offset, buf + offset, ri->size / 8)) {
|
|
return ri;
|
|
}
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
RZ_API RzRegSet *rz_reg_regset_get(RzReg *r, int type) {
|
|
rz_return_val_if_fail(r, NULL);
|
|
if (type < 0 || type >= RZ_REG_TYPE_LAST) {
|
|
return NULL;
|
|
}
|
|
RzRegSet *rs = &r->regset[type];
|
|
return rs->arena ? rs : NULL;
|
|
}
|
|
|
|
static bool foreach_reg_cb(RzIntervalNode *node, void *user) {
|
|
RzRegItem *from_list = user;
|
|
RzRegItem *from_tree = node->data;
|
|
if (from_list == from_tree) {
|
|
return true;
|
|
}
|
|
// Check if from_list is covered entirely by from_tree, but is also smaller than it.
|
|
// We already know that
|
|
// from_tree->offset <= from_list->offset < from_tree->offset + from_tree->size
|
|
if (from_list->offset + from_list->size > from_tree->offset + from_tree->size) {
|
|
// from_list expands beyond from_tree, so it's not covered
|
|
return true;
|
|
}
|
|
if (from_list->offset + from_list->size == from_tree->offset + from_tree->size) {
|
|
// they end at the same position, so it is covered entirely, but is it also smaller?
|
|
if (from_list->offset == from_tree->offset) {
|
|
// nope
|
|
return true;
|
|
}
|
|
}
|
|
// from_list ends before from_tree, so it is covered and smaller
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* \brief Filter out all register items that are smaller than but covered entirely by some other register
|
|
* \param regs list of RzRegItem
|
|
*/
|
|
RZ_API RZ_OWN RzList /*<RzRegItem *>*/ *rz_reg_filter_items_covered(RZ_BORROW RZ_NONNULL const RzList /*<RzRegItem *>*/ *regs) {
|
|
rz_return_val_if_fail(regs, NULL);
|
|
RzList *ret = rz_list_new();
|
|
if (!ret) {
|
|
return NULL;
|
|
}
|
|
RzIntervalTree t;
|
|
rz_interval_tree_init(&t, NULL);
|
|
RzRegItem *item;
|
|
RzListIter *it;
|
|
rz_list_foreach (regs, it, item) {
|
|
if (item->offset < 0 || item->size <= 0) {
|
|
continue;
|
|
}
|
|
rz_interval_tree_insert(&t, item->offset, item->offset + item->size - 1, item);
|
|
}
|
|
rz_list_foreach (regs, it, item) {
|
|
if (item->offset < 0 || item->size <= 0) {
|
|
rz_list_push(ret, item);
|
|
continue;
|
|
}
|
|
if (!rz_interval_tree_all_in(&t, item->offset, true, foreach_reg_cb, item)) {
|
|
// foreach_reg_cb break-ed so it found a cover
|
|
continue;
|
|
}
|
|
rz_list_push(ret, item);
|
|
}
|
|
rz_interval_tree_fini(&t);
|
|
return ret;
|
|
}
|