Register roles (RzRegisterId) are now associated directly with an RzRegItem in RzReg rather than mapping to name strings, which previously needed an additional hashtable lookup to get more information about the register. Conversely, if the name is needed from an RzRegItem, it is available directly as a member. This is not a pure refactor as there were cases before where a register name was assigned to a role in the register profile, but no register actually existed under that name. Such cases will now cause a warning to be printed during profile load and the role association will be ignored. Changes in register profiles in this commit are for fixing such cases.
520 lines
14 KiB
C
520 lines
14 KiB
C
// SPDX-FileCopyrightText: 2009-2020 pancake <pancake@nopcode.org>
|
|
// SPDX-License-Identifier: LGPL-3.0-only
|
|
|
|
#include <rz_core.h>
|
|
#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
|
|
};
|
|
|
|
/**
|
|
* \brief Check if the given register has the given RegisterId.
|
|
*
|
|
* \param reg const RzReg Pointer
|
|
* \param name Register name.
|
|
* \param id RegisterId to check.
|
|
* \return true if the register has the given RegisterId, false otherwise.
|
|
*/
|
|
RZ_API bool rz_reg_is_role(const RzReg *reg, const char *name, const RzRegisterId id) {
|
|
rz_return_val_if_fail(reg && name, false);
|
|
const char *sp = rz_reg_get_name(reg, id);
|
|
if (!sp) {
|
|
return false;
|
|
}
|
|
return RZ_STR_EQ(sp, name);
|
|
}
|
|
|
|
// 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->comment);
|
|
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 const char *rz_reg_get_name(const RzReg *reg, const int role) {
|
|
if (reg && role >= 0 && role < RZ_REG_NAME_LAST) {
|
|
return reg->by_role[role] ? reg->by_role[role]->name : NULL;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
RZ_API RzRegItem *rz_reg_get_by_role(RzReg *reg, RzRegisterId role) {
|
|
rz_return_val_if_fail(reg, NULL);
|
|
return reg->by_role[role];
|
|
}
|
|
|
|
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;
|
|
memset(reg->by_role, 0, sizeof(reg->by_role));
|
|
|
|
for (i = 0; i < RZ_REG_TYPE_LAST; i++) {
|
|
ht_sp_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_val(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, void *user) {
|
|
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, NULL);
|
|
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(const 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++) {
|
|
HtSP *ht = reg->regset[i].ht_regs;
|
|
if (ht) {
|
|
bool found = false;
|
|
RzRegItem *item = ht_sp_find(ht, 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;
|
|
}
|