type: add tk commands to list and manage typeclasses (#3381)
Expose the typeclasses of types through the new `tk` commands: - `tk <type>` shows the typeclass of a type; - `tkl` lists the available typeclasses, while `tkll` (verbose), `tklt` (table) and `tklj` (JSON) additionally list the types belonging to each typeclass; - `tks <type> <typeclass>` sets the typeclass of a type. A new rz_base_type_set_typeclass() API backs the `tks` command. Since a typedef without its own typeclass inherits the one of the type it points to, setting the typeclass of an atomic type is automatically reflected on the typedefs resolving to it. The `tk` help also explains what typeclasses are and lists the available ones. Closes #3371
This commit is contained in:
parent
f4e477388b
commit
f2f4543a26
9 changed files with 301 additions and 0 deletions
|
|
@ -865,3 +865,41 @@ RZ_IPI RzCmdStatus rz_type_xrefs_list_all_handler(RzCore *core, int argc, const
|
|||
types_xrefs_all(core);
|
||||
return RZ_CMD_STATUS_OK;
|
||||
}
|
||||
|
||||
RZ_IPI RzCmdStatus rz_type_typeclass_handler(RzCore *core, int argc, const char **argv) {
|
||||
RzTypeDB *typedb = rz_analysis_get_type_db(core->analysis);
|
||||
RzBaseType *btype = rz_type_db_get_base_type(typedb, argv[1]);
|
||||
if (!btype) {
|
||||
RZ_LOG_ERROR("Type \"%s\" does not exist\n", argv[1]);
|
||||
return RZ_CMD_STATUS_ERROR;
|
||||
}
|
||||
RzTypeTypeclass typeclass = rz_base_type_typeclass(typedb, btype);
|
||||
rz_cons_println(rz_type_typeclass_as_string(typeclass));
|
||||
return RZ_CMD_STATUS_OK;
|
||||
}
|
||||
|
||||
RZ_IPI RzCmdStatus rz_type_typeclass_list_handler(RzCore *core, int argc, const char **argv, RzOutputMode mode) {
|
||||
rz_core_types_typeclass_print_all(core, mode);
|
||||
return RZ_CMD_STATUS_OK;
|
||||
}
|
||||
|
||||
RZ_IPI RzCmdStatus rz_type_typeclass_set_handler(RzCore *core, int argc, const char **argv) {
|
||||
RzTypeDB *typedb = rz_analysis_get_type_db(core->analysis);
|
||||
RzBaseType *btype = rz_type_db_get_base_type(typedb, argv[1]);
|
||||
if (!btype) {
|
||||
RZ_LOG_ERROR("Type \"%s\" does not exist\n", argv[1]);
|
||||
return RZ_CMD_STATUS_ERROR;
|
||||
}
|
||||
RzTypeTypeclass typeclass = rz_type_typeclass_from_string(argv[2]);
|
||||
// rz_type_typeclass_from_string returns RZ_TYPE_TYPECLASS_NONE both for the
|
||||
// "None" typeclass and for an unknown string, so reject unknown ones here.
|
||||
if (typeclass == RZ_TYPE_TYPECLASS_NONE && strcmp(argv[2], "None")) {
|
||||
RZ_LOG_ERROR("Unknown typeclass \"%s\"\n", argv[2]);
|
||||
return RZ_CMD_STATUS_ERROR;
|
||||
}
|
||||
if (!rz_base_type_set_typeclass(btype, typeclass)) {
|
||||
RZ_LOG_ERROR("Cannot set typeclass \"%s\" on type \"%s\"\n", argv[2], argv[1]);
|
||||
return RZ_CMD_STATUS_ERROR;
|
||||
}
|
||||
return RZ_CMD_STATUS_OK;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -96,6 +96,7 @@ static const RzCmdDescDetail print_hexdump_format_details[4];
|
|||
static const RzCmdDescDetail print_rising_and_falling_entropy_details[2];
|
||||
static const RzCmdDescDetail type_define_from_format_details[2];
|
||||
static const RzCmdDescDetail type_rename_details[2];
|
||||
static const RzCmdDescDetail type_typeclass_set_details[2];
|
||||
static const RzCmdDescDetail interactive_visual_details[2];
|
||||
static const RzCmdDescDetail write_details[3];
|
||||
static const RzCmdDescDetail write_bits_details[2];
|
||||
|
|
@ -906,6 +907,8 @@ static const RzCmdDescArg type_union_c_args[2];
|
|||
static const RzCmdDescArg type_union_c_nl_args[2];
|
||||
static const RzCmdDescArg type_xrefs_list_args[2];
|
||||
static const RzCmdDescArg type_xrefs_function_args[2];
|
||||
static const RzCmdDescArg type_typeclass_args[2];
|
||||
static const RzCmdDescArg type_typeclass_set_args[3];
|
||||
static const RzCmdDescArg interactive_visual_args[2];
|
||||
static const RzCmdDescArg interactive_panel_load_args[2];
|
||||
static const RzCmdDescArg interactive_panel_store_args[2];
|
||||
|
|
@ -19903,6 +19906,60 @@ static const RzCmdDescHelp type_xrefs_list_all_help = {
|
|||
.args = type_xrefs_list_all_args,
|
||||
};
|
||||
|
||||
static const RzCmdDescHelp tk_help = {
|
||||
.summary = "Manage the typeclasses of types",
|
||||
.description = "Typeclasses classify atomic types by the general kind of value they hold, independently of their concrete name or size, so the analysis can pick a suitable type generically (for example any signed integer). The available typeclasses are Num (any number), Integral (any integer), Floating (any floating point number), Address (integer types used to work with pointers), Signed Integral and Unsigned Integral (the signed and unsigned subclasses of Integral) and None (the most generic one, used when no specific typeclass applies).",
|
||||
};
|
||||
static const RzCmdDescArg type_typeclass_args[] = {
|
||||
{
|
||||
.name = "type",
|
||||
.type = RZ_CMD_ARG_TYPE_ANY_TYPE,
|
||||
|
||||
},
|
||||
{ 0 },
|
||||
};
|
||||
static const RzCmdDescHelp type_typeclass_help = {
|
||||
.summary = "Show the typeclass of the given type",
|
||||
.args = type_typeclass_args,
|
||||
};
|
||||
|
||||
static const RzCmdDescArg type_typeclass_list_args[] = {
|
||||
{ 0 },
|
||||
};
|
||||
static const RzCmdDescHelp type_typeclass_list_help = {
|
||||
.summary = "List all typeclasses, or the types belonging to each of them",
|
||||
.args = type_typeclass_list_args,
|
||||
};
|
||||
|
||||
static const RzCmdDescDetailEntry type_typeclass_set_Examples_detail_entries[] = {
|
||||
{ .text = "tks", .arg_str = " int Floating", .comment = "treat the int type as the Floating typeclass" },
|
||||
{ .text = "tks", .arg_str = " int \"Signed Integral\"", .comment = "set a typeclass whose name contains a space (quote it)" },
|
||||
{ 0 },
|
||||
};
|
||||
static const RzCmdDescDetail type_typeclass_set_details[] = {
|
||||
{ .name = "Examples", .entries = type_typeclass_set_Examples_detail_entries },
|
||||
{ 0 },
|
||||
};
|
||||
static const RzCmdDescArg type_typeclass_set_args[] = {
|
||||
{
|
||||
.name = "type",
|
||||
.type = RZ_CMD_ARG_TYPE_ANY_TYPE,
|
||||
|
||||
},
|
||||
{
|
||||
.name = "typeclass",
|
||||
.type = RZ_CMD_ARG_TYPE_STRING,
|
||||
.flags = RZ_CMD_ARG_FLAG_LAST,
|
||||
|
||||
},
|
||||
{ 0 },
|
||||
};
|
||||
static const RzCmdDescHelp type_typeclass_set_help = {
|
||||
.summary = "Set the typeclass of a type",
|
||||
.details = type_typeclass_set_details,
|
||||
.args = type_typeclass_set_args,
|
||||
};
|
||||
|
||||
static const RzCmdDescHelp V_help = {
|
||||
.summary = "Interactive mode",
|
||||
};
|
||||
|
|
@ -25835,6 +25892,14 @@ RZ_IPI void rzshell_cmddescs_init(RzCore *core) {
|
|||
RzCmdDesc *type_xrefs_list_all_cd = rz_cmd_desc_argv_new(core->rcmd, tx_cd, "txl", rz_type_xrefs_list_all_handler, &type_xrefs_list_all_help);
|
||||
rz_warn_if_fail(type_xrefs_list_all_cd);
|
||||
|
||||
RzCmdDesc *tk_cd = rz_cmd_desc_group_new(core->rcmd, t_cd, "tk", rz_type_typeclass_handler, &type_typeclass_help, &tk_help);
|
||||
rz_warn_if_fail(tk_cd);
|
||||
RzCmdDesc *type_typeclass_list_cd = rz_cmd_desc_argv_modes_new(core->rcmd, tk_cd, "tkl", RZ_OUTPUT_MODE_STANDARD | RZ_OUTPUT_MODE_LONG | RZ_OUTPUT_MODE_TABLE | RZ_OUTPUT_MODE_JSON, rz_type_typeclass_list_handler, &type_typeclass_list_help);
|
||||
rz_warn_if_fail(type_typeclass_list_cd);
|
||||
|
||||
RzCmdDesc *type_typeclass_set_cd = rz_cmd_desc_argv_new(core->rcmd, tk_cd, "tks", rz_type_typeclass_set_handler, &type_typeclass_set_help);
|
||||
rz_warn_if_fail(type_typeclass_set_cd);
|
||||
|
||||
RzCmdDesc *V_cd = rz_cmd_desc_group_new(core->rcmd, root_cd, "V", rz_interactive_visual_handler, &interactive_visual_help, &V_help);
|
||||
rz_warn_if_fail(V_cd);
|
||||
RzCmdDesc *interactive_visual_help_cd = rz_cmd_desc_argv_new(core->rcmd, V_cd, "VH", rz_interactive_visual_help_handler, &interactive_visual_help_help);
|
||||
|
|
|
|||
|
|
@ -2575,6 +2575,12 @@ RZ_IPI RzCmdStatus rz_type_xrefs_function_handler(RzCore *core, int argc, const
|
|||
RZ_IPI RzCmdStatus rz_type_xrefs_graph_handler(RzCore *core, int argc, const char **argv);
|
||||
// "txl"
|
||||
RZ_IPI RzCmdStatus rz_type_xrefs_list_all_handler(RzCore *core, int argc, const char **argv);
|
||||
// "tk"
|
||||
RZ_IPI RzCmdStatus rz_type_typeclass_handler(RzCore *core, int argc, const char **argv);
|
||||
// "tkl"
|
||||
RZ_IPI RzCmdStatus rz_type_typeclass_list_handler(RzCore *core, int argc, const char **argv, RzOutputMode mode);
|
||||
// "tks"
|
||||
RZ_IPI RzCmdStatus rz_type_typeclass_set_handler(RzCore *core, int argc, const char **argv);
|
||||
// "V"
|
||||
RZ_IPI RzCmdStatus rz_interactive_visual_handler(RzCore *core, int argc, const char **argv);
|
||||
// "VH"
|
||||
|
|
|
|||
|
|
@ -360,3 +360,47 @@ commands:
|
|||
cname: type_xrefs_list_all
|
||||
summary: List all types used by any function
|
||||
args: []
|
||||
- name: tk
|
||||
summary: Manage the typeclasses of types
|
||||
description: >
|
||||
Typeclasses classify atomic types by the general kind of value they hold,
|
||||
independently of their concrete name or size, so the analysis can pick a
|
||||
suitable type generically (for example any signed integer). The available
|
||||
typeclasses are Num (any number), Integral (any integer), Floating (any
|
||||
floating point number), Address (integer types used to work with
|
||||
pointers), Signed Integral and Unsigned Integral (the signed and unsigned
|
||||
subclasses of Integral) and None (the most generic one, used when no
|
||||
specific typeclass applies).
|
||||
subcommands:
|
||||
- name: tk
|
||||
cname: type_typeclass
|
||||
summary: Show the typeclass of the given type
|
||||
args:
|
||||
- name: type
|
||||
type: RZ_CMD_ARG_TYPE_ANY_TYPE
|
||||
- name: tkl
|
||||
cname: type_typeclass_list
|
||||
summary: List all typeclasses, or the types belonging to each of them
|
||||
modes:
|
||||
- RZ_OUTPUT_MODE_STANDARD
|
||||
- RZ_OUTPUT_MODE_LONG
|
||||
- RZ_OUTPUT_MODE_TABLE
|
||||
- RZ_OUTPUT_MODE_JSON
|
||||
args: []
|
||||
- name: tks
|
||||
cname: type_typeclass_set
|
||||
summary: Set the typeclass of a type
|
||||
args:
|
||||
- name: type
|
||||
type: RZ_CMD_ARG_TYPE_ANY_TYPE
|
||||
- name: typeclass
|
||||
type: RZ_CMD_ARG_TYPE_STRING
|
||||
details:
|
||||
- name: Examples
|
||||
entries:
|
||||
- text: "tks"
|
||||
arg_str: " int Floating"
|
||||
comment: "treat the int type as the Floating typeclass"
|
||||
- text: "tks"
|
||||
arg_str: ' int "Signed Integral"'
|
||||
comment: "set a typeclass whose name contains a space (quote it)"
|
||||
|
|
|
|||
|
|
@ -112,6 +112,7 @@ RZ_IPI void rz_core_types_struct_print_format_all(RzCore *core);
|
|||
RZ_IPI void rz_core_types_union_print_format_all(RzCore *core);
|
||||
RZ_IPI void rz_core_types_print_all(RzCore *core, RzOutputMode mode);
|
||||
RZ_IPI bool rz_core_types_rename(RzAnalysis *analysis, RZ_NONNULL const char *from, RZ_NONNULL const char *to);
|
||||
RZ_IPI void rz_core_types_typeclass_print_all(RzCore *core, RzOutputMode mode);
|
||||
RZ_IPI void rz_types_define(RzCore *core, const char *type);
|
||||
RZ_IPI bool rz_types_open_file(RzCore *core, const char *path);
|
||||
RZ_IPI bool rz_types_open_editor(RzCore *core, RZ_NONNULL const char *typename);
|
||||
|
|
|
|||
|
|
@ -1078,3 +1078,76 @@ RZ_IPI bool rz_core_types_rename(RzAnalysis *analysis, RZ_NONNULL const char *fr
|
|||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Prints all typeclasses, or the types belonging to each typeclass
|
||||
*
|
||||
* \param core RzCore instance
|
||||
* \param mode Output mode: STANDARD lists the typeclass names, LONG/TABLE/JSON
|
||||
* additionally list the atomic types belonging to each typeclass
|
||||
*/
|
||||
RZ_IPI void rz_core_types_typeclass_print_all(RzCore *core, RzOutputMode mode) {
|
||||
rz_return_if_fail(core && core->analysis);
|
||||
RzTypeDB *typedb = rz_analysis_get_type_db(core->analysis);
|
||||
RzTypeTypeclass tc;
|
||||
switch (mode) {
|
||||
case RZ_OUTPUT_MODE_STANDARD:
|
||||
for (tc = RZ_TYPE_TYPECLASS_NUM; tc < RZ_TYPE_TYPECLASS_INVALID; tc++) {
|
||||
rz_cons_println(rz_type_typeclass_as_string(tc));
|
||||
}
|
||||
break;
|
||||
case RZ_OUTPUT_MODE_LONG:
|
||||
for (tc = RZ_TYPE_TYPECLASS_NUM; tc < RZ_TYPE_TYPECLASS_INVALID; tc++) {
|
||||
rz_cons_printf("%s:\n", rz_type_typeclass_as_string(tc));
|
||||
RzList *types = rz_type_typeclass_get_all(typedb, tc);
|
||||
RzListIter *it;
|
||||
RzBaseType *btype;
|
||||
rz_list_foreach (types, it, btype) {
|
||||
rz_cons_printf(" %s\n", btype->name);
|
||||
}
|
||||
rz_list_free(types);
|
||||
}
|
||||
break;
|
||||
case RZ_OUTPUT_MODE_TABLE: {
|
||||
RzTable *table = rz_table_new();
|
||||
rz_table_set_columnsf(table, "ss", "typeclass", "type");
|
||||
for (tc = RZ_TYPE_TYPECLASS_NUM; tc < RZ_TYPE_TYPECLASS_INVALID; tc++) {
|
||||
const char *tcname = rz_type_typeclass_as_string(tc);
|
||||
RzList *types = rz_type_typeclass_get_all(typedb, tc);
|
||||
RzListIter *it;
|
||||
RzBaseType *btype;
|
||||
rz_list_foreach (types, it, btype) {
|
||||
rz_table_add_rowf(table, "ss", tcname, btype->name);
|
||||
}
|
||||
rz_list_free(types);
|
||||
}
|
||||
char *s = rz_table_tostring(table);
|
||||
rz_cons_print(s);
|
||||
free(s);
|
||||
rz_table_free(table);
|
||||
break;
|
||||
}
|
||||
case RZ_OUTPUT_MODE_JSON: {
|
||||
PJ *pj = pj_new();
|
||||
pj_o(pj);
|
||||
for (tc = RZ_TYPE_TYPECLASS_NUM; tc < RZ_TYPE_TYPECLASS_INVALID; tc++) {
|
||||
pj_ka(pj, rz_type_typeclass_as_string(tc));
|
||||
RzList *types = rz_type_typeclass_get_all(typedb, tc);
|
||||
RzListIter *it;
|
||||
RzBaseType *btype;
|
||||
rz_list_foreach (types, it, btype) {
|
||||
pj_s(pj, btype->name);
|
||||
}
|
||||
rz_list_free(types);
|
||||
pj_end(pj);
|
||||
}
|
||||
pj_end(pj);
|
||||
rz_cons_println(pj_string(pj));
|
||||
pj_free(pj);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
rz_warn_if_reached();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -318,6 +318,7 @@ RZ_API int rz_type_kind(RzTypeDB *typedb, RZ_NONNULL const char *name);
|
|||
RZ_API RZ_BORROW const char *rz_type_typeclass_as_string(RzTypeTypeclass typeclass);
|
||||
RZ_API RzTypeTypeclass rz_type_typeclass_from_string(RZ_NONNULL const char *typeclass);
|
||||
RZ_API RzTypeTypeclass rz_base_type_typeclass(const RzTypeDB *typedb, RZ_NONNULL const RzBaseType *type);
|
||||
RZ_API bool rz_base_type_set_typeclass(RZ_NONNULL RzBaseType *type, RzTypeTypeclass typeclass);
|
||||
RZ_API RzTypeTypeclass rz_type_typeclass(const RzTypeDB *typedb, RZ_NONNULL const RzType *type);
|
||||
RZ_API bool rz_base_type_is_num(const RzTypeDB *typedb, RZ_NONNULL const RzBaseType *type);
|
||||
RZ_API bool rz_type_is_num(const RzTypeDB *typedb, RZ_NONNULL const RzType *type);
|
||||
|
|
|
|||
|
|
@ -151,6 +151,26 @@ RZ_API RzTypeTypeclass rz_base_type_typeclass(const RzTypeDB *typedb, RZ_NONNULL
|
|||
return tclass;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Sets the typeclass of an atomic base type
|
||||
*
|
||||
* The typeclass is stored in the type attributes. Setting it on an atomic type
|
||||
* also affects every typedef that resolves to it, since a typedef without its
|
||||
* own typeclass inherits the one of the type it points to.
|
||||
*
|
||||
* \param type The base type to change
|
||||
* \param typeclass The new typeclass to assign
|
||||
* \return true on success, false if \p typeclass is not a valid typeclass
|
||||
*/
|
||||
RZ_API bool rz_base_type_set_typeclass(RZ_NONNULL RzBaseType *type, RzTypeTypeclass typeclass) {
|
||||
rz_return_val_if_fail(type, false);
|
||||
if (typeclass >= RZ_TYPE_TYPECLASS_INVALID) {
|
||||
return false;
|
||||
}
|
||||
type->attrs = (type->attrs & ~((RzTypeAttribute)RZ_TYPE_ATTRIBUTE_TYPECLASS_MASK)) | typeclass;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Gets the type class
|
||||
*
|
||||
|
|
|
|||
53
test/db/cmd/cmd_typeclass
Normal file
53
test/db/cmd/cmd_typeclass
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
NAME=tk typeclass commands
|
||||
FILE==
|
||||
CMDS=<<EOF
|
||||
tkl
|
||||
td "typedef char zz_num;"
|
||||
td "typedef char zz_int;"
|
||||
td "typedef char zz_flt;"
|
||||
td "typedef char zz_addr;"
|
||||
tks zz_num Num
|
||||
tks zz_int Integral
|
||||
tks zz_flt Floating
|
||||
tks zz_addr Address
|
||||
tk zz_num
|
||||
tk zz_int
|
||||
tk zz_flt
|
||||
tk zz_addr
|
||||
tkll~zz_
|
||||
tklt~zz_[0,1]
|
||||
td "typedef char zz_x;"
|
||||
tks zz_x Floating
|
||||
tks zz_x None
|
||||
tk zz_x
|
||||
tk nonexistent
|
||||
tks nonexistent Num
|
||||
tks zz_x Bogus
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
Num
|
||||
Integral
|
||||
Floating
|
||||
Address
|
||||
Signed Integral
|
||||
Unsigned Integral
|
||||
Num
|
||||
Integral
|
||||
Floating
|
||||
Address
|
||||
zz_num
|
||||
zz_int
|
||||
zz_flt
|
||||
zz_addr
|
||||
Num zz_num
|
||||
Integral zz_int
|
||||
Floating zz_flt
|
||||
Address zz_addr
|
||||
None
|
||||
EOF
|
||||
EXPECT_ERR=<<EOF
|
||||
ERROR: Type "nonexistent" does not exist
|
||||
ERROR: Type "nonexistent" does not exist
|
||||
ERROR: Unknown typeclass "Bogus"
|
||||
EOF
|
||||
RUN
|
||||
Loading…
Reference in a new issue