diff --git a/librz/analysis/typelink.c b/librz/analysis/typelink.c index 3c055bb3dd..2af8eb61eb 100644 --- a/librz/analysis/typelink.c +++ b/librz/analysis/typelink.c @@ -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 /**/ *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 /**/ *rz_analysis_type_paths_by_address(RzAnalysis *analysis, ut64 addr) { +RZ_API RZ_OWN RzList /**/ *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, diff --git a/librz/core/ctypes.c b/librz/core/ctypes.c index bc52bc7aab..014178b2f7 100644 --- a/librz/core/ctypes.c +++ b/librz/core/ctypes.c @@ -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) { diff --git a/librz/include/rz_analysis.h b/librz/include/rz_analysis.h index 257bbe691c..83ad8abdd3 100644 --- a/librz/include/rz_analysis.h +++ b/librz/include/rz_analysis.h @@ -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 /**/ *rz_analysis_type_links(RzAnalysis *analysis); -RZ_API RZ_OWN RzList /**/ *rz_analysis_type_links_by_offset(RzAnalysis *analysis, ut64 offset); -RZ_API RZ_OWN RzList /**/ *rz_analysis_type_paths_by_address(RzAnalysis *analysis, ut64 addr); +RZ_API RZ_OWN RzList /**/ *rz_analysis_type_paths_by_address(RzAnalysis *analysis, ut64 addr); /* project */ RZ_API bool rz_analysis_xrefs_init(RzAnalysis *analysis); diff --git a/librz/include/rz_type.h b/librz/include/rz_type.h index 0984640f94..c2f73d3e8d 100644 --- a/librz/include/rz_type.h +++ b/librz/include/rz_type.h @@ -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 /**/ *rz_type_path_by_offset(const RzTypeDB *typedb, RzBaseType *btype, ut64 offset); +RZ_API RZ_OWN RzList /**/ *rz_base_type_path_by_offset(const RzTypeDB *typedb, const RzBaseType *btype, ut64 offset, unsigned int max_depth); +RZ_API RZ_OWN RzList /**/ *rz_type_path_by_offset(const RzTypeDB *typedb, const RzType *type, ut64 offset, unsigned int max_depth); RZ_API RZ_OWN RzList /**/ *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); diff --git a/librz/type/path.c b/librz/type/path.c index 595e9b8d81..b3cd47a9bc 100644 --- a/librz/type/path.c +++ b/librz/type/path.c @@ -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 /**/ *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 /**/ *list, const RzType *type, char *prefix, ut64 offset, unsigned int depth); + +static void collect_base_type_paths(const RzTypeDB *typedb, RzList /**/ *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 /**/ *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 /**/ *rz_type_path_by_offset(const RzTypeDB *typedb, RzBaseType *btype, ut64 offset) { +RZ_API RZ_OWN RzList /**/ *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 /**/ *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 /**/ *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); diff --git a/test/unit/test_type.c b/test/unit/test_type.c index f72b13bb69..9ea588c120 100644 --- a/test/unit/test_type.c +++ b/test/unit/test_type.c @@ -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; }