Permalink
Cannot retrieve contributors at this time
678 lines (623 sloc)
18 KB
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
rizin/librz/reg/profile.c
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // 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> | |
| 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 = strdup(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 = strdup(alias); | |
| pa->reg_name = strdup(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 = strdup(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 = strdup(comment_flag + 1); | |
| } else { | |
| def->flags = strdup(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 = strdup(rz_list_get_top(line_and_cmt)); | |
| if (!raw_comment) { | |
| RZ_LOG_WARN("Comment could not be split from register definition. Line: \"%s\"\n", line); | |
| continue; | |
| } | |
| char *tmp = rz_str_prepend(raw_comment, "#"); | |
| if (!tmp) { | |
| RZ_LOG_WARN("Could not prepend # to comment. Line: \"%s\".\n", line); | |
| continue; | |
| } | |
| char *comment = strdup(tmp); | |
| toks = rz_str_split_duplist_n_regex(rz_list_get_bottom(line_and_cmt), "[[:blank:]]+", 0, true); | |
| rz_list_append(toks, comment); | |
| rz_list_free(line_and_cmt); | |
| } else { | |
| toks = rz_str_split_duplist_n_regex(line, "[[:blank:]]+", 0, true); | |
| } | |
| ut32 toks_len = rz_list_length(toks); | |
| if (rz_list_empty(toks)) { | |
| continue; | |
| } | |
| const char *first_tok = rz_list_get_n(toks, 0); | |
| if (first_tok[0] == '#') { // Comment line | |
| 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); | |
| 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); | |
| continue; | |
| } | |
| } else { | |
| RZ_LOG_WARN("Invalid line \"%s\" at register profiles line %d.\n", line, l); | |
| 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; | |
| 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_pp_new0(); | |
| } | |
| // Dynamically update the list of supported bit sizes | |
| reg->bits |= item->size; | |
| rz_list_append(reg->regset[t].regs, item); | |
| ht_pp_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 = strdup(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 = strdup(def->comment); | |
| } | |
| if (def->flags) { | |
| item->flags = strdup(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 = strdup(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[16], groups[128], type[16]; | |
| 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, " %s %d %d %d %d %s %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); | |
| } |