Rewrite and extend type paths from offset

Tests and extends deriving paths into types at a given offset in the
following ways:
- Parameter for limiting the depth of the path
- Array support, e.g. `.member[5].anothermember`
- Typedef support
- Paths can be taken from arbitrary RzTypes, not only RzBaseType
- Removed the typename in the beginning of paths, which can be added by
  the caller if needed
This commit is contained in:
Florian Märkl 2022-12-09 17:31:50 +01:00
parent ff778e7507
commit 3e75e1f6c7
6 changed files with 704 additions and 137 deletions

View file

@ -107,55 +107,6 @@ struct TListMeta {
ut64 offset;
};
// TODO: Also resolve pointers and arrays
static bool type_paths_collect_by_offset_cb(void *user, ut64 k, const void *v) {
rz_return_val_if_fail(user && v, false);
struct TListMeta *tl = (struct TListMeta *)user;
RzType *t = (RzType *)v;
// Handle only identifiers here
if (t->kind != RZ_TYPE_KIND_IDENTIFIER) {
return true;
}
if (!t->identifier.name) {
return true;
}
// Get the base type
RzBaseType *btype = rz_type_db_get_base_type(tl->typedb, t->identifier.name);
if (!btype) {
return true;
}
if (btype->kind == RZ_BASE_TYPE_KIND_STRUCT || btype->kind == RZ_BASE_TYPE_KIND_UNION) {
RzList *list = rz_type_path_by_offset(tl->typedb, btype, tl->offset);
if (list) {
rz_list_join(tl->l, list);
}
}
return true;
}
/**
* \brief Returns the list of all structured types that are linked and have members matching the offset
*
* \param analysis RzAnalysis instance
* \param offset The offset of the member to match against
*/
RZ_API RZ_OWN RzList /*<RzTypePath *>*/ *rz_analysis_type_links_by_offset(RzAnalysis *analysis, ut64 offset) {
rz_return_val_if_fail(analysis, NULL);
if (offset == UT64_MAX) {
return NULL;
}
RzList *typepaths = rz_list_new();
struct TListMeta tl = {
.typedb = analysis->typedb,
.l = typepaths,
.addr = 0,
.offset = offset
};
ht_up_foreach(analysis->type_links, type_paths_collect_by_offset_cb, &tl);
return typepaths;
}
// TODO: Also resolve pointers and arrays
static bool type_paths_collect_by_address_cb(void *user, ut64 k, const void *v) {
rz_return_val_if_fail(user && v, false);
struct TListMeta *tl = (struct TListMeta *)user;
@ -184,26 +135,60 @@ static bool type_paths_collect_by_address_cb(void *user, ut64 k, const void *v)
return true;
}
if (btype->kind == RZ_BASE_TYPE_KIND_STRUCT || btype->kind == RZ_BASE_TYPE_KIND_UNION) {
RzList *list = rz_type_path_by_offset(tl->typedb, btype, offset);
RzList *list = rz_base_type_path_by_offset(tl->typedb, btype, offset, 1);
if (list) {
rz_list_join(tl->l, list);
RzListIter *iter;
RzTypePath *path;
list->free = NULL;
rz_list_foreach (list, iter, path) {
if (!path->path) {
rz_type_path_free(path);
continue;
}
char *s = rz_str_newf("%s%s", btype->name, path->path);
if (!s) {
rz_type_path_free(path);
continue;
}
free(path->path);
path->path = s;
RzTypePathTuple *tpl = RZ_NEW(RzTypePathTuple);
if (!tpl) {
rz_type_path_free(path);
continue;
}
tpl->path = path;
tpl->root = rz_type_identifier_of_base_type(tl->typedb, btype, false);
rz_list_append(tl->l, tpl);
}
rz_list_free(list);
}
}
return true;
}
static void type_path_tuple_free(void *p) {
if (!p) {
return;
}
RzTypePathTuple *tuple = p;
rz_type_path_free(tuple->path);
rz_type_free(tuple->root);
free(tuple);
}
/**
* \brief Returns the list of all type paths that are linked to some address and have suitable offset
*
* \param analysis RzAnalysis instance
* \param addr The address to check against possible matches
*/
RZ_API RZ_OWN RzList /*<RzTypePath *>*/ *rz_analysis_type_paths_by_address(RzAnalysis *analysis, ut64 addr) {
RZ_API RZ_OWN RzList /*<RzTypePathTuple *>*/ *rz_analysis_type_paths_by_address(RzAnalysis *analysis, ut64 addr) {
rz_return_val_if_fail(analysis, NULL);
if (addr == UT64_MAX) {
return NULL;
}
RzList *typepaths = rz_list_new();
RzList *typepaths = rz_list_newf(type_path_tuple_free);
struct TListMeta tl = {
.typedb = analysis->typedb,
.l = typepaths,

View file

@ -647,21 +647,22 @@ beach:
return;
}
static void set_offset_hint(RzCore *core, RzAnalysisOp *op, RZ_BORROW RzTypePath *tpath, ut64 laddr, ut64 at, int offimm) {
static void set_offset_hint(RzCore *core, RzAnalysisOp *op, RZ_BORROW RzTypePathTuple *tpath, ut64 laddr, ut64 at, int offimm) {
rz_return_if_fail(core && op && tpath);
if (tpath->typ->kind != RZ_TYPE_KIND_IDENTIFIER) {
if (tpath->root->kind != RZ_TYPE_KIND_IDENTIFIER) {
return;
}
char *cmt = (offimm == 0) ? strdup(tpath->path) : rz_type_as_string(core->analysis->typedb, tpath->typ);
char *cmt = (offimm == 0) ? strdup(tpath->path->path) : rz_type_as_string(core->analysis->typedb, tpath->root);
if (offimm > 0) {
// Set only the type path as the analysis hint
// only and only if the types are the exact match between
// possible member offset and the type linked to the laddr
RzList *paths = rz_analysis_type_paths_by_address(core->analysis, laddr + offimm);
if (paths && rz_list_length(paths)) {
RzTypePath *link = rz_list_get_top(paths);
rz_analysis_hint_set_offset(core->analysis, at, link->path);
RzTypePathTuple *link = rz_list_get_top(paths);
rz_analysis_hint_set_offset(core->analysis, at, link->path->path);
}
rz_list_free(paths);
} else if (cmt && rz_analysis_op_ismemref(op->type)) {
rz_meta_set_string(core->analysis, RZ_META_TYPE_VARTYPE, at, cmt);
}
@ -685,33 +686,37 @@ static void resolve_type_links(RzCore *core, ut64 at, struct TLAnalysisContext *
RzList *vlinks = rz_analysis_type_paths_by_address(core->analysis, ctx->src_addr + ctx->src_imm);
// TODO: Handle register based arg for struct offset propgation
if (vlinks && rz_list_length(vlinks) && ctx->var && ctx->var->kind != 'r') {
RzTypePath *vlink = rz_list_get_top(vlinks);
RzTypePathTuple *vlink = rz_list_get_top(vlinks);
// FIXME: For now we only propagate simple type identifiers,
// no pointers or arrays
if (vlink->typ->kind == RZ_TYPE_KIND_IDENTIFIER) {
if (!vlink->typ->identifier.name) {
if (vlink->root->kind == RZ_TYPE_KIND_IDENTIFIER) {
if (!vlink->root->identifier.name) {
rz_warn_if_reached();
return;
}
RzBaseType *varbtype = rz_type_db_get_base_type(core->analysis->typedb, vlink->typ->identifier.name);
RzBaseType *varbtype = rz_type_db_get_base_type(core->analysis->typedb, vlink->root->identifier.name);
if (varbtype) {
// if a var addr matches with struct , change it's type and name
// var int local_e0h --> var struct foo
// if (strcmp(var->name, vlink) && !*resolved) {
if (!*resolved) {
*resolved = true;
rz_analysis_var_set_type(ctx->var, vlink->typ, true);
rz_analysis_var_rename(ctx->var, vlink->typ->identifier.name, false);
rz_analysis_var_set_type(ctx->var, vlink->root, true);
rz_analysis_var_rename(ctx->var, vlink->root->identifier.name, false);
vlink->root = NULL;
}
}
}
} else if (slinks && rz_list_length(slinks)) {
RzTypePath *slink = rz_list_get_top(slinks);
RzTypePathTuple *slink = rz_list_get_top(slinks);
set_offset_hint(core, ctx->aop, slink, ctx->src_addr, at - ret, ctx->src_imm);
} else if (dlinks && rz_list_length(dlinks)) {
RzTypePath *dlink = rz_list_get_top(dlinks);
RzTypePathTuple *dlink = rz_list_get_top(dlinks);
set_offset_hint(core, ctx->aop, dlink, ctx->dst_addr, at - ret, ctx->dst_imm);
}
rz_list_free(slinks);
rz_list_free(dlinks);
rz_list_free(vlinks);
}
RZ_API void rz_core_link_stroff(RzCore *core, RzAnalysisFunction *fcn) {

View file

@ -1629,8 +1629,7 @@ RZ_API bool rz_analysis_type_set_link(RzAnalysis *analysis, RZ_OWN RzType *type,
RZ_API bool rz_analysis_type_unlink(RzAnalysis *analysis, ut64 addr);
RZ_API bool rz_analysis_type_unlink_all(RzAnalysis *analysis);
RZ_API RZ_OWN RzList /*<RzType *>*/ *rz_analysis_type_links(RzAnalysis *analysis);
RZ_API RZ_OWN RzList /*<RzTypePath *>*/ *rz_analysis_type_links_by_offset(RzAnalysis *analysis, ut64 offset);
RZ_API RZ_OWN RzList /*<RzTypePath *>*/ *rz_analysis_type_paths_by_address(RzAnalysis *analysis, ut64 addr);
RZ_API RZ_OWN RzList /*<RzTypePathTuple *>*/ *rz_analysis_type_paths_by_address(RzAnalysis *analysis, ut64 addr);
/* project */
RZ_API bool rz_analysis_xrefs_init(RzAnalysis *analysis);

View file

@ -172,10 +172,16 @@ struct rz_type_t {
};
typedef struct rz_type_path_t {
RzType *typ;
RzType *typ; ///< type at the leaf
char *path;
} RzTypePath;
/// A path and the type that the path came from
typedef struct rz_type_path_tuple_t {
RzTypePath *path;
RzType *root;
} RzTypePathTuple;
/**
* \brief Type Conditions
*/
@ -304,7 +310,8 @@ RZ_API RZ_OWN RzBaseType *rz_type_typeclass_get_default_sized(const RzTypeDB *ty
RZ_API RZ_OWN RzTypePath *rz_type_path_new(RZ_BORROW RZ_NONNULL RzType *type, RZ_OWN RZ_NONNULL char *path);
RZ_API void rz_type_path_free(RZ_NULLABLE RzTypePath *tpath);
RZ_API st64 rz_type_offset_by_path(const RzTypeDB *typedb, RZ_NONNULL const char *path);
RZ_API RZ_OWN RzList /*<RzTypePath *>*/ *rz_type_path_by_offset(const RzTypeDB *typedb, RzBaseType *btype, ut64 offset);
RZ_API RZ_OWN RzList /*<RzTypePath *>*/ *rz_base_type_path_by_offset(const RzTypeDB *typedb, const RzBaseType *btype, ut64 offset, unsigned int max_depth);
RZ_API RZ_OWN RzList /*<RzTypePath *>*/ *rz_type_path_by_offset(const RzTypeDB *typedb, const RzType *type, ut64 offset, unsigned int max_depth);
RZ_API RZ_OWN RzList /*<RzTypePath *>*/ *rz_type_db_get_by_offset(const RzTypeDB *typedb, ut64 offset);
RZ_API ut64 rz_type_db_struct_member_packed_offset(RZ_NONNULL const RzTypeDB *typedb, RZ_NONNULL const char *name, RZ_NONNULL const char *member);
RZ_API ut64 rz_type_db_struct_member_offset(RZ_NONNULL const RzTypeDB *typedb, RZ_NONNULL const char *name, RZ_NONNULL const char *member);

View file

@ -122,91 +122,140 @@ RZ_API st64 rz_type_offset_by_path(const RzTypeDB *typedb, RZ_NONNULL const char
return path_walker(typedb, path);
}
// TODO: Handle arrays
static bool structured_member_walker(const RzTypeDB *typedb, RzList /*<RzTypePath *>*/ *list, RzType *parent, RzType *type, char *path, ut64 offset) {
rz_return_val_if_fail(list && type, false);
if (type->kind != RZ_TYPE_KIND_IDENTIFIER) {
return false;
static void collect_type_paths(const RzTypeDB *typedb, RzList /*<RzTypePath *>*/ *list, const RzType *type, char *prefix, ut64 offset, unsigned int depth);
static void collect_base_type_paths(const RzTypeDB *typedb, RzList /*<RzTypePath *>*/ *list, const RzBaseType *btype, char *prefix, ut64 offset, unsigned int depth) {
rz_return_if_fail(typedb && list && btype && prefix);
if (!depth) {
return;
}
rz_return_val_if_fail(type->identifier.name, false);
bool result = true;
if (type->identifier.kind == RZ_TYPE_IDENTIFIER_KIND_STRUCT) {
// Get the base type
RzBaseType *btype = rz_type_db_get_base_type(typedb, type->identifier.name);
if (!btype) {
return false;
}
switch (btype->kind) {
case RZ_BASE_TYPE_KIND_STRUCT: {
RzTypeStructMember *memb;
ut64 memb_offset = 0;
rz_vector_foreach(&btype->struct_data.members, memb) {
if (memb_offset == offset) {
RzTypePath *tpath = rz_type_path_new(parent, rz_str_newf("%s.%s.%s", path, btype->name, memb->name));
RzTypePath *tpath = rz_type_path_new(memb->type, rz_str_newf("%s.%s", prefix, memb->name));
if (tpath) {
rz_list_append(list, tpath);
}
}
char *newpath = rz_str_newf("%s.%s", path, memb->name);
result &= structured_member_walker(typedb, list, parent, memb->type, newpath, memb_offset + offset);
memb_offset += rz_type_db_get_bitsize(typedb, memb->type) / 8;
free(newpath);
}
} else if (type->identifier.kind == RZ_TYPE_IDENTIFIER_KIND_UNION) {
// Get the base type
RzBaseType *btype = rz_type_db_get_base_type(typedb, type->identifier.name);
if (!btype) {
return false;
ut64 bytesz = rz_type_db_get_bitsize(typedb, memb->type) / 8;
if (memb_offset + bytesz > offset) {
char *newpath = rz_str_newf("%s.%s", prefix, memb->name);
collect_type_paths(typedb, list, memb->type, newpath, offset - memb_offset, depth - 1);
free(newpath);
break;
}
memb_offset += bytesz;
}
break;
}
case RZ_BASE_TYPE_KIND_UNION: {
RzTypeUnionMember *memb;
rz_vector_foreach(&btype->union_data.members, memb) {
char *newpath = rz_str_newf("%s.%s", path, memb->name);
result &= structured_member_walker(typedb, list, parent, memb->type, path, offset);
char *newpath = rz_str_newf("%s.%s", prefix, memb->name);
if (offset == 0) {
RzTypePath *tpath = rz_type_path_new(memb->type, newpath);
if (tpath) {
rz_list_append(list, tpath);
}
}
collect_type_paths(typedb, list, memb->type, newpath, offset, depth - 1);
if (offset != 0) {
free(newpath);
}
}
break;
}
case RZ_BASE_TYPE_KIND_TYPEDEF: {
RzType *ttype = rz_type_db_base_type_unwrap_typedef(typedb, btype);
if (!ttype) {
return;
}
collect_type_paths(typedb, list, ttype, prefix, offset, depth); // depth not decreased because this is not a visible step in the path
break;
}
default:
break;
}
}
static void collect_type_paths(const RzTypeDB *typedb, RzList /*<RzTypePath *>*/ *list, const RzType *type, char *prefix, ut64 offset, unsigned int depth) {
rz_return_if_fail(typedb && list && type && prefix);
if (!depth) {
return;
}
switch (type->kind) {
case RZ_TYPE_KIND_IDENTIFIER: {
rz_return_if_fail(type->identifier.name);
RzBaseType *btype = rz_type_db_get_base_type(typedb, type->identifier.name);
if (!btype) {
return;
}
collect_base_type_paths(typedb, list, btype, prefix, offset, depth); // depth not decreased because this is not a visible step in the path
break;
}
case RZ_TYPE_KIND_ARRAY: {
rz_return_if_fail(type->array.type);
ut64 stride = rz_type_db_get_bitsize(typedb, type->array.type) / 8;
if (!stride) {
break;
}
ut64 idx = offset / stride;
if (idx >= type->array.count) {
break;
}
ut64 idx_offset = offset - idx * stride;
char *newpath = rz_str_newf("%s[%" PFMT64u "]", prefix, idx);
if (idx_offset == 0) {
RzTypePath *tpath = rz_type_path_new(type->array.type, newpath);
if (tpath) {
rz_list_append(list, tpath);
}
}
collect_type_paths(typedb, list, type->array.type, newpath, idx_offset, depth - 1);
if (idx_offset != 0) {
free(newpath);
}
break;
}
default:
break;
}
return result;
}
/**
* \brief Returns the list of all type paths matching the offset
* \brief Returns the list of all paths into the base type matching the offset
*
* \param typedb Types Database instance
* \param btype The base type
* \param offset The offset of the path to match against
* \param btype The root type from which paths are searched
* \param offset The offset into \p btype to match against
* \param max_depth Maximum number of components a path may have
*/
RZ_API RZ_OWN RzList /*<RzTypePath *>*/ *rz_type_path_by_offset(const RzTypeDB *typedb, RzBaseType *btype, ut64 offset) {
RZ_API RZ_OWN RzList /*<RzTypePath *>*/ *rz_base_type_path_by_offset(const RzTypeDB *typedb, const RzBaseType *btype, ut64 offset, unsigned int max_depth) {
rz_return_val_if_fail(typedb && btype && btype->name, NULL);
RzList *list = rz_list_newf((RzListFree)rz_type_path_free);
if (btype->kind == RZ_BASE_TYPE_KIND_STRUCT) {
RzType *t = rz_type_identifier_of_base_type(typedb, btype, false);
RzTypeStructMember *memb;
ut64 memb_offset = 0;
rz_vector_foreach(&btype->struct_data.members, memb) {
if (memb_offset == offset) {
RzTypePath *tpath = rz_type_path_new(t, rz_str_newf("%s.%s", btype->name, memb->name));
if (tpath) {
rz_list_append(list, tpath);
}
}
// We go into the nested structures/unions if they are members of the structure
char *path = rz_str_newf("%s.%s", btype->name, memb->name);
structured_member_walker(typedb, list, t, memb->type, path, memb_offset + offset);
memb_offset += rz_type_db_get_bitsize(typedb, memb->type) / 8;
free(path);
}
} else if (btype->kind == RZ_BASE_TYPE_KIND_UNION) {
// This function makes sense only for structures since union
// members have exact same offset
// But if the union has compound members, e.g. structures, their
// internal offsets can be different
RzType *t = rz_type_identifier_of_base_type(typedb, btype, false);
RzTypeUnionMember *memb;
rz_vector_foreach(&btype->union_data.members, memb) {
char *path = rz_str_newf("%s.%s", btype->name, memb->name);
structured_member_walker(typedb, list, t, memb->type, path, offset);
free(path);
}
} else {
rz_warn_if_reached();
if (!list) {
return NULL;
}
collect_base_type_paths(typedb, list, btype, "", offset, max_depth);
return list;
}
/**
* \brief Returns the list of all paths into the type matching the offset
*
* \param type The root type from which paths are searched
* \param offset The offset into \p btype to match against
* \param max_depth Maximum number of components a path may have
*/
RZ_API RZ_OWN RzList /*<RzTypePath *>*/ *rz_type_path_by_offset(const RzTypeDB *typedb, const RzType *type, ut64 offset, unsigned int max_depth) {
rz_return_val_if_fail(typedb && type, NULL);
RzList *list = rz_list_newf((RzListFree)rz_type_path_free);
if (!list) {
return NULL;
}
collect_type_paths(typedb, list, type, "", offset, max_depth);
return list;
}
@ -224,10 +273,7 @@ RZ_API RZ_OWN RzList /*<RzTypePath *>*/ *rz_type_db_get_by_offset(const RzTypeDB
RzBaseType *t;
rz_list_foreach (types, iter, t) {
if (t->kind == RZ_BASE_TYPE_KIND_STRUCT || t->kind == RZ_BASE_TYPE_KIND_UNION) {
RzList *list = rz_type_path_by_offset(typedb, t, offset);
if (list) {
rz_list_join(result, list);
}
collect_base_type_paths(typedb, result, t, t->name, offset, 1);
}
}
rz_list_free(types);

View file

@ -1077,6 +1077,527 @@ bool test_typedef_loop(void) {
str = rz_base_type_as_format(typedb, btype);
mu_assert_streq_free(str, "", "format");
RzList *paths = rz_type_path_by_offset(typedb, ttype, 0, INT_MAX);
mu_assert_notnull(paths, "paths");
mu_assert_eq(rz_list_length(paths), 0, "paths");
rz_list_free(paths);
rz_type_free(ttype);
rz_type_db_free(typedb);
mu_end;
}
bool test_path_by_offset_struct(void) {
RzTypeDB *typedb = rz_type_db_new();
const char *types_dir = TEST_BUILD_TYPES_DIR;
rz_type_db_init(typedb, types_dir, "x86", 64, "linux");
// -- simple
char *error_msg = NULL;
RzType *ttype = rz_type_parse_string_single(typedb->parser, "struct Hello { int a; uint32_t b; };", &error_msg);
mu_assert_notnull(ttype, "type parse successful");
mu_assert_eq(ttype->kind, RZ_TYPE_KIND_IDENTIFIER, "parsed type");
mu_assert_streq(ttype->identifier.name, "Hello", "parsed type");
RzList *paths = rz_type_path_by_offset(typedb, ttype, 0, 5);
mu_assert_notnull(paths, "paths");
mu_assert_eq(rz_list_length(paths), 1, "paths");
RzTypePath *path = rz_list_get_n(paths, 0);
mu_assert_streq(path->path, ".a", "paths");
mu_assert_eq(path->typ->kind, RZ_TYPE_KIND_IDENTIFIER, "paths");
mu_assert_streq(path->typ->identifier.name, "int", "paths");
rz_list_free(paths);
paths = rz_type_path_by_offset(typedb, ttype, 2, 5);
mu_assert_notnull(paths, "paths");
mu_assert_eq(rz_list_length(paths), 0, "paths");
rz_list_free(paths);
paths = rz_type_path_by_offset(typedb, ttype, 4, 5);
mu_assert_notnull(paths, "paths");
mu_assert_eq(rz_list_length(paths), 1, "paths");
path = rz_list_get_n(paths, 0);
mu_assert_streq(path->path, ".b", "paths");
mu_assert_eq(path->typ->kind, RZ_TYPE_KIND_IDENTIFIER, "paths");
mu_assert_streq(path->typ->identifier.name, "uint32_t", "paths");
rz_list_free(paths);
rz_type_free(ttype);
// -- recursive
ttype = rz_type_parse_string_single(typedb->parser, "struct World { uint64_t ulu; Hello mulu; int32_t urshak; };", &error_msg);
mu_assert_notnull(ttype, "type parse successful");
mu_assert_eq(ttype->kind, RZ_TYPE_KIND_IDENTIFIER, "parsed type");
mu_assert_streq(ttype->identifier.name, "World", "parsed type");
paths = rz_type_path_by_offset(typedb, ttype, 0, 5);
mu_assert_notnull(paths, "paths");
mu_assert_eq(rz_list_length(paths), 1, "paths");
path = rz_list_get_n(paths, 0);
mu_assert_streq(path->path, ".ulu", "paths");
mu_assert_eq(path->typ->kind, RZ_TYPE_KIND_IDENTIFIER, "paths");
mu_assert_streq(path->typ->identifier.name, "uint64_t", "paths");
rz_list_free(paths);
paths = rz_type_path_by_offset(typedb, ttype, 8, 5);
mu_assert_notnull(paths, "paths");
mu_assert_eq(rz_list_length(paths), 2, "paths");
path = rz_list_get_n(paths, 0);
mu_assert_streq(path->path, ".mulu", "paths");
mu_assert_eq(path->typ->kind, RZ_TYPE_KIND_IDENTIFIER, "paths");
mu_assert_streq(path->typ->identifier.name, "Hello", "paths");
path = rz_list_get_n(paths, 1);
mu_assert_streq(path->path, ".mulu.a", "paths");
mu_assert_eq(path->typ->kind, RZ_TYPE_KIND_IDENTIFIER, "paths");
mu_assert_streq(path->typ->identifier.name, "int", "paths");
rz_list_free(paths);
paths = rz_type_path_by_offset(typedb, ttype, 10, 5);
mu_assert_notnull(paths, "paths");
mu_assert_eq(rz_list_length(paths), 0, "paths");
rz_list_free(paths);
paths = rz_type_path_by_offset(typedb, ttype, 12, 5);
mu_assert_notnull(paths, "paths");
mu_assert_eq(rz_list_length(paths), 1, "paths");
path = rz_list_get_n(paths, 0);
mu_assert_streq(path->path, ".mulu.b", "paths");
mu_assert_eq(path->typ->kind, RZ_TYPE_KIND_IDENTIFIER, "paths");
mu_assert_streq(path->typ->identifier.name, "uint32_t", "paths");
rz_list_free(paths);
paths = rz_type_path_by_offset(typedb, ttype, 8, 2);
mu_assert_notnull(paths, "paths");
mu_assert_eq(rz_list_length(paths), 2, "paths");
path = rz_list_get_n(paths, 0);
mu_assert_streq(path->path, ".mulu", "paths");
mu_assert_eq(path->typ->kind, RZ_TYPE_KIND_IDENTIFIER, "paths");
mu_assert_streq(path->typ->identifier.name, "Hello", "paths");
path = rz_list_get_n(paths, 1);
mu_assert_streq(path->path, ".mulu.a", "paths");
mu_assert_eq(path->typ->kind, RZ_TYPE_KIND_IDENTIFIER, "paths");
mu_assert_streq(path->typ->identifier.name, "int", "paths");
rz_list_free(paths);
paths = rz_type_path_by_offset(typedb, ttype, 8, 1);
mu_assert_notnull(paths, "paths");
mu_assert_eq(rz_list_length(paths), 1, "paths");
path = rz_list_get_n(paths, 0);
mu_assert_streq(path->path, ".mulu", "paths");
mu_assert_eq(path->typ->kind, RZ_TYPE_KIND_IDENTIFIER, "paths");
mu_assert_streq(path->typ->identifier.name, "Hello", "paths");
rz_list_free(paths);
paths = rz_type_path_by_offset(typedb, ttype, 8, 0);
mu_assert_notnull(paths, "paths");
mu_assert_eq(rz_list_length(paths), 0, "paths");
rz_list_free(paths);
rz_type_free(ttype);
// -- base type
RzBaseType *btype = rz_type_db_get_base_type(typedb, "World");
mu_assert_notnull(btype, "get base type");
paths = rz_base_type_path_by_offset(typedb, btype, 8, 2);
mu_assert_notnull(paths, "paths");
mu_assert_eq(rz_list_length(paths), 2, "paths");
path = rz_list_get_n(paths, 0);
mu_assert_streq(path->path, ".mulu", "paths");
mu_assert_eq(path->typ->kind, RZ_TYPE_KIND_IDENTIFIER, "paths");
mu_assert_streq(path->typ->identifier.name, "Hello", "paths");
path = rz_list_get_n(paths, 1);
mu_assert_streq(path->path, ".mulu.a", "paths");
mu_assert_eq(path->typ->kind, RZ_TYPE_KIND_IDENTIFIER, "paths");
mu_assert_streq(path->typ->identifier.name, "int", "paths");
rz_list_free(paths);
paths = rz_base_type_path_by_offset(typedb, btype, 8, 1);
mu_assert_notnull(paths, "paths");
mu_assert_eq(rz_list_length(paths), 1, "paths");
path = rz_list_get_n(paths, 0);
mu_assert_streq(path->path, ".mulu", "paths");
mu_assert_eq(path->typ->kind, RZ_TYPE_KIND_IDENTIFIER, "paths");
mu_assert_streq(path->typ->identifier.name, "Hello", "paths");
rz_list_free(paths);
paths = rz_base_type_path_by_offset(typedb, btype, 8, 0);
mu_assert_notnull(paths, "paths");
mu_assert_eq(rz_list_length(paths), 0, "paths");
rz_list_free(paths);
rz_type_db_free(typedb);
mu_end;
}
bool test_path_by_offset_union(void) {
RzTypeDB *typedb = rz_type_db_new();
const char *types_dir = TEST_BUILD_TYPES_DIR;
rz_type_db_init(typedb, types_dir, "x86", 64, "linux");
// -- simple
char *error_msg = NULL;
RzType *ttype = rz_type_parse_string_single(typedb->parser, "union Card { int fish; uint32_t senf; };", &error_msg);
mu_assert_notnull(ttype, "type parse successful");
mu_assert_eq(ttype->kind, RZ_TYPE_KIND_IDENTIFIER, "parsed type");
mu_assert_streq(ttype->identifier.name, "Card", "parsed type");
RzList *paths = rz_type_path_by_offset(typedb, ttype, 0, 5);
mu_assert_notnull(paths, "paths");
mu_assert_eq(rz_list_length(paths), 2, "paths");
RzTypePath *path = rz_list_get_n(paths, 0);
mu_assert_streq(path->path, ".fish", "paths");
mu_assert_eq(path->typ->kind, RZ_TYPE_KIND_IDENTIFIER, "paths");
mu_assert_streq(path->typ->identifier.name, "int", "paths");
path = rz_list_get_n(paths, 1);
mu_assert_streq(path->path, ".senf", "paths");
mu_assert_eq(path->typ->kind, RZ_TYPE_KIND_IDENTIFIER, "paths");
mu_assert_streq(path->typ->identifier.name, "uint32_t", "paths");
rz_list_free(paths);
paths = rz_type_path_by_offset(typedb, ttype, 2, 5);
mu_assert_notnull(paths, "paths");
mu_assert_eq(rz_list_length(paths), 0, "paths");
rz_list_free(paths);
paths = rz_type_path_by_offset(typedb, ttype, 4, 5);
mu_assert_notnull(paths, "paths");
mu_assert_eq(rz_list_length(paths), 0, "paths");
rz_list_free(paths);
rz_type_free(ttype);
// -- recursive
ttype = rz_type_parse_string_single(typedb->parser, "struct Hello { int a; uint32_t b; };", &error_msg);
mu_assert_notnull(ttype, "type parse successful");
mu_assert_eq(ttype->kind, RZ_TYPE_KIND_IDENTIFIER, "parsed type");
mu_assert_streq(ttype->identifier.name, "Hello", "parsed type");
rz_type_free(ttype);
ttype = rz_type_parse_string_single(typedb->parser, "union World { uint64_t ulu; Hello mulu; int32_t urshak; };", &error_msg);
mu_assert_notnull(ttype, "type parse successful");
mu_assert_eq(ttype->kind, RZ_TYPE_KIND_IDENTIFIER, "parsed type");
mu_assert_streq(ttype->identifier.name, "World", "parsed type");
paths = rz_type_path_by_offset(typedb, ttype, 0, 5);
mu_assert_notnull(paths, "paths");
mu_assert_eq(rz_list_length(paths), 4, "paths");
path = rz_list_get_n(paths, 0);
mu_assert_streq(path->path, ".ulu", "paths");
mu_assert_eq(path->typ->kind, RZ_TYPE_KIND_IDENTIFIER, "paths");
mu_assert_streq(path->typ->identifier.name, "uint64_t", "paths");
path = rz_list_get_n(paths, 1);
mu_assert_streq(path->path, ".mulu", "paths");
mu_assert_eq(path->typ->kind, RZ_TYPE_KIND_IDENTIFIER, "paths");
mu_assert_streq(path->typ->identifier.name, "Hello", "paths");
path = rz_list_get_n(paths, 2);
mu_assert_streq(path->path, ".mulu.a", "paths");
mu_assert_eq(path->typ->kind, RZ_TYPE_KIND_IDENTIFIER, "paths");
mu_assert_streq(path->typ->identifier.name, "int", "paths");
path = rz_list_get_n(paths, 3);
mu_assert_streq(path->path, ".urshak", "paths");
mu_assert_eq(path->typ->kind, RZ_TYPE_KIND_IDENTIFIER, "paths");
mu_assert_streq(path->typ->identifier.name, "int32_t", "paths");
rz_list_free(paths);
paths = rz_type_path_by_offset(typedb, ttype, 8, 5);
mu_assert_notnull(paths, "paths");
mu_assert_eq(rz_list_length(paths), 0, "paths");
rz_list_free(paths);
paths = rz_type_path_by_offset(typedb, ttype, 4, 5);
mu_assert_notnull(paths, "paths");
mu_assert_eq(rz_list_length(paths), 1, "paths");
path = rz_list_get_n(paths, 0);
mu_assert_streq(path->path, ".mulu.b", "paths");
mu_assert_eq(path->typ->kind, RZ_TYPE_KIND_IDENTIFIER, "paths");
mu_assert_streq(path->typ->identifier.name, "uint32_t", "paths");
rz_list_free(paths);
paths = rz_type_path_by_offset(typedb, ttype, 0, 2);
mu_assert_notnull(paths, "paths");
mu_assert_eq(rz_list_length(paths), 4, "paths");
path = rz_list_get_n(paths, 0);
mu_assert_streq(path->path, ".ulu", "paths");
mu_assert_eq(path->typ->kind, RZ_TYPE_KIND_IDENTIFIER, "paths");
mu_assert_streq(path->typ->identifier.name, "uint64_t", "paths");
path = rz_list_get_n(paths, 1);
mu_assert_streq(path->path, ".mulu", "paths");
mu_assert_eq(path->typ->kind, RZ_TYPE_KIND_IDENTIFIER, "paths");
mu_assert_streq(path->typ->identifier.name, "Hello", "paths");
path = rz_list_get_n(paths, 2);
mu_assert_streq(path->path, ".mulu.a", "paths");
mu_assert_eq(path->typ->kind, RZ_TYPE_KIND_IDENTIFIER, "paths");
mu_assert_streq(path->typ->identifier.name, "int", "paths");
path = rz_list_get_n(paths, 3);
mu_assert_streq(path->path, ".urshak", "paths");
mu_assert_eq(path->typ->kind, RZ_TYPE_KIND_IDENTIFIER, "paths");
mu_assert_streq(path->typ->identifier.name, "int32_t", "paths");
rz_list_free(paths);
paths = rz_type_path_by_offset(typedb, ttype, 0, 1);
mu_assert_notnull(paths, "paths");
mu_assert_eq(rz_list_length(paths), 3, "paths");
path = rz_list_get_n(paths, 0);
mu_assert_streq(path->path, ".ulu", "paths");
mu_assert_eq(path->typ->kind, RZ_TYPE_KIND_IDENTIFIER, "paths");
mu_assert_streq(path->typ->identifier.name, "uint64_t", "paths");
path = rz_list_get_n(paths, 1);
mu_assert_streq(path->path, ".mulu", "paths");
mu_assert_eq(path->typ->kind, RZ_TYPE_KIND_IDENTIFIER, "paths");
mu_assert_streq(path->typ->identifier.name, "Hello", "paths");
path = rz_list_get_n(paths, 2);
mu_assert_streq(path->path, ".urshak", "paths");
mu_assert_eq(path->typ->kind, RZ_TYPE_KIND_IDENTIFIER, "paths");
mu_assert_streq(path->typ->identifier.name, "int32_t", "paths");
rz_list_free(paths);
paths = rz_type_path_by_offset(typedb, ttype, 0, 0);
mu_assert_notnull(paths, "paths");
mu_assert_eq(rz_list_length(paths), 0, "paths");
rz_list_free(paths);
rz_type_free(ttype);
// -- base type
RzBaseType *btype = rz_type_db_get_base_type(typedb, "World");
mu_assert_notnull(btype, "get base type");
paths = rz_base_type_path_by_offset(typedb, btype, 0, 2);
mu_assert_notnull(paths, "paths");
mu_assert_eq(rz_list_length(paths), 4, "paths");
path = rz_list_get_n(paths, 0);
mu_assert_streq(path->path, ".ulu", "paths");
mu_assert_eq(path->typ->kind, RZ_TYPE_KIND_IDENTIFIER, "paths");
mu_assert_streq(path->typ->identifier.name, "uint64_t", "paths");
path = rz_list_get_n(paths, 1);
mu_assert_streq(path->path, ".mulu", "paths");
mu_assert_eq(path->typ->kind, RZ_TYPE_KIND_IDENTIFIER, "paths");
mu_assert_streq(path->typ->identifier.name, "Hello", "paths");
path = rz_list_get_n(paths, 2);
mu_assert_streq(path->path, ".mulu.a", "paths");
mu_assert_eq(path->typ->kind, RZ_TYPE_KIND_IDENTIFIER, "paths");
mu_assert_streq(path->typ->identifier.name, "int", "paths");
path = rz_list_get_n(paths, 3);
mu_assert_streq(path->path, ".urshak", "paths");
mu_assert_eq(path->typ->kind, RZ_TYPE_KIND_IDENTIFIER, "paths");
mu_assert_streq(path->typ->identifier.name, "int32_t", "paths");
rz_list_free(paths);
paths = rz_base_type_path_by_offset(typedb, btype, 0, 1);
mu_assert_notnull(paths, "paths");
mu_assert_eq(rz_list_length(paths), 3, "paths");
path = rz_list_get_n(paths, 0);
mu_assert_streq(path->path, ".ulu", "paths");
mu_assert_eq(path->typ->kind, RZ_TYPE_KIND_IDENTIFIER, "paths");
mu_assert_streq(path->typ->identifier.name, "uint64_t", "paths");
path = rz_list_get_n(paths, 1);
mu_assert_streq(path->path, ".mulu", "paths");
mu_assert_eq(path->typ->kind, RZ_TYPE_KIND_IDENTIFIER, "paths");
mu_assert_streq(path->typ->identifier.name, "Hello", "paths");
path = rz_list_get_n(paths, 2);
mu_assert_streq(path->path, ".urshak", "paths");
mu_assert_eq(path->typ->kind, RZ_TYPE_KIND_IDENTIFIER, "paths");
mu_assert_streq(path->typ->identifier.name, "int32_t", "paths");
rz_list_free(paths);
paths = rz_base_type_path_by_offset(typedb, btype, 0, 0);
mu_assert_notnull(paths, "paths");
mu_assert_eq(rz_list_length(paths), 0, "paths");
rz_list_free(paths);
rz_type_db_free(typedb);
mu_end;
}
bool test_path_by_offset_array(void) {
RzTypeDB *typedb = rz_type_db_new();
const char *types_dir = TEST_BUILD_TYPES_DIR;
rz_type_db_init(typedb, types_dir, "x86", 64, "linux");
// -- simple
char *error_msg = NULL;
RzType *ttype = rz_type_parse_string_single(typedb->parser, "int [5]", &error_msg);
mu_assert_notnull(ttype, "type parse successful");
mu_assert_eq(ttype->kind, RZ_TYPE_KIND_ARRAY, "parsed type");
RzList *paths = rz_type_path_by_offset(typedb, ttype, 0, 5);
mu_assert_notnull(paths, "paths");
mu_assert_eq(rz_list_length(paths), 1, "paths");
RzTypePath *path = rz_list_get_n(paths, 0);
mu_assert_streq(path->path, "[0]", "paths");
mu_assert_eq(path->typ->kind, RZ_TYPE_KIND_IDENTIFIER, "paths");
mu_assert_streq(path->typ->identifier.name, "int", "paths");
rz_list_free(paths);
paths = rz_type_path_by_offset(typedb, ttype, 1, 5);
mu_assert_notnull(paths, "paths");
mu_assert_eq(rz_list_length(paths), 0, "paths");
rz_list_free(paths);
paths = rz_type_path_by_offset(typedb, ttype, 4, 5);
mu_assert_notnull(paths, "paths");
mu_assert_eq(rz_list_length(paths), 1, "paths");
path = rz_list_get_n(paths, 0);
mu_assert_streq(path->path, "[1]", "paths");
mu_assert_eq(path->typ->kind, RZ_TYPE_KIND_IDENTIFIER, "paths");
mu_assert_streq(path->typ->identifier.name, "int", "paths");
rz_list_free(paths);
paths = rz_type_path_by_offset(typedb, ttype, 16, 5);
mu_assert_notnull(paths, "paths");
mu_assert_eq(rz_list_length(paths), 1, "paths");
path = rz_list_get_n(paths, 0);
mu_assert_streq(path->path, "[4]", "paths");
mu_assert_eq(path->typ->kind, RZ_TYPE_KIND_IDENTIFIER, "paths");
mu_assert_streq(path->typ->identifier.name, "int", "paths");
rz_list_free(paths);
paths = rz_type_path_by_offset(typedb, ttype, 19, 5);
mu_assert_notnull(paths, "paths");
mu_assert_eq(rz_list_length(paths), 0, "paths");
rz_list_free(paths);
paths = rz_type_path_by_offset(typedb, ttype, 20, 5);
mu_assert_notnull(paths, "paths");
mu_assert_eq(rz_list_length(paths), 0, "paths");
rz_list_free(paths);
rz_type_free(ttype);
// -- recursive
ttype = rz_type_parse_string_single(typedb->parser, "struct Hello { int a; uint32_t b; };", &error_msg);
mu_assert_notnull(ttype, "type parse successful");
mu_assert_eq(ttype->kind, RZ_TYPE_KIND_IDENTIFIER, "parsed type");
mu_assert_streq(ttype->identifier.name, "Hello", "parsed type");
rz_type_free(ttype);
ttype = rz_type_parse_string_single(typedb->parser, "Hello [5]", &error_msg);
mu_assert_notnull(ttype, "type parse successful");
mu_assert_eq(ttype->kind, RZ_TYPE_KIND_ARRAY, "parsed type");
paths = rz_type_path_by_offset(typedb, ttype, 0, 5);
mu_assert_notnull(paths, "paths");
mu_assert_eq(rz_list_length(paths), 2, "paths");
path = rz_list_get_n(paths, 0);
mu_assert_streq(path->path, "[0]", "paths");
mu_assert_eq(path->typ->kind, RZ_TYPE_KIND_IDENTIFIER, "paths");
mu_assert_streq(path->typ->identifier.name, "Hello", "paths");
path = rz_list_get_n(paths, 1);
mu_assert_streq(path->path, "[0].a", "paths");
mu_assert_eq(path->typ->kind, RZ_TYPE_KIND_IDENTIFIER, "paths");
mu_assert_streq(path->typ->identifier.name, "int", "paths");
rz_list_free(paths);
paths = rz_type_path_by_offset(typedb, ttype, 12, 5);
mu_assert_notnull(paths, "paths");
mu_assert_eq(rz_list_length(paths), 1, "paths");
path = rz_list_get_n(paths, 0);
mu_assert_streq(path->path, "[1].b", "paths");
mu_assert_eq(path->typ->kind, RZ_TYPE_KIND_IDENTIFIER, "paths");
mu_assert_streq(path->typ->identifier.name, "uint32_t", "paths");
rz_list_free(paths);
paths = rz_type_path_by_offset(typedb, ttype, 0, 2);
mu_assert_notnull(paths, "paths");
mu_assert_eq(rz_list_length(paths), 2, "paths");
path = rz_list_get_n(paths, 0);
mu_assert_streq(path->path, "[0]", "paths");
mu_assert_eq(path->typ->kind, RZ_TYPE_KIND_IDENTIFIER, "paths");
mu_assert_streq(path->typ->identifier.name, "Hello", "paths");
path = rz_list_get_n(paths, 1);
mu_assert_streq(path->path, "[0].a", "paths");
mu_assert_eq(path->typ->kind, RZ_TYPE_KIND_IDENTIFIER, "paths");
mu_assert_streq(path->typ->identifier.name, "int", "paths");
rz_list_free(paths);
paths = rz_type_path_by_offset(typedb, ttype, 0, 1);
mu_assert_notnull(paths, "paths");
mu_assert_eq(rz_list_length(paths), 1, "paths");
path = rz_list_get_n(paths, 0);
mu_assert_streq(path->path, "[0]", "paths");
mu_assert_eq(path->typ->kind, RZ_TYPE_KIND_IDENTIFIER, "paths");
mu_assert_streq(path->typ->identifier.name, "Hello", "paths");
rz_list_free(paths);
paths = rz_type_path_by_offset(typedb, ttype, 0, 0);
mu_assert_notnull(paths, "paths");
mu_assert_eq(rz_list_length(paths), 0, "paths");
rz_list_free(paths);
rz_type_free(ttype);
rz_type_db_free(typedb);
mu_end;
}
bool test_path_by_offset_typedef(void) {
RzTypeDB *typedb = rz_type_db_new();
const char *types_dir = TEST_BUILD_TYPES_DIR;
rz_type_db_init(typedb, types_dir, "x86", 64, "linux");
char *error_msg = NULL;
RzType *ttype = rz_type_parse_string_single(typedb->parser, "struct Card { int fish; uint32_t senf; };", &error_msg);
mu_assert_notnull(ttype, "type parse successful");
mu_assert_eq(ttype->kind, RZ_TYPE_KIND_IDENTIFIER, "parsed type");
mu_assert_streq(ttype->identifier.name, "Card", "parsed type");
RzBaseType *btype = rz_type_base_type_new(RZ_BASE_TYPE_KIND_TYPEDEF);
btype->name = strdup("Alias");
btype->type = RZ_NEW0(RzType);
btype->type->kind = RZ_TYPE_KIND_IDENTIFIER;
btype->type->identifier.name = strdup("Card");
rz_type_db_save_base_type(typedb, btype);
RzList *paths = rz_type_path_by_offset(typedb, ttype, 0, 5);
mu_assert_notnull(paths, "paths");
mu_assert_eq(rz_list_length(paths), 1, "paths");
RzTypePath *path = rz_list_get_n(paths, 0);
mu_assert_streq(path->path, ".fish", "paths");
mu_assert_eq(path->typ->kind, RZ_TYPE_KIND_IDENTIFIER, "paths");
mu_assert_streq(path->typ->identifier.name, "int", "paths");
rz_list_free(paths);
paths = rz_type_path_by_offset(typedb, ttype, 1, 5);
mu_assert_notnull(paths, "paths");
mu_assert_eq(rz_list_length(paths), 0, "paths");
rz_list_free(paths);
paths = rz_type_path_by_offset(typedb, ttype, 4, 5);
mu_assert_notnull(paths, "paths");
mu_assert_eq(rz_list_length(paths), 1, "paths");
path = rz_list_get_n(paths, 0);
mu_assert_streq(path->path, ".senf", "paths");
mu_assert_eq(path->typ->kind, RZ_TYPE_KIND_IDENTIFIER, "paths");
mu_assert_streq(path->typ->identifier.name, "uint32_t", "paths");
rz_list_free(paths);
// Resolving a typedef should not count as a depth step as we
// can't observe it in the path.
paths = rz_type_path_by_offset(typedb, ttype, 4, 1);
mu_assert_notnull(paths, "paths");
mu_assert_eq(rz_list_length(paths), 1, "paths");
path = rz_list_get_n(paths, 0);
mu_assert_streq(path->path, ".senf", "paths");
mu_assert_eq(path->typ->kind, RZ_TYPE_KIND_IDENTIFIER, "paths");
mu_assert_streq(path->typ->identifier.name, "uint32_t", "paths");
rz_list_free(paths);
paths = rz_type_path_by_offset(typedb, ttype, 4, 0);
mu_assert_notnull(paths, "paths");
mu_assert_eq(rz_list_length(paths), 0, "paths");
rz_list_free(paths);
rz_type_free(ttype);
rz_type_db_free(typedb);
mu_end;
}
@ -1103,6 +1624,10 @@ int all_tests() {
mu_run_test(test_references);
mu_run_test(test_addr_bits);
mu_run_test(test_typedef_loop);
mu_run_test(test_path_by_offset_struct);
mu_run_test(test_path_by_offset_union);
mu_run_test(test_path_by_offset_array);
mu_run_test(test_path_by_offset_typedef);
return tests_passed != tests_run;
}