rizin/librz/arch/pdb_process.c
NOT XVilka 84b2f31035
Fix memory leaks in arch and core (#6590)
Co-authored-by: Anton Kochkov <anton.kochkov@gmail.com>
2026-07-04 13:36:06 +08:00

868 lines
28 KiB
C

// SPDX-FileCopyrightText: 2020 HoundThe <cgkajm@gmail.com>
// SPDX-FileCopyrightText: 2021 Basstorm <basstorm@nyist.edu.cn>
// SPDX-License-Identifier: LGPL-3.0-only
#include <rz_bin.h>
#include <rz_core.h>
#include <rz_type.h>
#include <rz_pdb.h>
#include "analysis_private.h"
#include "../bin/pdb/pdb.h"
static RzType *pdb_type_parse(const RzTypeDB *typedb, RzPdbTpiStream *stream, RzPdbTpiType *type, char *name);
static RzType *modifier_parse(const RzTypeDB *typedb, RzPdbTpiStream *stream, RzPdbTpiType *type);
static RzType *pointer_parse(const RzTypeDB *typedb, RzPdbTpiStream *stream, RzPdbTpiType *type, char *name);
static RzType *procedure_parse(const RzTypeDB *typedb, RzPdbTpiStream *stream, RzPdbTpiType *type, char *name);
static RzType *array_parse(const RzTypeDB *typedb, RzPdbTpiStream *stream, RzPdbTpiType *type);
static void arglist_parse(const RzTypeDB *typedb, RzPdbTpiStream *stream, RzPdbTpiType *arglist, RzPVector /*<RzCallableArg *>*/ *vec);
static RzType *mfunction_parse(const RzTypeDB *typedb, RzPdbTpiStream *stream, RzPdbTpiType *type_info, char *name);
static RzType *onemethod_parse(const RzTypeDB *typedb, RzPdbTpiStream *stream, RzPdbTpiType *type_info);
static RzType *member_parse(const RzTypeDB *typedb, RzPdbTpiStream *stream, RzPdbTpiType *type_info, char *name, ut64 *bitfield_width);
static RzType *nest_parse(const RzTypeDB *typedb, RzPdbTpiStream *stream, RzPdbTpiType *t);
static RzType *union_parse(const RzTypeDB *typedb, RzPdbTpiStream *stream, RzPdbTpiType *type);
static RzTypeUnionMember *union_member_parse(const RzTypeDB *typedb, RzPdbTpiStream *stream, RzPdbTpiType *type_info);
static RzType *class_parse(const RzTypeDB *typedb, RzPdbTpiStream *stream, RzPdbTpiType *type);
static RzTypeStructMember *class_member_parse(const RzTypeDB *typedb, RzPdbTpiStream *stream, RzPdbTpiType *t);
static RzType *enum_parse(const RzTypeDB *typedb, RzPdbTpiStream *stream, RzPdbTpiType *type);
static RzTypeEnumCase *enumerate_parse(RzPdbTpiType *type);
static RzType *pdb_simple_type_parse(const RzTypeDB *typedb, const RzPdbTpiType *type);
static bool is_parsable_type(const RzPdbTpiType *t) {
const RzPDBTpiKind k = t->kind;
return k == TpiKind_CLASS ||
k == TpiKind_UNION ||
k == TpiKind_ENUM;
}
/**
* \brief Create a type name from offset
*
* \param offset
* \return char* Name or NULL if error
*/
static char *create_type_name_from_offset(ut64 offset) {
int offset_length = snprintf(NULL, 0, "type_0x%" PFMT64x, offset);
char *str = malloc(offset_length + 1);
snprintf(str, offset_length + 1, "type_0x%" PFMT64x, offset);
return str;
}
static RzType *pdb_simple_type_parse(const RzTypeDB *typedb, const RzPdbTpiType *type) {
rz_warn_if_fail(type->kind == TpiKind_SIMPLE_TYPE);
const Tpi_LF_SimpleType *simple_type = type->data;
char *error_msg = NULL;
RzType *typ = rz_type_parse_string_single(typedb->parser, simple_type->type, &error_msg);
if (error_msg) {
RZ_LOG_ERROR("%s : Error parsing complex type member \"%s\" type:\n%s\n",
__FUNCTION__, simple_type->type, error_msg);
RZ_FREE(error_msg);
}
return typ;
}
static RzType *array_parse(const RzTypeDB *typedb, RzPdbTpiStream *stream, RzPdbTpiType *type) {
rz_return_val_if_fail(type && stream && typedb, NULL);
Tpi_LF_Array *lf_array = type->data;
RzPdbTpiType *element = rz_bin_pdb_get_type_by_index(stream, lf_array->element_type);
if (!element) {
return NULL;
}
RzType *element_type = pdb_type_parse(typedb, stream, element, NULL);
if (!element_type) {
return NULL;
}
RzType *typ = RZ_NEW0(RzType);
if (!typ) {
return NULL;
}
typ->kind = RZ_TYPE_KIND_ARRAY;
typ->array.type = element_type;
typ->array.count = rz_bin_pdb_get_type_val(type);
return typ;
}
static RzType *modifier_parse(const RzTypeDB *typedb, RzPdbTpiStream *stream, RzPdbTpiType *type) {
rz_return_val_if_fail(type && stream && typedb, NULL);
Tpi_LF_Modifier *lf_modifier = type->data;
RzPdbTpiType *m_utype = rz_bin_pdb_get_type_by_index(stream, lf_modifier->modified_type);
if (m_utype) {
RzType *typ = pdb_type_parse(typedb, stream, m_utype, NULL);
if (typ && lf_modifier->umodifier.const_) {
switch (typ->kind) {
case RZ_TYPE_KIND_IDENTIFIER:
typ->identifier.is_const = true;
break;
case RZ_TYPE_KIND_POINTER:
typ->pointer.is_const = true;
break;
default:
break;
}
}
return typ;
}
return NULL;
}
static RzType *pointer_parse(
const RzTypeDB *typedb, RzPdbTpiStream *stream, RzPdbTpiType *type, char *name) {
rz_return_val_if_fail(type && typedb, NULL);
Tpi_LF_Pointer *lf_pointer = type->data;
RzType *typ = RZ_NEW0(RzType);
if (!typ) {
goto error;
}
typ->kind = RZ_TYPE_KIND_POINTER;
RzPdbTpiType *p_utype = rz_bin_pdb_get_type_by_index(stream, lf_pointer->utype);
if (p_utype) {
RzType *tmp = pdb_type_parse(typedb, stream, p_utype, name);
if (!tmp) {
goto error;
}
typ->pointer.type = tmp;
return typ;
}
error:
rz_type_free(typ);
return NULL;
}
static void arglist_parse(const RzTypeDB *typedb, RzPdbTpiStream *stream, RzPdbTpiType *arglist, RzPVector /*<RzCallableArg *>*/ *vec) {
rz_return_if_fail(arglist && typedb && vec);
Tpi_LF_Arglist *lf_arglist = arglist->data;
ut32 *ptr_types = lf_arglist->arg_type;
for (int i = 0; i < lf_arglist->count; i++) {
RzPdbTpiType *stype = rz_bin_pdb_get_type_by_index(stream, *ptr_types++);
if (!stype) {
continue;
}
RzType *type = pdb_type_parse(typedb, stream, stype, NULL);
if (!type) {
continue;
}
RzCallableArg *arg = RZ_NEW0(RzCallableArg);
arg->name = rz_str_newf("arg%d", i);
arg->type = type;
rz_pvector_push(vec, arg);
}
}
static RzType *procedure_parse(
const RzTypeDB *typedb, RzPdbTpiStream *stream, RzPdbTpiType *type, char *name) {
rz_return_val_if_fail(type && stream && typedb, NULL);
Tpi_LF_Procedure *lf_procedure = type->data;
RzType *typ = RZ_NEW0(RzType);
RzCallable *callable = RZ_NEW0(RzCallable);
if (!typ || !callable) {
free(typ);
free(callable);
return NULL;
}
typ->kind = RZ_TYPE_KIND_CALLABLE;
typ->callable = callable;
if (!name) {
typ->callable->name = create_type_name_from_offset(type->index);
} else {
typ->callable->name = rz_str_dup(name);
}
typ->callable->cc = rz_bin_pdb_calling_convention_as_string(lf_procedure->func_attr.calling_convention);
// parse return type
RzPdbTpiType *ret_type = rz_bin_pdb_get_type_by_index(stream, lf_procedure->return_type);
if (ret_type) {
typ->callable->ret = pdb_type_parse(typedb, stream, ret_type, name);
if (!typ->callable->ret) {
typ->callable->noret = true;
}
}
// parse parameter list
typ->callable->args = rz_pvector_new((RzPVectorFree)rz_type_callable_arg_free);
if (!typ->callable->args) {
rz_type_free(typ);
return NULL;
}
RzPdbTpiType *arglist = rz_bin_pdb_get_type_by_index(stream, lf_procedure->arg_list);
if (arglist) {
arglist_parse(typedb, stream, arglist, typ->callable->args);
}
if (!rz_type_func_save((RzTypeDB *)typedb, callable)) {
// A callable with this name is already registered. PDB type graphs
// frequently reference the same procedure type more than once, and
// unnamed procedures get a deterministic name derived from their TPI
// offset, so duplicates are expected. rz_type_func_save() does not take
// ownership on failure, so reuse the already-stored callable and free
// this one; otherwise it (and its whole subtree) would be leaked.
RzCallable *existing = rz_type_func_get((RzTypeDB *)typedb, callable->name);
rz_type_callable_free(callable);
typ->callable = existing;
if (!existing) {
rz_type_free(typ);
return NULL;
}
}
return typ;
}
static RzType *mfunction_parse(const RzTypeDB *typedb, RzPdbTpiStream *stream, RzPdbTpiType *type_info, char *name) {
rz_return_val_if_fail(type_info && stream && typedb, NULL);
Tpi_LF_MFcuntion *lf_mfunction = type_info->data;
RzType *type = RZ_NEW0(RzType);
RzCallable *callable = RZ_NEW0(RzCallable);
if (!type || !callable) {
free(type);
free(callable);
return NULL;
}
type->kind = RZ_TYPE_KIND_CALLABLE;
type->callable = callable;
type->callable->name = rz_str_dup(name);
type->callable->cc = rz_bin_pdb_calling_convention_as_string(lf_mfunction->func_attr.calling_convention);
// parse return type
RzPdbTpiType *ret_type = rz_bin_pdb_get_type_by_index(stream, lf_mfunction->return_type);
if (ret_type) {
type->callable->ret = pdb_type_parse(typedb, stream, ret_type, name);
if (!type->callable->ret) {
type->callable->noret = true;
}
}
// parse parameter list
type->callable->args = rz_pvector_new((RzPVectorFree)rz_type_callable_arg_free);
if (!type->callable->args) {
rz_type_free(type);
return NULL;
}
RzPdbTpiType *arglist = rz_bin_pdb_get_type_by_index(stream, lf_mfunction->arglist);
if (arglist) {
arglist_parse(typedb, stream, arglist, type->callable->args);
}
if (!rz_type_func_save((RzTypeDB *)typedb, callable)) {
// See procedure_parse(): rz_type_func_save() does not take ownership when
// a callable of the same name already exists, so free this duplicate and
// reuse the stored one to avoid leaking it together with its subtree.
RzCallable *existing = rz_type_func_get((RzTypeDB *)typedb, callable->name);
rz_type_callable_free(callable);
type->callable = existing;
if (!existing) {
rz_type_free(type);
return NULL;
}
}
return type;
}
static RzType *onemethod_parse(
const RzTypeDB *typedb, RzPdbTpiStream *stream, RzPdbTpiType *type_info) {
rz_return_val_if_fail(type_info && typedb, NULL);
Tpi_LF_OneMethod *lf_onemethod = type_info->data;
char *name = rz_bin_pdb_get_type_name(type_info);
RzPdbTpiType *utype = rz_bin_pdb_get_type_by_index(stream, lf_onemethod->index);
if (!utype) {
return NULL;
}
if (utype->kind == TpiKind_MFUNCTION) {
return mfunction_parse(typedb, stream, utype, name);
}
return NULL;
}
static RzType *member_parse(const RzTypeDB *typedb, RzPdbTpiStream *stream, RzPdbTpiType *type_info, char *name, ut64 *bitfield_width) {
rz_return_val_if_fail(type_info && typedb, NULL);
Tpi_LF_Member *lf_member = type_info->data;
RzPdbTpiType *utype = rz_bin_pdb_get_type_by_index(stream, lf_member->field_type);
if (!utype) {
return NULL;
}
if (utype->kind == TpiKind_BITFIELD) {
// A bitfield member ("int a : 4;"): resolve the member to its underlying
// integer type and report the bit width via \p bitfield_width, so it is
// stored as a bitfield (see RzTypeStructMember.size / RzTypeUnionMember.size).
Tpi_LF_Bitfield *lf_bitfield = utype->data;
if (!lf_bitfield) {
return NULL;
}
if (bitfield_width) {
*bitfield_width = lf_bitfield->length;
}
RzPdbTpiType *base = rz_bin_pdb_get_type_by_index(stream, lf_bitfield->base_type);
if (!base) {
return NULL;
}
return pdb_type_parse(typedb, stream, base, name);
}
return pdb_type_parse(typedb, stream, utype, name);
}
static RzType *static_member_parse(const RzTypeDB *typedb, RzPdbTpiStream *stream, RzPdbTpiType *type_info, char *name) {
rz_return_val_if_fail(type_info && typedb, NULL);
Tpi_LF_StaticMember *lf_stmember = type_info->data;
RzPdbTpiType *utype = rz_bin_pdb_get_type_by_index(stream, lf_stmember->field_type);
if (!utype) {
return NULL;
}
return pdb_type_parse(typedb, stream, utype, name);
}
static RzType *type_new_identify(const char *name, RzTypeIdentifierKind k) {
if (RZ_STR_ISEMPTY(name)) {
return NULL;
}
RzType *t = RZ_NEW0(RzType);
if (!t) {
return NULL;
}
t->kind = RZ_TYPE_KIND_IDENTIFIER;
t->identifier.name = rz_str_dup(name);
t->identifier.kind = k;
return t;
}
static RzTypeIdentifierKind iKind_from_bKind(RzBaseTypeKind k) {
switch (k) {
case RZ_BASE_TYPE_KIND_STRUCT: return RZ_TYPE_IDENTIFIER_KIND_STRUCT;
case RZ_BASE_TYPE_KIND_UNION: return RZ_TYPE_IDENTIFIER_KIND_UNION;
case RZ_BASE_TYPE_KIND_ENUM: return RZ_TYPE_IDENTIFIER_KIND_ENUM;
case RZ_BASE_TYPE_KIND_TYPEDEF:
case RZ_BASE_TYPE_KIND_ATOMIC: return RZ_TYPE_IDENTIFIER_KIND_UNSPECIFIED;
}
return RZ_TYPE_IDENTIFIER_KIND_UNSPECIFIED;
}
static RzType *nest_parse(const RzTypeDB *typedb, RzPdbTpiStream *stream, RzPdbTpiType *type_info) {
rz_return_val_if_fail(type_info && stream && typedb, NULL);
Tpi_LF_NestType *lf_nest = type_info->data;
RzPdbTpiType *utpi = rz_bin_pdb_get_type_by_index(stream, lf_nest->index);
if (!utpi) {
return NULL;
}
const char *uname = rz_bin_pdb_get_type_name(utpi);
if (uname) {
const RzBaseType *bt = rz_type_db_get_base_type(typedb, uname);
if (bt) {
return type_new_identify(bt->name, iKind_from_bKind(bt->kind));
}
}
// pdb_type_parse() already returns an owned type, so return it directly
// instead of cloning and leaking the original.
return pdb_type_parse(typedb, stream, utpi, NULL);
}
/**
* \brief Parses struct member
*
* \param typedb Types DB instance
* \param stream TPI Stream
* \param t Current RzPdbTpiType (member)
* \return RzTypeStructMember* parsed member, NULL if fail
*/
static RzTypeStructMember *class_member_parse(
const RzTypeDB *typedb, RzPdbTpiStream *stream, RzPdbTpiType *t) {
rz_return_val_if_fail(t, NULL);
char *name = NULL;
ut64 offset = 0;
ut64 bitfield_width = 0;
RzType *type = NULL;
switch (t->kind) {
case TpiKind_ONEMETHOD: {
name = rz_bin_pdb_get_type_name(t);
type = onemethod_parse(typedb, stream, t);
break;
}
case TpiKind_MEMBER: {
offset = rz_bin_pdb_get_type_val(t);
name = rz_bin_pdb_get_type_name(t);
type = member_parse(typedb, stream, t, name, &bitfield_width);
break;
}
case TpiKind_STMEMBER: {
name = rz_bin_pdb_get_type_name(t);
type = static_member_parse(typedb, stream, t, name);
break;
}
case TpiKind_NESTTYPE: {
name = rz_bin_pdb_get_type_name(t);
type = nest_parse(typedb, stream, t);
break;
}
case TpiKind_VBCLASS:
case TpiKind_BCLASS:
// For structure, we don't need base class for now
goto cleanup;
case TpiKind_METHOD:
// TODO: need to handle overloaded methods here
goto cleanup;
case TpiKind_VFUNCTAB:
// For structure, we don't need vtable for now
goto cleanup;
default:
rz_warn_if_reached();
RZ_LOG_ERROR("%s : unsupported leaf type 0x%x\n", __FUNCTION__, t->leaf);
goto cleanup;
}
if (!type) {
RZ_LOG_INFO("%s : couldn't parse structure member type!\n", __FUNCTION__);
return NULL;
}
RzTypeStructMember *member = RZ_NEW0(RzTypeStructMember);
if (!member) {
goto cleanup;
}
member->name = rz_str_dup(name);
member->type = type;
member->offset = offset;
member->size = bitfield_width; // bitfield width in bits, 0 if not a bitfield
return member;
cleanup:
rz_type_free(type);
return NULL;
}
static inline bool is_tpitype_unnamed(const char *name) {
return !name || !strcmp(name, "<unnamed-tag>") || !strcmp(name, "<anonymous-tag>");
}
static inline RzBaseType *get_tpitype_basetype(const RzTypeDB *typedb, RzPdbTpiType *type, const char *name) {
RzBaseType *base_type;
if (is_tpitype_unnamed(name)) {
char *tmp_name = create_type_name_from_offset(type->index);
base_type = rz_type_db_get_base_type(typedb, tmp_name);
free(tmp_name);
} else {
base_type = rz_type_db_get_base_type(typedb, name);
}
return base_type;
}
static RzType *create_rztype(RzPdbTpiType *type, RzTypeIdentifierKind kind, const char *name) {
RzType *t = RZ_NEW0(RzType);
if (!t) {
return NULL;
}
t->kind = RZ_TYPE_KIND_IDENTIFIER;
t->identifier.kind = kind;
t->identifier.name = is_tpitype_unnamed(name) ? create_type_name_from_offset(type->index) : rz_str_dup(name);
return t;
}
#define PDB_PROCESS_LF_INDEX \
if (member_info->kind == TpiKind_INDEX) { \
ut32 index = rz_bin_pdb_get_type_val(member_info); \
if (index == type->index) { \
break; \
} \
RzPdbTpiType *t = rz_bin_pdb_get_type_by_index(stream, index); \
if (!t) { \
continue; \
} \
RzPVector *t_members = rz_bin_pdb_get_type_members(stream, t); \
if (!t_members) { \
continue; \
} \
members = t_members; \
goto foreach_members; \
}
/**
* \brief Parses structures into BaseType and saves them into hashtable
*
* \param t RzTypeDB instance
* \param stream TPI Stream
* \param type Current type
*/
static RzType *class_parse(const RzTypeDB *typedb, RzPdbTpiStream *stream, RzPdbTpiType *type) {
rz_return_val_if_fail(typedb && stream && type, NULL);
RzBaseType *base_type = NULL;
char *name = rz_bin_pdb_get_type_name(type);
if (name) {
base_type = get_tpitype_basetype(typedb, type, name);
if (base_type) {
if (base_type->kind != RZ_BASE_TYPE_KIND_STRUCT) {
RZ_LOG_WARN("PDB: Type of %s (struct) conflicts with already defined type (%s), redefining it.\n",
name, rz_type_base_type_kind_as_string(base_type->kind));
rz_type_db_delete_base_type((RzTypeDB *)typedb, base_type);
base_type = NULL;
} else if (type->parsed || rz_bin_pdb_type_is_fwdref(type)) {
return base_type->type ? rz_type_clone(base_type->type) : create_rztype(type, RZ_TYPE_IDENTIFIER_KIND_STRUCT, name);
} else if (base_type->attrs != RZ_TYPE_TYPECLASS_INVALID) {
RZ_LOG_INFO("%s : Redefining type %s.\n", __FUNCTION__, name);
}
}
}
if (!base_type) {
base_type = rz_type_base_type_new(RZ_BASE_TYPE_KIND_STRUCT);
if (!base_type) {
return NULL;
}
RzType *typ = create_rztype(type, RZ_TYPE_IDENTIFIER_KIND_STRUCT, name);
if (!typ) {
rz_type_base_type_free(base_type);
return NULL;
}
base_type->type = typ;
base_type->name = rz_str_dup(typ->identifier.name);
base_type->attrs = RZ_TYPE_TYPECLASS_INVALID;
if (!rz_type_db_save_base_type(typedb, base_type)) {
return NULL;
}
if (rz_bin_pdb_type_is_fwdref(type)) {
return rz_type_clone(base_type->type);
}
}
rz_vector_clear(&base_type->struct_data.members);
RzPVector *members = rz_bin_pdb_get_type_members(stream, type);
void **it;
foreach_members:
rz_pvector_foreach (members, it) {
RzPdbTpiType *member_info = *it;
PDB_PROCESS_LF_INDEX;
RzTypeStructMember *struct_member = class_member_parse(typedb, stream, member_info);
if (!struct_member) {
continue; // skip the failure
}
void *element = rz_vector_push(&base_type->struct_data.members, struct_member);
if (!element) {
rz_warn_if_reached();
rz_type_base_struct_member_free(struct_member, NULL);
return NULL;
}
free(struct_member);
}
base_type->size = rz_bin_pdb_get_type_val(type);
if (base_type->attrs == RZ_TYPE_TYPECLASS_INVALID) {
base_type->attrs = RZ_TYPE_TYPECLASS_NONE;
rz_list_append(stream->print_type, base_type);
}
type->parsed = true;
return base_type->type ? rz_type_clone(base_type->type) : NULL;
}
/**
* \brief Parses union member
*
* \param typedb Types DB instance
* \param stream TPI Stream
* \param type_info Current RzPdbTpiType (member)
* \return RzTypeUnionMember* parsed member, NULL if fail
*/
static RzTypeUnionMember *union_member_parse(const RzTypeDB *typedb, RzPdbTpiStream *stream, RzPdbTpiType *type_info) {
rz_return_val_if_fail(type_info && stream && typedb, NULL);
char *name = NULL;
ut64 offset = 0;
ut64 bitfield_width = 0;
RzType *type = NULL;
switch (type_info->kind) {
case TpiKind_ONEMETHOD: {
name = rz_bin_pdb_get_type_name(type_info);
type = onemethod_parse(typedb, stream, type_info);
break;
}
case TpiKind_MEMBER: {
offset = rz_bin_pdb_get_type_val(type_info);
name = rz_bin_pdb_get_type_name(type_info);
type = member_parse(typedb, stream, type_info, name, &bitfield_width);
break;
}
case TpiKind_NESTTYPE: {
name = rz_bin_pdb_get_type_name(type_info);
type = nest_parse(typedb, stream, type_info);
break;
}
default:
rz_warn_if_reached();
RZ_LOG_ERROR("%s : unsupported leaf type 0x%x\n", __FUNCTION__, type_info->leaf);
goto cleanup;
}
if (!type) {
RZ_LOG_INFO("%s : couldn't parse union member type!\n", __FUNCTION__);
return NULL;
}
RzTypeUnionMember *member = RZ_NEW0(RzTypeUnionMember);
if (!member) {
goto cleanup;
}
member->name = rz_str_dup(name);
member->type = type;
member->offset = offset;
member->size = bitfield_width; // bitfield width in bits, 0 if not a bitfield
return member;
cleanup:
rz_type_free(type);
return NULL;
}
/**
* \brief Parses union into BaseType and saves it into hashtable
* \param typedb Types DB instance
* \param stream TPI Stream
* \param type_info Current RzPdbTpiType (enum case)
*/
static RzType *union_parse(const RzTypeDB *typedb, RzPdbTpiStream *stream, RzPdbTpiType *type) {
rz_return_val_if_fail(typedb && stream && type, NULL);
RzBaseType *base_type = NULL;
char *name = rz_bin_pdb_get_type_name(type);
if (name) {
base_type = get_tpitype_basetype(typedb, type, name);
if (base_type) {
if (base_type->kind != RZ_BASE_TYPE_KIND_UNION) {
RZ_LOG_WARN("PDB: Type of %s (union) conflicts with already defined type (%s), redefining it.\n",
name, rz_type_base_type_kind_as_string(base_type->kind));
rz_type_db_delete_base_type((RzTypeDB *)typedb, base_type);
base_type = NULL;
} else if (type->parsed || rz_bin_pdb_type_is_fwdref(type)) {
return base_type->type ? rz_type_clone(base_type->type) : create_rztype(type, RZ_TYPE_IDENTIFIER_KIND_UNION, name);
} else if (base_type->attrs != RZ_TYPE_TYPECLASS_INVALID) {
RZ_LOG_INFO("%s : Redefining type %s.\n", __FUNCTION__, name);
}
}
}
if (!base_type) {
base_type = rz_type_base_type_new(RZ_BASE_TYPE_KIND_UNION);
if (!base_type) {
return NULL;
}
RzType *typ = create_rztype(type, RZ_TYPE_IDENTIFIER_KIND_UNION, name);
if (!typ) {
rz_type_base_type_free(base_type);
return NULL;
}
base_type->type = typ;
base_type->name = rz_str_dup(typ->identifier.name);
base_type->attrs = RZ_TYPE_TYPECLASS_INVALID;
if (!rz_type_db_save_base_type(typedb, base_type)) {
return NULL;
}
if (rz_bin_pdb_type_is_fwdref(type)) {
return rz_type_clone(base_type->type);
}
}
rz_vector_clear(&base_type->union_data.members);
RzPVector *members = rz_bin_pdb_get_type_members(stream, type);
void **it;
foreach_members:
rz_pvector_foreach (members, it) {
RzPdbTpiType *member_info = *it;
PDB_PROCESS_LF_INDEX;
RzTypeUnionMember *union_member = union_member_parse(typedb, stream, member_info);
if (!union_member) {
continue; // skip the failure
}
void *element = rz_vector_push(&base_type->union_data.members, union_member);
if (!element) {
rz_type_base_union_member_free(union_member, NULL);
return NULL;
}
free(union_member);
}
base_type->size = rz_bin_pdb_get_type_val(type);
if (base_type->attrs == RZ_TYPE_TYPECLASS_INVALID) {
base_type->attrs = RZ_TYPE_TYPECLASS_NONE;
rz_list_append(stream->print_type, base_type);
}
type->parsed = true;
return base_type->type ? rz_type_clone(base_type->type) : NULL;
}
/**
* \brief Parse enum case
* \param type Current type info (enum case)
* \return RzTypeEnumCase* parsed enum case, NULL if fail
*/
static RzTypeEnumCase *enumerate_parse(RzPdbTpiType *type) {
rz_return_val_if_fail(type && type->kind == TpiKind_ENUMERATE, NULL);
// sometimes, the type doesn't have get_val for some reason
ut64 value = rz_bin_pdb_get_type_val(type);
char *name = rz_bin_pdb_get_type_name(type);
RzTypeEnumCase *cas = RZ_NEW0(RzTypeEnumCase);
if (!cas) {
goto cleanup;
}
cas->name = rz_str_dup(name);
cas->val = value;
return cas;
cleanup:
return NULL;
}
/**
* \brief Parses enum into BaseType and saves it into SDB
*
* \param t RzTypeDB instance
* \param stream TPI Stream
* \param type Current type
*/
static RzType *enum_parse(const RzTypeDB *typedb, RzPdbTpiStream *stream, RzPdbTpiType *type) {
rz_return_val_if_fail(typedb && type, NULL);
Tpi_LF_Enum *lf_enum = type->data;
// assert all member functions we need info from
RzBaseType *base_type = NULL;
char *name = rz_bin_pdb_get_type_name(type);
if (name) {
base_type = get_tpitype_basetype(typedb, type, name);
if (base_type) {
if (base_type->kind != RZ_BASE_TYPE_KIND_ENUM) {
RZ_LOG_WARN("PDB: Type of %s (enum) conflicts with already defined type (%s), redefining it.\n",
name, rz_type_base_type_kind_as_string(base_type->kind));
rz_type_db_delete_base_type((RzTypeDB *)typedb, base_type);
base_type = NULL;
} else if (type->parsed || rz_bin_pdb_type_is_fwdref(type)) {
return base_type->type ? rz_type_clone(base_type->type) : NULL;
} else if (base_type->attrs != RZ_TYPE_TYPECLASS_INVALID) {
RZ_LOG_INFO("%s : Redefining type %s.\n", __FUNCTION__, name);
}
}
}
if (!base_type) {
base_type = rz_type_base_type_new(RZ_BASE_TYPE_KIND_ENUM);
if (!base_type) {
return NULL;
}
RzPdbTpiType *utype = rz_bin_pdb_get_type_by_index(stream, lf_enum->utype);
if (!utype) {
rz_type_base_type_free(base_type);
return NULL;
}
RzType *btype = pdb_type_parse(typedb, stream, utype, NULL);
if (!btype) {
rz_type_base_type_free(base_type);
return NULL;
}
base_type->name = is_tpitype_unnamed(name) ? create_type_name_from_offset(type->index) : rz_str_dup(name);
base_type->size = rz_type_db_get_bitsize(typedb, btype);
base_type->type = btype;
base_type->attrs = RZ_TYPE_TYPECLASS_INVALID;
if (!rz_type_db_save_base_type(typedb, base_type)) {
return NULL;
}
if (rz_bin_pdb_type_is_fwdref(type)) {
return rz_type_clone(base_type->type);
}
}
rz_vector_clear(&base_type->enum_data.cases);
RzPVector *members = rz_bin_pdb_get_type_members(stream, type);
void **it;
foreach_members:
rz_pvector_foreach (members, it) {
RzPdbTpiType *member_info = *it;
PDB_PROCESS_LF_INDEX;
RzTypeEnumCase *enum_case = enumerate_parse(member_info);
if (!enum_case) {
continue; // skip it, move forward
}
void *element = rz_vector_push(&base_type->enum_data.cases, enum_case);
if (!element) {
rz_type_base_enum_case_free(enum_case, NULL);
return NULL;
}
free(enum_case);
}
if (base_type->attrs == RZ_TYPE_TYPECLASS_INVALID) {
base_type->attrs = RZ_TYPE_TYPECLASS_NONE;
rz_list_append(stream->print_type, base_type);
}
type->parsed = true;
return base_type->type ? rz_type_clone(base_type->type) : NULL;
}
static RzType *pdb_type_parse(const RzTypeDB *typedb, RzPdbTpiStream *stream, RzPdbTpiType *type, char *name) {
switch (type->kind) {
case TpiKind_SIMPLE_TYPE:
return pdb_simple_type_parse(typedb, type);
case TpiKind_CLASS:
return class_parse(typedb, stream, type);
case TpiKind_UNION:
return union_parse(typedb, stream, type);
case TpiKind_ENUM:
return enum_parse(typedb, stream, type);
case TpiKind_MODIFIER:
return modifier_parse(typedb, stream, type);
case TpiKind_ARRAY:
return array_parse(typedb, stream, type);
case TpiKind_POINTER:
return pointer_parse(typedb, stream, type, name);
case TpiKind_PROCEDURE:
return procedure_parse(typedb, stream, type, name);
case TpiKind_MFUNCTION:
return mfunction_parse(typedb, stream, type, name);
case TpiKind_BITFIELD:
case TpiKind_BCLASS:
case TpiKind_FILEDLIST:
case TpiKind_ENUMERATE:
case TpiKind_ARGLIST:
case TpiKind_METHODLIST:
case TpiKind_VTSHAPE:
case TpiKind_VFTABLE:
case TpiKind_LABEL:
case TpiKind_NESTTYPE:
case TpiKind_MEMBER:
case TpiKind_METHOD:
case TpiKind_ONEMETHOD:
case TpiKind_VFUNCTAB:
case TpiKind_STMEMBER:
case TpiKind_VBCLASS:
case TpiKind_INDEX:
return NULL;
default:
rz_warn_if_reached();
RZ_LOG_DEBUG("Unknown type record: #0x%" PFMT32x ": leaf=0x%" PFMT32x "\n",
type->index, type->leaf);
break;
}
return NULL;
}
/**
* \brief Delegate the type parsing to appropriate function
*
* \param t RzTypeDB instance
* \param stream TPI Stream
* \param type Current type
*/
RZ_API RzType *rz_type_db_pdb_parse(const RzTypeDB *typedb, RzPdbTpiStream *stream, RzPdbTpiType *type) {
rz_return_val_if_fail(typedb && type, NULL);
return pdb_type_parse(typedb, stream, type, NULL);
}
/**
* \brief Saves PDB types from TPI stream into the type database
*
* \param t RzTypeDB instance
* \param pdb PDB instance
*/
RZ_API void rz_type_db_pdb_load(const RzTypeDB *typedb, const RzPdb *pdb) {
rz_return_if_fail(typedb && pdb);
RzPdbTpiStream *stream = pdb->s_tpi;
if (!stream) { // no TPI stream found
return;
}
stream->print_type = rz_list_new();
if (!stream->print_type) {
return;
}
RBIter it;
RzPdbTpiType *type;
rz_rbtree_foreach (stream->types, it, type, RzPdbTpiType, rb) {
if (type && is_parsable_type(type)) {
// rz_type_db_pdb_parse() returns an owned clone of the parsed type.
// The base type itself is saved into the typedb during parsing, so
// this returned clone is not needed here and must be freed, otherwise
// one type tree leaks for every parsable type in the PDB.
rz_type_free(rz_type_db_pdb_parse(typedb, stream, type));
}
}
}