682 lines
18 KiB
C
682 lines
18 KiB
C
// SPDX-FileCopyrightText: 2009-2020 pancake <pancake@nopcode.org>
|
|
// SPDX-License-Identifier: LGPL-3.0-only
|
|
|
|
#include <rz_list.h>
|
|
#include <rz_util/rz_log.h>
|
|
#include <rz_util/rz_str.h>
|
|
#include <rz_reg.h>
|
|
#include <rz_util.h>
|
|
#include <rz_util/rz_assert.h>
|
|
#include <rz_lib.h>
|
|
#include <string.h>
|
|
|
|
#define GDB_NAME_SZ 16
|
|
#define GDB_TYPE_SZ 16
|
|
#define GDB_GROUPS_SZ 128
|
|
|
|
static void rz_reg_profile_def_free(RzRegProfileDef *def) {
|
|
if (!def) {
|
|
return;
|
|
}
|
|
free(def->name);
|
|
free(def->comment);
|
|
free(def->flags);
|
|
free(def);
|
|
}
|
|
|
|
static void rz_reg_profile_alias_free(RzRegProfileAlias *alias) {
|
|
if (!alias) {
|
|
return;
|
|
}
|
|
free(alias->reg_name);
|
|
free(alias->alias);
|
|
free(alias);
|
|
}
|
|
|
|
/**
|
|
* Like rz_reg_type_by_name, but warn if non-existent
|
|
*/
|
|
static int expect_reg_type_by_name(const char *str) {
|
|
int r = rz_reg_type_by_name(str);
|
|
if (r < 0) {
|
|
RZ_LOG_WARN("No register type for type abbreviation \"%s\".\n", str);
|
|
}
|
|
return r;
|
|
}
|
|
|
|
/**
|
|
* \brief Parses a register type string.
|
|
*
|
|
* The type string must be of the following form:
|
|
*
|
|
* <sub-type>@<main-type>
|
|
*
|
|
* "<sub-type>@" is optional.
|
|
*
|
|
* \param item Register item whichs types are set.
|
|
* \param type_str The type string.
|
|
* \return true On success.
|
|
* \return false On failure.
|
|
*/
|
|
static bool parse_type(RZ_OUT RzRegProfileDef *def, const char *type_str) {
|
|
rz_return_val_if_fail(def && type_str, false);
|
|
char *s = rz_str_dup(type_str);
|
|
char *at = strchr(s, '@');
|
|
if (at) {
|
|
// This register has a secondary type e.g. xmm@fpu
|
|
def->arena_type = expect_reg_type_by_name(at + 1);
|
|
s[at - s] = '\0';
|
|
def->type = expect_reg_type_by_name(s);
|
|
} else {
|
|
def->type = expect_reg_type_by_name(s);
|
|
def->arena_type = def->type;
|
|
if (def->type == RZ_REG_TYPE_FLG) { // Hack to put flgs into gpr arena.
|
|
def->arena_type = RZ_REG_TYPE_GPR;
|
|
} else {
|
|
def->arena_type = def->type;
|
|
}
|
|
}
|
|
bool res = true;
|
|
if (def->type < 0 || def->arena_type < 0) {
|
|
RZ_LOG_ERROR("Illegal type abbreviation \"%s\"\n", s);
|
|
res = false;
|
|
}
|
|
free(s);
|
|
return res;
|
|
}
|
|
|
|
/**
|
|
* \brief Parses the size of a register definition.
|
|
* Sizes with . in front are in bits. Otherwise in bytes.
|
|
*
|
|
* \param s Size string.
|
|
* \return ut32 The size as integer or UT64_MAX if it fails.
|
|
*/
|
|
static ut32 parse_size(char *s) {
|
|
rz_return_val_if_fail(s, UT32_MAX);
|
|
if (s[0] == '.') {
|
|
return strtoul(s + 1, NULL, 0);
|
|
} else {
|
|
return strtoul(s, NULL, 0) * 8;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* \brief Parses the offset of a register defintion and sets the offset in \p def->offset.
|
|
*
|
|
* Offset is of the form: <byte>.<bit>
|
|
* .<bit> is optional.
|
|
*
|
|
* \param s Offset string.
|
|
* \param def The defintion item to store the offset into \p def->offset in bits.
|
|
* \return false On failure (sets def->offset = UT32_MAX).
|
|
* \return true On success.
|
|
*/
|
|
static bool parse_offset(const char *s, RZ_OUT RzRegProfileDef *def) {
|
|
rz_return_val_if_fail(s && def, false);
|
|
if (s[0] == '?') {
|
|
def->offset = UT32_MAX;
|
|
return true;
|
|
} else if (s[0] == '.') {
|
|
def->offset = strtoul(s + 1, NULL, 0);
|
|
return true;
|
|
}
|
|
def->offset = strtoul(s, NULL, 0) * 8;
|
|
|
|
const char *bi = strchr(s, '.');
|
|
if (!bi) {
|
|
// No bit offset given.
|
|
return true;
|
|
}
|
|
|
|
ut8 bit_offset = strtoul(bi + 1, NULL, 0);
|
|
def->offset += bit_offset;
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* \brief Parses a register alias.
|
|
*
|
|
* The alias is of the form:
|
|
* "=<alias> <reg name>"
|
|
*
|
|
* \param reg The RzReg struct with the register profile.
|
|
* \param tokens A list with both tokens of the alias string.
|
|
* \return true On success.
|
|
* \return false On Failure.
|
|
*/
|
|
static bool parse_alias(RZ_OUT RzList /*<RzRegProfileAlias *>*/ *alias_list, RZ_BORROW RzList /*<char *>*/ *tokens) {
|
|
rz_return_val_if_fail(alias_list && tokens, false);
|
|
RzRegProfileAlias *pa = RZ_NEW0(RzRegProfileAlias);
|
|
if (!pa) {
|
|
RZ_LOG_WARN("Unable to allocate memory.\n");
|
|
return false;
|
|
}
|
|
|
|
const char *real_name = rz_list_get_n(tokens, 1);
|
|
const char *alias = rz_list_get_n(tokens, 0);
|
|
if (!alias) {
|
|
RZ_LOG_WARN("Failed to get alias name from token.\n");
|
|
free(pa);
|
|
return false;
|
|
}
|
|
|
|
RzRegisterId role = rz_reg_get_name_idx(alias + 1);
|
|
if (!(role >= 0 && role < RZ_REG_NAME_LAST)) {
|
|
RZ_LOG_WARN("Invalid alias\n");
|
|
free(pa);
|
|
return false;
|
|
}
|
|
|
|
pa->alias = rz_str_dup(alias);
|
|
pa->reg_name = rz_str_dup(real_name);
|
|
pa->role = role;
|
|
rz_list_append(alias_list, pa);
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* \brief Parses a register definition.
|
|
*
|
|
* \param reg Register struct with the register profile.
|
|
* \param tokens List of strings of a single register definition.
|
|
* \return false On failure.
|
|
* \return true On success.
|
|
*/
|
|
static bool parse_def(RZ_OUT RzList /*<RzRegProfileDef *>*/ *def_list, RZ_BORROW RzList /*<char *>*/ *tokens) {
|
|
rz_return_val_if_fail(def_list && tokens, false);
|
|
|
|
RzRegProfileDef *def = RZ_NEW0(RzRegProfileDef);
|
|
if (!def) {
|
|
RZ_LOG_WARN("Unable to allocate memory.\n");
|
|
return false;
|
|
}
|
|
const char *name = rz_list_get_n(tokens, 1);
|
|
if (!name) {
|
|
goto reg_parse_error;
|
|
}
|
|
def->name = rz_str_dup(name);
|
|
|
|
if (!parse_type(def, rz_list_get_n(tokens, 0))) {
|
|
RZ_LOG_WARN("Invalid register type.\n");
|
|
goto reg_parse_error;
|
|
}
|
|
|
|
def->size = parse_size(rz_list_get_n(tokens, 2));
|
|
if (def->size == UT32_MAX || def->size == 0) {
|
|
RZ_LOG_WARN("Invalid register size.\n");
|
|
goto reg_parse_error;
|
|
}
|
|
|
|
def->packed = parse_size(rz_list_get_n(tokens, 4));
|
|
if (def->packed == UT32_MAX) {
|
|
RZ_LOG_WARN("Invalid register packed size.\n");
|
|
goto reg_parse_error;
|
|
}
|
|
|
|
if (!parse_offset(rz_list_get_n(tokens, 3), def)) {
|
|
RZ_LOG_WARN("Invalid register offset.\n");
|
|
goto reg_parse_error;
|
|
}
|
|
|
|
// Comments and flags are optional
|
|
if (rz_list_length(tokens) == 6) {
|
|
const char *comment_flag = rz_list_get_n(tokens, 5);
|
|
if (!comment_flag) {
|
|
goto reg_parse_error;
|
|
}
|
|
if (comment_flag[0] == '#') {
|
|
// Remove # from the comment
|
|
def->comment = rz_str_dup(comment_flag + 1);
|
|
} else {
|
|
def->flags = rz_str_dup(comment_flag);
|
|
}
|
|
}
|
|
RZ_LOG_DEBUG("profile: register def: %s %d %d %s\n", def->name, def->size, def->offset, def->flags);
|
|
rz_list_append(def_list, def);
|
|
|
|
return true;
|
|
|
|
reg_parse_error:
|
|
rz_reg_profile_def_free(def);
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* \brief Parses a register profile string. Each line is either a register alias or a register definiton.
|
|
*
|
|
* A register alias string is of the following form:
|
|
* "=<alias> <name>\n"
|
|
*
|
|
* A register definition string is of the following form:
|
|
* "(<sub-type>@)main-type <name> .<size> <byte offset>(.<bit offset>) <packed> (# <comment> OR <flags>)\n"
|
|
*
|
|
* Elements in "()" are optional.
|
|
* Each "<...>" token is separated by tab or space characters.
|
|
*
|
|
* * alias: Register alias (e.g. PC, A1 etc.)
|
|
* * name: Register name.
|
|
* * size: Register size in bits.
|
|
* * main-type: Register type: gpr, fpr, ctr, flg etc.
|
|
* * sub-type: The second register type (e.g. xmm@fpu : xmm is sub-type of fpu)
|
|
* * byte offset: Offset into register profile in bytes.
|
|
* * bit offset: Offset into the byte offset in bits.
|
|
* * packed: Packed size of the register in bytes.
|
|
* * comment: A comment about the register.
|
|
* * Flags this register holds.
|
|
*
|
|
* \param reg Register struct which holds all register items.
|
|
* \param profile Register profile string.
|
|
* \return false On failure.
|
|
* \return true On success.
|
|
*/
|
|
static bool parse_reg_profile_str(RZ_OUT RzList /*<RzRegProfileAlias *>*/ *alias_list, RZ_OUT RzList /*<RzRegProfileDef *>*/ *def_list, const char *profile_str) {
|
|
rz_return_val_if_fail(alias_list && def_list && profile_str, false);
|
|
|
|
RzList *def_lines = rz_str_split_duplist_n(profile_str, "\n", 0, true);
|
|
rz_return_val_if_fail(def_lines, false);
|
|
|
|
st32 l = 0; // Line number
|
|
const char *line;
|
|
bool is_alias = false;
|
|
RzListIter *it;
|
|
RzList *toks = NULL;
|
|
rz_list_foreach (def_lines, it, line) {
|
|
++l;
|
|
if (RZ_STR_ISEMPTY(line)) {
|
|
continue;
|
|
}
|
|
if (rz_str_strchr(line, "#")) {
|
|
RzList *line_and_cmt = rz_str_split_duplist_n_regex(line, "\\#", 0, true);
|
|
char *raw_comment = rz_str_dup(rz_list_last_val(line_and_cmt));
|
|
if (!raw_comment) {
|
|
RZ_LOG_WARN("Comment could not be split from register definition. Line: \"%s\"\n", line);
|
|
continue;
|
|
}
|
|
char *comment = rz_str_prepend(raw_comment, "#");
|
|
if (!comment) {
|
|
RZ_LOG_WARN("Could not prepend # to comment. Line: \"%s\".\n", line);
|
|
continue;
|
|
}
|
|
toks = rz_str_split_duplist_n_regex(rz_list_first_val(line_and_cmt), "\\s+", 0, true);
|
|
rz_list_append(toks, comment);
|
|
rz_list_free(line_and_cmt);
|
|
} else {
|
|
toks = rz_str_split_duplist_n_regex(line, "\\s+", 0, true);
|
|
}
|
|
ut32 toks_len = rz_list_length(toks);
|
|
if (rz_list_empty(toks)) {
|
|
goto free_toks_continue;
|
|
}
|
|
|
|
const char *first_tok = rz_list_get_n(toks, 0);
|
|
if (first_tok[0] == '#') { // Comment line
|
|
goto free_toks_continue;
|
|
} else if (first_tok[0] == '=') { // Alias
|
|
if (toks_len != 2) {
|
|
RZ_LOG_WARN("Invalid number of %d columns in alias \"%s\" at line %d. 2 needed.\n", toks_len, line, l);
|
|
goto free_toks_continue;
|
|
}
|
|
is_alias = true;
|
|
} else if (isalpha(first_tok[0])) {
|
|
if (toks_len != 5 && toks_len != 6) {
|
|
RZ_LOG_WARN("Invalid number of %d columns in definition \"%s\" at line %d. 5 or 6 needed.\n", toks_len, line, l);
|
|
goto free_toks_continue;
|
|
}
|
|
} else {
|
|
RZ_LOG_WARN("Invalid line \"%s\" at register profiles line %d.\n", line, l);
|
|
goto free_toks_continue;
|
|
}
|
|
bool success = is_alias
|
|
? parse_alias(alias_list, toks)
|
|
: parse_def(def_list, toks);
|
|
if (!success) {
|
|
RZ_LOG_WARN("Parsing error in \"%s\" at line %d.\n", line, l);
|
|
rz_list_free(toks);
|
|
rz_list_free(def_lines);
|
|
return false;
|
|
}
|
|
is_alias = false;
|
|
free_toks_continue:
|
|
rz_list_free(toks);
|
|
}
|
|
rz_list_free(def_lines);
|
|
|
|
return true;
|
|
}
|
|
|
|
static void add_item_to_regset(RZ_BORROW RzReg *reg, RZ_BORROW RzRegItem *item) {
|
|
rz_return_if_fail(reg && item);
|
|
RzRegisterType t = item->arena;
|
|
|
|
if (!reg->regset[t].regs) {
|
|
reg->regset[t].regs = rz_list_newf((RzListFree)rz_reg_item_free);
|
|
}
|
|
if (!reg->regset[t].ht_regs) {
|
|
reg->regset[t].ht_regs = ht_sp_new(HT_STR_DUP, NULL, NULL);
|
|
}
|
|
|
|
// Dynamically update the list of supported bit sizes
|
|
reg->bits |= item->size;
|
|
rz_list_append(reg->regset[t].regs, item);
|
|
ht_sp_insert(reg->regset[t].ht_regs, item->name, item);
|
|
|
|
// Update the overall type of registers into a regset
|
|
if (item->type == RZ_REG_TYPE_ANY) {
|
|
reg->regset[t].maskregstype = UT32_MAX;
|
|
return;
|
|
}
|
|
reg->regset[t].maskregstype |= ((int)1 << item->type);
|
|
}
|
|
|
|
/**
|
|
* \brief Fills \p reg->regset with the definitions and alias of the register profile.
|
|
*
|
|
* \param reg The RzReg struct which holds the register profile and an empty \p reg->regset
|
|
* \return false On failure.
|
|
* \return true On success.
|
|
*/
|
|
RZ_API bool rz_reg_set_reg_profile(RZ_BORROW RzReg *reg) {
|
|
rz_return_val_if_fail(reg, false);
|
|
rz_return_val_if_fail(reg->reg_profile.alias && reg->reg_profile.defs, false);
|
|
|
|
RzListIter *it;
|
|
RzRegProfileAlias *alias;
|
|
rz_list_foreach (reg->reg_profile.alias, it, alias) {
|
|
if (!rz_reg_set_name(reg, alias->role, alias->reg_name)) {
|
|
RZ_LOG_WARN("Invalid alias gviven.\n");
|
|
return false;
|
|
}
|
|
}
|
|
RzRegProfileDef *def;
|
|
rz_list_foreach (reg->reg_profile.defs, it, def) {
|
|
RzRegItem *item = RZ_NEW0(RzRegItem);
|
|
if (!item) {
|
|
RZ_LOG_WARN("Unable to allocate memory.\n");
|
|
return false;
|
|
}
|
|
|
|
item->name = rz_str_dup(def->name);
|
|
|
|
item->type = def->type;
|
|
item->arena = def->arena_type;
|
|
|
|
item->size = def->size;
|
|
item->offset = def->offset;
|
|
// Update the overall profile size
|
|
if (item->offset + item->size > reg->size) {
|
|
reg->size = item->offset + item->size;
|
|
}
|
|
|
|
item->packed_size = def->packed;
|
|
|
|
if (def->comment) {
|
|
item->comment = rz_str_dup(def->comment);
|
|
}
|
|
if (def->flags) {
|
|
item->flags = rz_str_dup(def->flags);
|
|
}
|
|
|
|
add_item_to_regset(reg, item);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* \brief Parses a register profile string and sets up all registers accordingly in \p reg.
|
|
*
|
|
* \param reg The RzReg struct which should hold the register data.
|
|
* \param profile The register profile string.
|
|
* \return false On failure;
|
|
* \return true On success.
|
|
*/
|
|
RZ_API bool rz_reg_set_profile_string(RZ_NONNULL RzReg *reg, RZ_NONNULL const char *profile_str) {
|
|
rz_return_val_if_fail(reg && profile_str, false);
|
|
// Same profile, no need to change
|
|
if (reg->reg_profile_str && !strcmp(reg->reg_profile_str, profile_str)) {
|
|
return true;
|
|
}
|
|
|
|
// we should reset all the arenas before setting the new reg profile
|
|
rz_reg_arena_pop(reg);
|
|
// Purge the old registers
|
|
rz_reg_free_internal(reg, true);
|
|
rz_reg_arena_shrink(reg);
|
|
|
|
// Cache the profile string
|
|
reg->reg_profile_str = rz_str_dup(profile_str);
|
|
reg->reg_profile.defs = rz_list_newf((RzListFree)rz_reg_profile_def_free);
|
|
reg->reg_profile.alias = rz_list_newf((RzListFree)rz_reg_profile_alias_free);
|
|
rz_return_val_if_fail(reg->reg_profile.defs && reg->reg_profile.alias, true);
|
|
|
|
if (!parse_reg_profile_str(reg->reg_profile.alias, reg->reg_profile.defs, profile_str)) {
|
|
RZ_LOG_WARN("Could not parse register profile string.\n");
|
|
rz_reg_free_internal(reg, false);
|
|
return false;
|
|
}
|
|
|
|
// Check for duplicates
|
|
RzListIter *it, *tmp;
|
|
RzRegProfileDef *def;
|
|
rz_list_foreach_safe (reg->reg_profile.defs, it, tmp, def) {
|
|
if (rz_reg_get(reg, def->name, RZ_REG_TYPE_ANY)) {
|
|
RZ_LOG_WARN("Ignoring duplicated register definition '%s'.\n", def->name);
|
|
rz_list_delete(reg->reg_profile.defs, it);
|
|
}
|
|
}
|
|
|
|
if (!rz_reg_set_reg_profile(reg)) {
|
|
RZ_LOG_WARN("Could not set reg profile.\n");
|
|
return false;
|
|
}
|
|
|
|
reg->size = 0;
|
|
for (ut32 i = 0; i < RZ_REG_TYPE_LAST; i++) {
|
|
RzRegSet *rs = ®->regset[i];
|
|
if (rs && rs->arena) {
|
|
reg->size += rs->arena->size; // Sums minimum arena size.
|
|
}
|
|
}
|
|
|
|
rz_reg_fit_arena(reg);
|
|
// dup the last arena to allow regdiffing
|
|
rz_reg_arena_push(reg);
|
|
rz_reg_reindex(reg);
|
|
return true;
|
|
}
|
|
|
|
RZ_API bool rz_reg_set_profile(RzReg *reg, const char *profile) {
|
|
rz_return_val_if_fail(reg && profile, false);
|
|
char *base, *file;
|
|
char *str = rz_file_slurp(profile, NULL);
|
|
if (!str) {
|
|
base = rz_sys_getenv(RZ_LIB_ENV);
|
|
if (base) {
|
|
file = rz_str_append(base, profile);
|
|
str = rz_file_slurp(file, NULL);
|
|
free(file);
|
|
}
|
|
}
|
|
if (!str) {
|
|
eprintf("rz_reg_set_profile: Cannot find '%s'\n", profile);
|
|
return false;
|
|
}
|
|
bool ret = rz_reg_set_profile_string(reg, str);
|
|
free(str);
|
|
return ret;
|
|
}
|
|
|
|
static char *gdb_to_rz_profile(const char *gdb) {
|
|
rz_return_val_if_fail(gdb, NULL);
|
|
RzStrBuf *sb = rz_strbuf_new("");
|
|
if (!sb) {
|
|
return NULL;
|
|
}
|
|
char *ptr1, *gptr, *gptr1;
|
|
char name[GDB_NAME_SZ + 1], groups[GDB_GROUPS_SZ + 1], type[GDB_TYPE_SZ + 1];
|
|
const int all = 1, gpr = 2, save = 4, restore = 8, float_ = 16,
|
|
sse = 32, vector = 64, system = 128, mmx = 256;
|
|
int number, rel, offset, size, type_bits, ret;
|
|
// Every line is -
|
|
// Name Number Rel Offset Size Type Groups
|
|
const char *ptr = rz_str_trim_head_ro(gdb);
|
|
|
|
// It's possible someone includes the heading line too. Skip it
|
|
if (rz_str_startswith(ptr, "Name")) {
|
|
if (!(ptr = strchr(ptr, '\n'))) {
|
|
rz_strbuf_free(sb);
|
|
return NULL;
|
|
}
|
|
ptr++;
|
|
}
|
|
for (;;) {
|
|
// Skip whitespace at beginning of line and empty lines
|
|
while (isspace((ut8)*ptr)) {
|
|
ptr++;
|
|
}
|
|
if (!*ptr) {
|
|
break;
|
|
}
|
|
if ((ptr1 = strchr(ptr, '\n'))) {
|
|
*ptr1 = '\0';
|
|
} else {
|
|
eprintf("Could not parse line: %s (missing \\n)\n", ptr);
|
|
rz_strbuf_free(sb);
|
|
return false;
|
|
}
|
|
ret = sscanf(ptr, " %" RZ_STR_DEF(GDB_NAME_SZ) "s %d %d %d %d %" RZ_STR_DEF(GDB_TYPE_SZ) "s %" RZ_STR_DEF(GDB_GROUPS_SZ) "s",
|
|
name, &number, &rel, &offset, &size, type, groups);
|
|
// Groups is optional, others not
|
|
if (ret < 6) {
|
|
if (*ptr != '*') {
|
|
eprintf("Could not parse line: %s\n", ptr);
|
|
rz_strbuf_free(sb);
|
|
return NULL;
|
|
}
|
|
ptr = ptr1 + 1;
|
|
continue;
|
|
}
|
|
// If name is '', then skip
|
|
if (rz_str_startswith(name, "''")) {
|
|
if (!ptr1) {
|
|
break;
|
|
}
|
|
ptr = ptr1 + 1;
|
|
continue;
|
|
}
|
|
// If size is 0, skip
|
|
if (size == 0) {
|
|
if (!ptr1) {
|
|
break;
|
|
}
|
|
ptr = ptr1 + 1;
|
|
continue;
|
|
}
|
|
type_bits = 0;
|
|
// Parse group
|
|
if (ret >= 7) {
|
|
gptr = groups;
|
|
while (1) {
|
|
if ((gptr1 = strchr(gptr, ','))) {
|
|
*gptr1 = '\0';
|
|
}
|
|
if (rz_str_startswith(gptr, "general")) {
|
|
type_bits |= gpr;
|
|
} else if (rz_str_startswith(gptr, "all")) {
|
|
type_bits |= all;
|
|
} else if (rz_str_startswith(gptr, "save")) {
|
|
type_bits |= save;
|
|
} else if (rz_str_startswith(gptr, "restore")) {
|
|
type_bits |= restore;
|
|
} else if (rz_str_startswith(gptr, "float")) {
|
|
type_bits |= float_;
|
|
} else if (rz_str_startswith(gptr, "sse")) {
|
|
type_bits |= sse;
|
|
} else if (rz_str_startswith(gptr, "mmx")) {
|
|
type_bits |= mmx;
|
|
} else if (rz_str_startswith(gptr, "vector")) {
|
|
type_bits |= vector;
|
|
} else if (rz_str_startswith(gptr, "system")) {
|
|
type_bits |= system;
|
|
}
|
|
if (!gptr1) {
|
|
break;
|
|
}
|
|
gptr = gptr1 + 1;
|
|
}
|
|
}
|
|
// If type is not defined, skip
|
|
if (!*type) {
|
|
if (!ptr1) {
|
|
break;
|
|
}
|
|
ptr = ptr1 + 1;
|
|
continue;
|
|
}
|
|
// TODO: More mappings between gdb and rizin reg groups. For now, either fpu or gpr
|
|
if (!(type_bits & sse) && !(type_bits & float_)) {
|
|
type_bits |= gpr;
|
|
}
|
|
// Print line
|
|
rz_strbuf_appendf(sb, "%s\t%s\t.%d\t%d\t0\n",
|
|
// Ref: Comment above about more register type mappings
|
|
((type_bits & mmx) || (type_bits & float_) || (type_bits & sse)) ? "fpu" : "gpr",
|
|
name, size * 8, offset);
|
|
// Go to next line
|
|
if (!ptr1) {
|
|
break;
|
|
}
|
|
ptr = ptr1 + 1;
|
|
continue;
|
|
}
|
|
return rz_strbuf_drain(sb);
|
|
}
|
|
|
|
RZ_API char *rz_reg_parse_gdb_profile(const char *profile_file) {
|
|
char *str = NULL;
|
|
if (!(str = rz_file_slurp(profile_file, NULL))) {
|
|
char *base = rz_sys_getenv(RZ_LIB_ENV);
|
|
if (base) {
|
|
char *file = rz_str_appendf(base, RZ_SYS_DIR "%s", profile_file);
|
|
if (file) {
|
|
str = rz_file_slurp(file, NULL);
|
|
free(file);
|
|
}
|
|
}
|
|
}
|
|
if (str) {
|
|
char *ret = gdb_to_rz_profile(str);
|
|
free(str);
|
|
return ret;
|
|
}
|
|
eprintf("rz_reg_parse_gdb_profile: Cannot find '%s'\n", profile_file);
|
|
return NULL;
|
|
}
|
|
|
|
RZ_API char *rz_reg_profile_to_cc(RzReg *reg) {
|
|
const char *r0 = rz_reg_get_name_by_type(reg, "R0");
|
|
const char *a0 = rz_reg_get_name_by_type(reg, "A0");
|
|
const char *a1 = rz_reg_get_name_by_type(reg, "A1");
|
|
const char *a2 = rz_reg_get_name_by_type(reg, "A2");
|
|
const char *a3 = rz_reg_get_name_by_type(reg, "A3");
|
|
|
|
if (!a0) {
|
|
RZ_LOG_WARN("It is mandatory to have at least one argument register defined in the register profile.\n");
|
|
return NULL;
|
|
}
|
|
if (!r0) {
|
|
r0 = a0;
|
|
}
|
|
if (a3 && a2 && a1) {
|
|
return rz_str_newf("%s reg(%s, %s, %s, %s)", r0, a0, a1, a2, a3);
|
|
}
|
|
if (a2 && a1) {
|
|
return rz_str_newf("%s reg(%s, %s, %s)", r0, a0, a1, a2);
|
|
}
|
|
if (a1) {
|
|
return rz_str_newf("%s reg(%s, %s)", r0, a0, a1);
|
|
}
|
|
return rz_str_newf("%s reg(%s)", r0, a0);
|
|
}
|