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:
Anton Kochkov 2026-06-02 13:43:11 +08:00 committed by GitHub
parent f4e477388b
commit f2f4543a26
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 301 additions and 0 deletions

View file

@ -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;
}

View file

@ -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);

View file

@ -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"

View file

@ -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)"

View file

@ -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);

View file

@ -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;
}
}

View file

@ -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);

View file

@ -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
View 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