mirror of
https://github.com/rizinorg/rizin.git
synced 2026-10-12 02:21:57 +00:00
Create global variables from PDB (#4021)
This commit is contained in:
@@ -103,7 +103,7 @@ rz_analysis_sources = [
|
||||
'serialize_analysis.c',
|
||||
'similarity.c',
|
||||
'switch.c',
|
||||
'type_pdb.c',
|
||||
'pdb_process.c',
|
||||
'types.c',
|
||||
'value.c',
|
||||
'var.c',
|
||||
|
||||
@@ -28,7 +28,6 @@ static RzType *parse_enum(const RzTypeDB *typedb, RzPdbTpiStream *stream, RzPdbT
|
||||
static RzTypeEnumCase *parse_enumerate(RzPdbTpiType *type);
|
||||
|
||||
static bool is_parsable_type(const TpiLeafType type) {
|
||||
rz_return_val_if_fail(type, false);
|
||||
return (type == LF_STRUCTURE ||
|
||||
type == LF_UNION ||
|
||||
type == LF_ENUM ||
|
||||
@@ -52,7 +51,7 @@ static char *create_type_name_from_offset(ut64 offset) {
|
||||
|
||||
static RzType *parse_type_array(const RzTypeDB *typedb, RzPdbTpiStream *stream, RzPdbTpiType *type) {
|
||||
rz_return_val_if_fail(type && stream && typedb, NULL);
|
||||
Tpi_LF_Array *lf_array = type->type_data;
|
||||
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;
|
||||
@@ -73,7 +72,7 @@ static RzType *parse_type_array(const RzTypeDB *typedb, RzPdbTpiStream *stream,
|
||||
|
||||
static RzType *parse_regular_type(const RzTypeDB *typedb, RzPdbTpiStream *stream, RzPdbTpiType *type, char *name) {
|
||||
rz_return_val_if_fail(type && stream && typedb, NULL);
|
||||
switch (type->leaf_type) {
|
||||
switch (type->leaf) {
|
||||
case LF_CLASS:
|
||||
case LF_CLASS_19:
|
||||
// TODO: https://github.com/rizinorg/rizin/issues/1205
|
||||
@@ -99,7 +98,7 @@ static RzType *parse_regular_type(const RzTypeDB *typedb, RzPdbTpiStream *stream
|
||||
case LF_ENUM:
|
||||
return parse_enum(typedb, stream, type);
|
||||
default:
|
||||
RZ_LOG_INFO("%s : unsupported leaf type 0x%x\n", __FUNCTION__, type->leaf_type);
|
||||
RZ_LOG_INFO("%s : unsupported leaf type 0x%x\n", __FUNCTION__, type->leaf);
|
||||
break;
|
||||
}
|
||||
return NULL;
|
||||
@@ -107,11 +106,11 @@ static RzType *parse_regular_type(const RzTypeDB *typedb, RzPdbTpiStream *stream
|
||||
|
||||
static RzType *parse_type_modifier(const RzTypeDB *typedb, RzPdbTpiStream *stream, RzPdbTpiType *type) {
|
||||
rz_return_val_if_fail(type && stream && typedb, NULL);
|
||||
Tpi_LF_Modifier *lf_modifier = type->type_data;
|
||||
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 = parse_type(typedb, stream, m_utype, NULL);
|
||||
if (typ && lf_modifier->umodifier.bits.const_) {
|
||||
if (typ && lf_modifier->umodifier.const_) {
|
||||
switch (typ->kind) {
|
||||
case RZ_TYPE_KIND_IDENTIFIER:
|
||||
typ->identifier.is_const = true;
|
||||
@@ -130,7 +129,7 @@ static RzType *parse_type_modifier(const RzTypeDB *typedb, RzPdbTpiStream *strea
|
||||
|
||||
static RzType *parse_type_pointer(const RzTypeDB *typedb, RzPdbTpiStream *stream, RzPdbTpiType *type, char *name) {
|
||||
rz_return_val_if_fail(type && typedb, NULL);
|
||||
Tpi_LF_Pointer *lf_pointer = type->type_data;
|
||||
Tpi_LF_Pointer *lf_pointer = type->data;
|
||||
RzType *typ = RZ_NEW0(RzType);
|
||||
if (!typ) {
|
||||
goto error;
|
||||
@@ -153,8 +152,8 @@ error:
|
||||
static RzType *parse_type(const RzTypeDB *typedb, RzPdbTpiStream *stream, RzPdbTpiType *type, char *name) {
|
||||
rz_return_val_if_fail(type && typedb, NULL);
|
||||
RzType *typ;
|
||||
if (type->leaf_type == LF_SIMPLE_TYPE) {
|
||||
Tpi_LF_SimpleType *simple_type = type->type_data;
|
||||
if (type->leaf == LF_SIMPLE_TYPE) {
|
||||
Tpi_LF_SimpleType *simple_type = type->data;
|
||||
char *error_msg = NULL;
|
||||
typ = rz_type_parse_string_single(typedb->parser, simple_type->type, &error_msg);
|
||||
if (error_msg) {
|
||||
@@ -163,7 +162,7 @@ static RzType *parse_type(const RzTypeDB *typedb, RzPdbTpiStream *stream, RzPdbT
|
||||
}
|
||||
return typ;
|
||||
} else {
|
||||
if (type->leaf_type == LF_POINTER) {
|
||||
if (type->leaf == LF_POINTER) {
|
||||
return parse_type_pointer(typedb, stream, type, name);
|
||||
} else {
|
||||
return parse_regular_type(typedb, stream, type, name);
|
||||
@@ -173,7 +172,7 @@ static RzType *parse_type(const RzTypeDB *typedb, RzPdbTpiStream *stream, RzPdbT
|
||||
|
||||
static void parse_type_arglist(const RzTypeDB *typedb, RzPdbTpiStream *stream, RzPdbTpiType *arglist, RzPVector /*<RzCallableArg *>*/ *vec) {
|
||||
rz_return_if_fail(arglist && typedb && vec);
|
||||
Tpi_LF_Arglist *lf_arglist = arglist->type_data;
|
||||
Tpi_LF_Arglist *lf_arglist = arglist->data;
|
||||
if (!vec) {
|
||||
return;
|
||||
}
|
||||
@@ -196,7 +195,7 @@ static void parse_type_arglist(const RzTypeDB *typedb, RzPdbTpiStream *stream, R
|
||||
|
||||
static RzType *parse_type_procedure(const RzTypeDB *typedb, RzPdbTpiStream *stream, RzPdbTpiType *type, char *name) {
|
||||
rz_return_val_if_fail(type && stream && typedb, NULL);
|
||||
Tpi_LF_Procedure *lf_procedure = type->type_data;
|
||||
Tpi_LF_Procedure *lf_procedure = type->data;
|
||||
RzType *typ = RZ_NEW0(RzType);
|
||||
RzCallable *callable = RZ_NEW0(RzCallable);
|
||||
if (!typ || !callable) {
|
||||
@@ -207,7 +206,7 @@ static RzType *parse_type_procedure(const RzTypeDB *typedb, RzPdbTpiStream *stre
|
||||
typ->kind = RZ_TYPE_KIND_CALLABLE;
|
||||
typ->callable = callable;
|
||||
if (!name) {
|
||||
typ->callable->name = create_type_name_from_offset(type->type_index);
|
||||
typ->callable->name = create_type_name_from_offset(type->index);
|
||||
} else {
|
||||
typ->callable->name = strdup(name);
|
||||
}
|
||||
@@ -237,7 +236,7 @@ static RzType *parse_type_procedure(const RzTypeDB *typedb, RzPdbTpiStream *stre
|
||||
|
||||
static RzType *parse_type_mfunction(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->type_data;
|
||||
Tpi_LF_MFcuntion *lf_mfunction = type_info->data;
|
||||
RzType *type = RZ_NEW0(RzType);
|
||||
RzCallable *callable = RZ_NEW0(RzCallable);
|
||||
if (!type || !callable) {
|
||||
@@ -273,13 +272,13 @@ static RzType *parse_type_mfunction(const RzTypeDB *typedb, RzPdbTpiStream *stre
|
||||
|
||||
static RzType *parse_type_onemethod(const RzTypeDB *typedb, RzPdbTpiStream *stream, RzPdbTpiType *type_info) {
|
||||
rz_return_val_if_fail(type_info && typedb, NULL);
|
||||
Tpi_LF_OneMethod *lf_onemethod = type_info->type_data;
|
||||
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->leaf_type == LF_MFUNCTION) {
|
||||
if (utype->leaf == LF_MFUNCTION) {
|
||||
return parse_type_mfunction(typedb, stream, utype, name);
|
||||
}
|
||||
return NULL;
|
||||
@@ -287,7 +286,7 @@ static RzType *parse_type_onemethod(const RzTypeDB *typedb, RzPdbTpiStream *stre
|
||||
|
||||
static RzType *parse_type_member(const RzTypeDB *typedb, RzPdbTpiStream *stream, RzPdbTpiType *type_info, char *name) {
|
||||
rz_return_val_if_fail(type_info && typedb, NULL);
|
||||
Tpi_LF_Member *lf_member = type_info->type_data;
|
||||
Tpi_LF_Member *lf_member = type_info->data;
|
||||
|
||||
RzPdbTpiType *utype = rz_bin_pdb_get_type_by_index(stream, lf_member->index);
|
||||
if (!utype) {
|
||||
@@ -298,7 +297,7 @@ static RzType *parse_type_member(const RzTypeDB *typedb, RzPdbTpiStream *stream,
|
||||
|
||||
static RzType *parse_type_static_member(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->type_data;
|
||||
Tpi_LF_StaticMember *lf_stmember = type_info->data;
|
||||
RzPdbTpiType *utype = rz_bin_pdb_get_type_by_index(stream, lf_stmember->index);
|
||||
if (!utype) {
|
||||
return NULL;
|
||||
@@ -308,7 +307,7 @@ static RzType *parse_type_static_member(const RzTypeDB *typedb, RzPdbTpiStream *
|
||||
|
||||
static RzType *parse_type_nest(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->type_data;
|
||||
Tpi_LF_NestType *lf_nest = type_info->data;
|
||||
RzPdbTpiType *utype = rz_bin_pdb_get_type_by_index(stream, lf_nest->index);
|
||||
if (!utype) {
|
||||
return NULL;
|
||||
@@ -356,7 +355,7 @@ static RzTypeStructMember *parse_struct_member(const RzTypeDB *typedb, RzPdbTpiS
|
||||
char *name = NULL;
|
||||
ut64 offset = 0;
|
||||
RzType *type = NULL;
|
||||
switch (type_info->leaf_type) {
|
||||
switch (type_info->leaf) {
|
||||
case LF_ONEMETHOD: {
|
||||
name = rz_bin_pdb_get_type_name(type_info);
|
||||
type = parse_type_onemethod(typedb, stream, type_info);
|
||||
@@ -388,7 +387,7 @@ static RzTypeStructMember *parse_struct_member(const RzTypeDB *typedb, RzPdbTpiS
|
||||
// For structure, we don't need vtable for now
|
||||
goto cleanup;
|
||||
default:
|
||||
RZ_LOG_ERROR("%s : unsupported leaf type 0x%x\n", __FUNCTION__, type_info->leaf_type);
|
||||
RZ_LOG_ERROR("%s : unsupported leaf type 0x%x\n", __FUNCTION__, type_info->leaf);
|
||||
goto cleanup;
|
||||
}
|
||||
if (!type) {
|
||||
@@ -416,7 +415,7 @@ static inline bool is_tpitype_unnamed(const char *name) {
|
||||
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->type_index);
|
||||
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 {
|
||||
@@ -432,10 +431,28 @@ static RzType *create_rztype(RzPdbTpiType *type, RzTypeIdentifierKind kind, cons
|
||||
}
|
||||
t->kind = RZ_TYPE_KIND_IDENTIFIER;
|
||||
t->identifier.kind = kind;
|
||||
t->identifier.name = is_tpitype_unnamed(name) ? create_type_name_from_offset(type->type_index) : strdup(name);
|
||||
t->identifier.name = is_tpitype_unnamed(name) ? create_type_name_from_offset(type->index) : strdup(name);
|
||||
return t;
|
||||
}
|
||||
|
||||
#define PDB_PROCESS_LF_INDEX \
|
||||
if (member_info->leaf == LF_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
|
||||
*
|
||||
@@ -487,19 +504,12 @@ static RzType *parse_structure(const RzTypeDB *typedb, RzPdbTpiStream *stream, R
|
||||
}
|
||||
}
|
||||
rz_vector_clear(&base_type->struct_data.members);
|
||||
RzList *members = rz_bin_pdb_get_type_members(stream, type);
|
||||
RzListIter *it;
|
||||
RzPdbTpiType *member_info;
|
||||
rz_list_foreach (members, it, member_info) {
|
||||
if (member_info->leaf_type == LF_INDEX) {
|
||||
type = rz_bin_pdb_get_type_by_index(stream, rz_bin_pdb_get_type_val(member_info));
|
||||
members = rz_bin_pdb_get_type_members(stream, type);
|
||||
it = rz_list_head(members);
|
||||
if (!it) {
|
||||
break;
|
||||
}
|
||||
member_info = rz_list_first(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 = parse_struct_member(typedb, stream, member_info);
|
||||
if (!struct_member) {
|
||||
continue; // skip the failure
|
||||
@@ -533,7 +543,7 @@ static RzTypeUnionMember *parse_union_member(const RzTypeDB *typedb, RzPdbTpiStr
|
||||
char *name = NULL;
|
||||
ut64 offset = 0;
|
||||
RzType *type = NULL;
|
||||
switch (type_info->leaf_type) {
|
||||
switch (type_info->leaf) {
|
||||
case LF_ONEMETHOD: {
|
||||
name = rz_bin_pdb_get_type_name(type_info);
|
||||
type = parse_type_onemethod(typedb, stream, type_info);
|
||||
@@ -551,7 +561,7 @@ static RzTypeUnionMember *parse_union_member(const RzTypeDB *typedb, RzPdbTpiStr
|
||||
break;
|
||||
}
|
||||
default:
|
||||
RZ_LOG_ERROR("%s : unsupported leaf type 0x%x\n", __FUNCTION__, type_info->leaf_type);
|
||||
RZ_LOG_ERROR("%s : unsupported leaf type 0x%x\n", __FUNCTION__, type_info->leaf);
|
||||
goto cleanup;
|
||||
}
|
||||
if (!type) {
|
||||
@@ -622,19 +632,12 @@ static RzType *parse_union(const RzTypeDB *typedb, RzPdbTpiStream *stream, RzPdb
|
||||
}
|
||||
}
|
||||
rz_vector_clear(&base_type->union_data.members);
|
||||
RzList *members = rz_bin_pdb_get_type_members(stream, type);
|
||||
RzListIter *it;
|
||||
RzPdbTpiType *member_info;
|
||||
rz_list_foreach (members, it, member_info) {
|
||||
if (member_info->leaf_type == LF_INDEX) {
|
||||
type = rz_bin_pdb_get_type_by_index(stream, rz_bin_pdb_get_type_val(member_info));
|
||||
members = rz_bin_pdb_get_type_members(stream, type);
|
||||
it = rz_list_head(members);
|
||||
if (!it) {
|
||||
break;
|
||||
}
|
||||
member_info = rz_list_first(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 = parse_union_member(typedb, stream, member_info);
|
||||
if (!union_member) {
|
||||
continue; // skip the failure
|
||||
@@ -661,7 +664,7 @@ static RzType *parse_union(const RzTypeDB *typedb, RzPdbTpiStream *stream, RzPdb
|
||||
* \return RzTypeEnumCase* parsed enum case, NULL if fail
|
||||
*/
|
||||
static RzTypeEnumCase *parse_enumerate(RzPdbTpiType *type) {
|
||||
rz_return_val_if_fail(type && type->leaf_type == LF_ENUMERATE, NULL);
|
||||
rz_return_val_if_fail(type && type->leaf == LF_ENUMERATE, NULL);
|
||||
|
||||
char *name = NULL;
|
||||
ut64 value = 0;
|
||||
@@ -688,7 +691,7 @@ cleanup:
|
||||
*/
|
||||
static RzType *parse_enum(const RzTypeDB *typedb, RzPdbTpiStream *stream, RzPdbTpiType *type) {
|
||||
rz_return_val_if_fail(typedb && type, NULL);
|
||||
Tpi_LF_Enum *lf_enum = type->type_data;
|
||||
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);
|
||||
@@ -724,7 +727,7 @@ static RzType *parse_enum(const RzTypeDB *typedb, RzPdbTpiStream *stream, RzPdbT
|
||||
rz_type_base_type_free(base_type);
|
||||
return NULL;
|
||||
}
|
||||
base_type->name = is_tpitype_unnamed(name) ? create_type_name_from_offset(type->type_index) : strdup(name);
|
||||
base_type->name = is_tpitype_unnamed(name) ? create_type_name_from_offset(type->index) : strdup(name);
|
||||
base_type->size = rz_type_db_get_bitsize(typedb, btype);
|
||||
base_type->type = btype;
|
||||
base_type->attrs = RZ_TYPE_TYPECLASS_INVALID;
|
||||
@@ -737,19 +740,12 @@ static RzType *parse_enum(const RzTypeDB *typedb, RzPdbTpiStream *stream, RzPdbT
|
||||
}
|
||||
}
|
||||
rz_vector_clear(&base_type->enum_data.cases);
|
||||
RzList *members = rz_bin_pdb_get_type_members(stream, type);
|
||||
RzListIter *it;
|
||||
RzPdbTpiType *member_info;
|
||||
rz_list_foreach (members, it, member_info) {
|
||||
if (member_info->leaf_type == LF_INDEX) {
|
||||
type = rz_bin_pdb_get_type_by_index(stream, rz_bin_pdb_get_type_val(member_info));
|
||||
members = rz_bin_pdb_get_type_members(stream, type);
|
||||
it = rz_list_head(members);
|
||||
if (!it) {
|
||||
break;
|
||||
}
|
||||
member_info = rz_list_first(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;
|
||||
RzTypeEnumCase *enum_case = parse_enumerate(member_info);
|
||||
if (!enum_case) {
|
||||
continue; // skip it, move forward
|
||||
@@ -776,29 +772,43 @@ static RzType *parse_enum(const RzTypeDB *typedb, RzPdbTpiStream *stream, RzPdbT
|
||||
* \param stream TPI Stream
|
||||
* \param type Current type
|
||||
*/
|
||||
static void parse_types(const RzTypeDB *typedb, RzPdbTpiStream *stream, RzPdbTpiType *type) {
|
||||
rz_return_if_fail(typedb && type);
|
||||
RZ_API RzType *rz_type_db_pdb_parse(const RzTypeDB *typedb, RzPdbTpiStream *stream, RzPdbTpiType *type) {
|
||||
rz_return_val_if_fail(typedb && type, NULL);
|
||||
|
||||
switch (type->leaf_type) {
|
||||
switch (type->leaf) {
|
||||
case LF_SIMPLE_TYPE: {
|
||||
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;
|
||||
}
|
||||
case LF_CLASS:
|
||||
case LF_CLASS_19:
|
||||
break;
|
||||
case LF_STRUCTURE:
|
||||
case LF_STRUCTURE_19:
|
||||
parse_structure(typedb, stream, type);
|
||||
break;
|
||||
return parse_structure(typedb, stream, type);
|
||||
case LF_UNION:
|
||||
parse_union(typedb, stream, type);
|
||||
break;
|
||||
return parse_union(typedb, stream, type);
|
||||
case LF_ENUM:
|
||||
parse_enum(typedb, stream, type);
|
||||
break;
|
||||
return parse_enum(typedb, stream, type);
|
||||
case LF_MODIFIER:
|
||||
return parse_type_modifier(typedb, stream, type);
|
||||
case LF_ARRAY:
|
||||
return parse_type_array(typedb, stream, type);
|
||||
case LF_POINTER:
|
||||
return parse_type_pointer(typedb, stream, type, NULL);
|
||||
case LF_PROCEDURE:
|
||||
return parse_type_procedure(typedb, stream, type, NULL);
|
||||
default:
|
||||
// shouldn't happen, happens when someone modifies leafs that get here
|
||||
// but not how they should be parsed
|
||||
RZ_LOG_ERROR("Unknown type record");
|
||||
RZ_LOG_DEBUG("Unknown type record: #0x%" PFMT32x ": leaf=0x%" PFMT32x "\n",
|
||||
type->index, type->leaf);
|
||||
break;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -807,7 +817,7 @@ static void parse_types(const RzTypeDB *typedb, RzPdbTpiStream *stream, RzPdbTpi
|
||||
* \param t RzTypeDB instance
|
||||
* \param pdb PDB instance
|
||||
*/
|
||||
RZ_API void rz_parse_pdb_types(const RzTypeDB *typedb, const RzPdb *pdb) {
|
||||
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
|
||||
@@ -822,8 +832,8 @@ RZ_API void rz_parse_pdb_types(const RzTypeDB *typedb, const RzPdb *pdb) {
|
||||
RBIter it;
|
||||
RzPdbTpiType *type;
|
||||
rz_rbtree_foreach (stream->types, it, type, RzPdbTpiType, rb) {
|
||||
if (type && is_parsable_type(type->leaf_type)) {
|
||||
parse_types(typedb, stream, type);
|
||||
if (type && is_parsable_type(type->leaf)) {
|
||||
rz_type_db_pdb_parse(typedb, stream, type);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -259,6 +259,7 @@ rz_bin_sources = [
|
||||
'pdb/pdb_downloader.c',
|
||||
'pdb/stream_pe.c',
|
||||
'pdb/tpi.c',
|
||||
'pdb/symbol.c',
|
||||
|
||||
'dwarf/abbrev.c',
|
||||
'dwarf/addr.c',
|
||||
|
||||
+22
-59
@@ -4,34 +4,12 @@
|
||||
|
||||
#include "pdb.h"
|
||||
|
||||
static bool parse_gdata_global(GDataGlobal *global, RzBuffer *buf, ut32 initial_seek) {
|
||||
if (!rz_buf_read_le32(buf, &global->symtype) ||
|
||||
!rz_buf_read_le32(buf, &global->offset)) {
|
||||
return false;
|
||||
}
|
||||
if (!rz_buf_read_le16(buf, &global->segment)) {
|
||||
return false;
|
||||
}
|
||||
if (global->leaf_type == 0x110E) {
|
||||
global->name = rz_buf_get_string(buf, rz_buf_tell(buf));
|
||||
ut16 len = strlen(global->name) + 1;
|
||||
global->name_len = len;
|
||||
rz_buf_seek(buf, len, RZ_BUF_CUR);
|
||||
} else {
|
||||
if (!rz_buf_read8(buf, &global->name_len)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
ut32 read_len = rz_buf_tell(buf) - initial_seek;
|
||||
if (read_len % 4) {
|
||||
ut16 remain = 4 - (read_len % 4);
|
||||
rz_buf_seek(buf, remain, RZ_BUF_CUR);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
RZ_IPI bool parse_gdata_stream(RzPdb *pdb, RzPdbMsfStream *stream) {
|
||||
rz_return_val_if_fail(pdb && stream, false);
|
||||
PDBSymbolTable *syms = pdb_global_symbols(pdb);
|
||||
if (!syms) {
|
||||
return false;
|
||||
}
|
||||
if (!pdb->s_gdata) {
|
||||
pdb->s_gdata = RZ_NEW0(RzPdbGDataStream);
|
||||
}
|
||||
@@ -40,53 +18,38 @@ RZ_IPI bool parse_gdata_stream(RzPdb *pdb, RzPdbMsfStream *stream) {
|
||||
RZ_LOG_ERROR("Error allocating memory.\n");
|
||||
return false;
|
||||
}
|
||||
RzBuffer *buf = stream->stream_data;
|
||||
s->global_list = rz_list_new();
|
||||
if (!s->global_list) {
|
||||
s->global_symbols = rz_pvector_new(free);
|
||||
if (!s->global_symbols) {
|
||||
return false;
|
||||
}
|
||||
ut16 len;
|
||||
|
||||
PDBSymbolIter iter = { 0 };
|
||||
PDBSymbolTable_iter(syms, &iter);
|
||||
while (true) {
|
||||
ut32 initial_seek = rz_buf_tell(buf);
|
||||
if (!rz_buf_read_le16(buf, &len)) {
|
||||
PDBSymbol *symbol = RZ_NEW0(PDBSymbol);
|
||||
if (!symbol) {
|
||||
goto err;
|
||||
}
|
||||
if (!PDBSymbolIter_next(&iter, symbol)) {
|
||||
free(symbol);
|
||||
break;
|
||||
}
|
||||
if (len == 0 || len == UT16_MAX) {
|
||||
break;
|
||||
}
|
||||
ut16 leaf_type;
|
||||
if (!rz_buf_read_le16(buf, &leaf_type)) {
|
||||
return false;
|
||||
}
|
||||
if (leaf_type == 0x110E || leaf_type == 0x1009) {
|
||||
GDataGlobal *global = RZ_NEW0(GDataGlobal);
|
||||
if (!global) {
|
||||
goto skip;
|
||||
}
|
||||
global->leaf_type = leaf_type;
|
||||
if (!parse_gdata_global(global, buf, initial_seek)) {
|
||||
RZ_FREE(global);
|
||||
return false;
|
||||
}
|
||||
rz_list_append(s->global_list, global);
|
||||
if (!symbol->data) {
|
||||
free(symbol);
|
||||
continue;
|
||||
}
|
||||
skip:
|
||||
rz_buf_seek(buf, len - sizeof(ut16), RZ_BUF_CUR);
|
||||
rz_pvector_push(s->global_symbols, symbol);
|
||||
}
|
||||
|
||||
return true;
|
||||
err:
|
||||
return false;
|
||||
}
|
||||
|
||||
RZ_IPI void free_gdata_stream(RzPdbGDataStream *stream) {
|
||||
if (!stream) {
|
||||
return;
|
||||
}
|
||||
GDataGlobal *global;
|
||||
RzListIter *it;
|
||||
rz_list_foreach (stream->global_list, it, global) {
|
||||
RZ_FREE(global->name);
|
||||
RZ_FREE(global);
|
||||
}
|
||||
rz_list_free(stream->global_list);
|
||||
rz_pvector_free(stream->global_symbols);
|
||||
free(stream);
|
||||
}
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2021 Basstorm <[email protected]>
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
|
||||
#ifndef PDB_GDATA_H
|
||||
#define PDB_GDATA_H
|
||||
|
||||
#include <rz_util.h>
|
||||
|
||||
typedef struct {
|
||||
ut16 leaf_type;
|
||||
ut32 symtype;
|
||||
ut32 offset;
|
||||
ut16 segment;
|
||||
char *name;
|
||||
ut8 name_len;
|
||||
} GDataGlobal;
|
||||
|
||||
#endif
|
||||
+35
-38
@@ -52,33 +52,16 @@ RZ_IPI void free_omap_stream(RzPdbOmapStream *stream) {
|
||||
free(stream);
|
||||
}
|
||||
|
||||
// inclusive indices
|
||||
// 0 <= imin when using truncate toward zero divide
|
||||
// imid = (imin+imax)/2;
|
||||
// imin unrestricted when using truncate toward minus infinity divide
|
||||
// imid = (imin+imax)>>1; or
|
||||
// imid = (int)floor((imin+imax)/2.0);
|
||||
static int binary_search(unsigned int *A, int key, int imin, int imax) {
|
||||
int imid;
|
||||
|
||||
// continually narrow search until just one element remains
|
||||
while (imin < imax) {
|
||||
imid = (imin + imax) / 2;
|
||||
if (A[imid] < key) {
|
||||
imin = imid + 1;
|
||||
} else {
|
||||
imax = imid;
|
||||
}
|
||||
static int cmp_ut64(const void *pa, const void *pb) {
|
||||
ut64 a = *(ut64 *)pb;
|
||||
ut64 b = *(ut64 *)pa;
|
||||
if (a < b) {
|
||||
return -1;
|
||||
}
|
||||
// At exit of while:
|
||||
// if A[] is empty, then imax < imin
|
||||
// otherwise imax == imin
|
||||
|
||||
// deferred test for equality
|
||||
if ((imax == imin) && (A[imin] == key)) {
|
||||
return imin;
|
||||
if (a > b) {
|
||||
return 1;
|
||||
}
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -88,21 +71,19 @@ static int binary_search(unsigned int *A, int key, int imin, int imax) {
|
||||
* \param address Where to remap
|
||||
* \return int
|
||||
*/
|
||||
RZ_API int rz_bin_pdb_omap_remap(RZ_NONNULL RzPdbOmapStream *omap_stream, int address) {
|
||||
static ut64 pdb_omap_remap(RZ_NONNULL RzPdbOmapStream *omap_stream, ut64 address) {
|
||||
OmapEntry *omap_entry = 0;
|
||||
RzListIter *it = 0;
|
||||
int i = 0;
|
||||
int pos = 0;
|
||||
int len = 0;
|
||||
|
||||
if (!omap_stream) {
|
||||
return address;
|
||||
}
|
||||
|
||||
len = rz_list_length(omap_stream->entries);
|
||||
ut32 len = rz_list_length(omap_stream->entries);
|
||||
|
||||
if (omap_stream->froms == 0) {
|
||||
omap_stream->froms = (unsigned int *)malloc(4 * len);
|
||||
omap_stream->froms = (ut64 *)malloc(sizeof(ut64) * len);
|
||||
if (!omap_stream->froms) {
|
||||
return -1;
|
||||
}
|
||||
@@ -114,17 +95,12 @@ RZ_API int rz_bin_pdb_omap_remap(RZ_NONNULL RzPdbOmapStream *omap_stream, int ad
|
||||
}
|
||||
}
|
||||
|
||||
// mb (len -1) ???
|
||||
pos = binary_search(omap_stream->froms, address, 0, (len));
|
||||
|
||||
if (pos == -1) {
|
||||
const ut64 *p = bsearch(&address, omap_stream->froms, len, sizeof(ut64), cmp_ut64);
|
||||
if (!p) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (omap_stream->froms[pos] != address) {
|
||||
pos -= 1;
|
||||
}
|
||||
omap_entry = (OmapEntry *)rz_list_get_n(omap_stream->entries, pos);
|
||||
omap_entry = (OmapEntry *)rz_list_get_n(omap_stream->entries, *p);
|
||||
if (!omap_entry) {
|
||||
return -1;
|
||||
}
|
||||
@@ -133,3 +109,24 @@ RZ_API int rz_bin_pdb_omap_remap(RZ_NONNULL RzPdbOmapStream *omap_stream, int ad
|
||||
}
|
||||
return omap_entry->to + (address - omap_entry->from);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Convert the section offset to relative virtual address
|
||||
*
|
||||
* \param pdb The PDB reference
|
||||
* \param section_offset The section offset
|
||||
* \return The relative virtual address
|
||||
*/
|
||||
RZ_API ut64 rz_bin_pdb_to_rva(RZ_NONNULL const RzPdb *pdb, const PDBSectionOffset *section_offset) {
|
||||
static const ut64 DEFAULT = UT64_MAX;
|
||||
rz_return_val_if_fail(pdb && pdb->s_pe, DEFAULT);
|
||||
PeImageSectionHeader *section_hdr = pdb_section_hdr_by_index(pdb->s_pe, section_offset->section_index);
|
||||
if (!section_hdr) {
|
||||
return DEFAULT;
|
||||
}
|
||||
ut64 internal_rva = section_offset->offset + section_hdr->virtual_address;
|
||||
if (pdb->s_omap) {
|
||||
return pdb_omap_remap(pdb->s_omap, internal_rva);
|
||||
}
|
||||
return internal_rva;
|
||||
}
|
||||
|
||||
+36
-6
@@ -44,19 +44,19 @@ static bool parse_streams(RzPdb *pdb) {
|
||||
break;
|
||||
case PDB_STREAM_PDB:
|
||||
if (!parse_pdb_stream(pdb, ms)) {
|
||||
RZ_LOG_ERROR("Parse pdb stream failed.");
|
||||
RZ_LOG_ERROR("Parse pdb stream failed.\n");
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case PDB_STREAM_TPI:
|
||||
if (!parse_tpi_stream(pdb, ms)) {
|
||||
RZ_LOG_ERROR("Parse tpi stream failed.");
|
||||
RZ_LOG_ERROR("Parse tpi stream failed.\n");
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case PDB_STREAM_DBI:
|
||||
if (!parse_dbi_stream(pdb, ms)) {
|
||||
RZ_LOG_ERROR("Parse dbi stream failed.");
|
||||
RZ_LOG_ERROR("Parse dbi stream failed.\n");
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
@@ -66,19 +66,19 @@ static bool parse_streams(RzPdb *pdb) {
|
||||
}
|
||||
if (ms->stream_idx == pdb->s_dbi->hdr.sym_record_stream) {
|
||||
if (!parse_gdata_stream(pdb, ms)) {
|
||||
RZ_LOG_ERROR("Parse gdata stream failed.");
|
||||
RZ_LOG_ERROR("Parse gdata stream failed.\n");
|
||||
return false;
|
||||
}
|
||||
} else if (ms->stream_idx == pdb->s_dbi->dbg_hdr.sn_section_hdr ||
|
||||
ms->stream_idx == pdb->s_dbi->dbg_hdr.sn_section_hdr_orig) {
|
||||
if (!parse_pe_stream(pdb, ms)) {
|
||||
RZ_LOG_ERROR("Parse pe stream failed.");
|
||||
RZ_LOG_ERROR("Parse pe stream failed.\n");
|
||||
return false;
|
||||
}
|
||||
} else if (ms->stream_idx == pdb->s_dbi->dbg_hdr.sn_omap_to_src ||
|
||||
ms->stream_idx == pdb->s_dbi->dbg_hdr.sn_omap_from_src) {
|
||||
if (!parse_omap_stream(pdb, ms)) {
|
||||
RZ_LOG_ERROR("Parse omap stream failed.");
|
||||
RZ_LOG_ERROR("Parse omap stream failed.\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -89,6 +89,36 @@ static bool parse_streams(RzPdb *pdb) {
|
||||
return true;
|
||||
}
|
||||
|
||||
RZ_IPI RzPdbMsfStream *pdb_raw_steam(RzPdb *pdb, ut16 index) {
|
||||
if (!(pdb && pdb->streams)) {
|
||||
return NULL;
|
||||
}
|
||||
RzListIter *it;
|
||||
RzPdbMsfStream *ms;
|
||||
rz_list_foreach (pdb->streams, it, ms) {
|
||||
if (ms->stream_idx == index) {
|
||||
return ms;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
RZ_IPI PDBSymbolTable *pdb_global_symbols(RzPdb *pdb) {
|
||||
if (!(pdb && pdb->s_dbi)) {
|
||||
return NULL;
|
||||
}
|
||||
RzPdbMsfStream *steam = pdb_raw_steam(pdb, pdb->s_dbi->hdr.sym_record_stream);
|
||||
if (!steam) {
|
||||
return NULL;
|
||||
}
|
||||
PDBSymbolTable *symbols = RZ_NEW0(PDBSymbolTable);
|
||||
if (!symbols) {
|
||||
return NULL;
|
||||
}
|
||||
symbols->b = steam->stream_data;
|
||||
return symbols;
|
||||
}
|
||||
|
||||
static void msf_stream_free(void *data) {
|
||||
RzPdbMsfStream *msfstream = data;
|
||||
rz_buf_free(msfstream->stream_data);
|
||||
|
||||
+7
-3
@@ -1,15 +1,19 @@
|
||||
// SPDX-FileCopyrightText: 2021 Basstorm <[email protected]>
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
|
||||
#ifndef PDB_PRIVATE_INCLUDE_H_
|
||||
#define PDB_PRIVATE_INCLUDE_H_
|
||||
|
||||
#include <rz_pdb.h>
|
||||
#include "dbi.h"
|
||||
#include "gdata.h"
|
||||
#include "omap.h"
|
||||
#include "stream_pe.h"
|
||||
#include "tpi.h"
|
||||
#include "symbol.h"
|
||||
|
||||
RZ_IPI RzPdbMsfStream *pdb_raw_steam(RzPdb *pdb, ut16 index);
|
||||
RZ_IPI PDBSymbolTable *pdb_global_symbols(RzPdb *pdb);
|
||||
|
||||
#ifndef PDB_PRIVATE_INCLUDE_H_
|
||||
#define PDB_PRIVATE_INCLUDE_H_
|
||||
// OMAP
|
||||
RZ_IPI bool parse_omap_stream(RzPdb *pdb, RzPdbMsfStream *stream);
|
||||
RZ_IPI void free_omap_stream(RzPdbOmapStream *stream);
|
||||
|
||||
@@ -26,4 +26,8 @@ typedef struct {
|
||||
ut32 charactestics;
|
||||
} PeImageSectionHeader;
|
||||
|
||||
static inline PeImageSectionHeader *pdb_section_hdr_by_index(RZ_NONNULL const RzPdbPeStream *s, ut64 index) {
|
||||
return rz_list_get_n(s->sections_hdrs, index - 1);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,182 @@
|
||||
// SPDX-FileCopyrightText: 2023 billow <[email protected]>
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
|
||||
#include "symbol.h"
|
||||
|
||||
#define map_err(x) \
|
||||
if (!(x)) { \
|
||||
goto err; \
|
||||
}
|
||||
|
||||
RZ_IPI bool PDBSectionOffset_parse(RzBuffer *b, PDBSectionOffset *section_offset) {
|
||||
if (!section_offset) {
|
||||
return false;
|
||||
}
|
||||
ut32 offset = 0;
|
||||
ut16 section_index = 0;
|
||||
if (!(rz_buf_read_le32(b, &offset) &&
|
||||
rz_buf_read_le16(b, §ion_index))) {
|
||||
return false;
|
||||
}
|
||||
section_offset->section_index = section_index;
|
||||
section_offset->offset = offset;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool symbol_name_parse(RzBuffer *b, PDBSymbolKind kind, char **result) {
|
||||
if (kind < S_ST_MAX) {
|
||||
// TODO: pascal
|
||||
} else {
|
||||
return rz_buf_read_string(b, result) > 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
RZ_IPI bool PDBSData_parse(RzBuffer *b, PDBSymbolKind kind, PDBSData *sdata) {
|
||||
if (!sdata) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(rz_buf_read_le32(b, &sdata->type_index) &&
|
||||
PDBSectionOffset_parse(b, &sdata->offset) &&
|
||||
symbol_name_parse(b, kind, &sdata->name))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (kind) {
|
||||
case S_GMANDATA:
|
||||
case S_GMANDATA_ST:
|
||||
sdata->managed = true;
|
||||
sdata->global = true;
|
||||
break;
|
||||
case S_GDATA32:
|
||||
case S_GDATA32_ST:
|
||||
sdata->global = true;
|
||||
break;
|
||||
case S_LMANDATA:
|
||||
case S_LMANDATA_ST:
|
||||
sdata->managed = true;
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
RZ_IPI bool PDBSPublic_parse(RzBuffer *b, PDBSymbolKind kind, PDBSPublic *s) {
|
||||
map_err(b && s);
|
||||
ut32 flags = 0;
|
||||
map_err(rz_buf_read_le32(b, &flags) &&
|
||||
PDBSectionOffset_parse(b, &s->offset) &&
|
||||
symbol_name_parse(b, kind, &s->name));
|
||||
s->code = flags & cvpsfCode;
|
||||
s->function = flags & cvpsfFunction;
|
||||
s->managed = flags & cvpsfManaged;
|
||||
s->msil = flags & cvpsfMSIL;
|
||||
return true;
|
||||
err:
|
||||
return false;
|
||||
}
|
||||
|
||||
RZ_IPI bool PDBSymbol_parse(RzBuffer *b, PDBSymbol *symbol) {
|
||||
if (!symbol) {
|
||||
return false;
|
||||
}
|
||||
map_err(rz_buf_read_le16(b, &symbol->raw_kind));
|
||||
|
||||
switch (symbol->raw_kind) {
|
||||
case S_LDATA32:
|
||||
case S_LDATA32_ST:
|
||||
case S_GDATA32:
|
||||
case S_GDATA32_ST:
|
||||
case S_LMANDATA:
|
||||
case S_LMANDATA_ST:
|
||||
case S_GMANDATA:
|
||||
case S_GMANDATA_ST:
|
||||
symbol->kind = PDB_Data;
|
||||
symbol->data = RZ_NEW0(PDBSData);
|
||||
map_err(symbol->data);
|
||||
map_err(PDBSData_parse(b, symbol->raw_kind, symbol->data));
|
||||
break;
|
||||
case S_PUB32:
|
||||
case S_PUB32_ST:
|
||||
symbol->kind = PDB_Public;
|
||||
symbol->data = RZ_NEW0(PDBSPublic);
|
||||
map_err(symbol->data);
|
||||
map_err(PDBSPublic_parse(b, symbol->raw_kind, symbol->data));
|
||||
break;
|
||||
default:
|
||||
RZ_LOG_DEBUG("unimplemented symbol kind: 0x%" PFMT32x "\n", symbol->raw_kind);
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
err:
|
||||
return false;
|
||||
}
|
||||
|
||||
RZ_IPI bool PDBSymbolTable_iter(PDBSymbolTable *symbol_table, PDBSymbolIter *iter) {
|
||||
map_err(symbol_table && iter);
|
||||
iter->b = rz_buf_new_with_buf(symbol_table->b);
|
||||
return true;
|
||||
err:
|
||||
return false;
|
||||
}
|
||||
|
||||
RZ_IPI PDBSymbol *PDBSymbolTable_symbol_by_index(PDBSymbolTable *symbol_table, PDBSymbolIndex index) {
|
||||
PDBSymbolIter iter = { 0 };
|
||||
map_err(PDBSymbolTable_iter(symbol_table, &iter));
|
||||
map_err(PDBSymbolIter_seek(&iter, index));
|
||||
PDBSymbol *symbol = RZ_NEW0(PDBSymbol);
|
||||
map_err(symbol);
|
||||
map_err(PDBSymbolIter_next(&iter, symbol));
|
||||
|
||||
return symbol;
|
||||
err:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
RZ_IPI bool PDBSymbolIter_next(PDBSymbolIter *iter, PDBSymbol *symbol) {
|
||||
if (!iter) {
|
||||
return false;
|
||||
}
|
||||
RzBuffer *b = NULL;
|
||||
|
||||
while (rz_buf_tell(iter->b) < rz_buf_size(iter->b)) {
|
||||
PDBSymbolIndex index = rz_buf_tell(iter->b);
|
||||
ut16 length = 0;
|
||||
map_err(rz_buf_read_le16(iter->b, &length));
|
||||
if (length < 2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
b = rz_buf_new_slice(iter->b, rz_buf_tell(iter->b), length);
|
||||
map_err(b);
|
||||
rz_buf_seek(iter->b, length, RZ_BUF_CUR);
|
||||
|
||||
if (!symbol) {
|
||||
continue;
|
||||
}
|
||||
symbol->index = index;
|
||||
symbol->length = length;
|
||||
map_err(PDBSymbol_parse(b, symbol));
|
||||
|
||||
switch (symbol->raw_kind) {
|
||||
case S_ALIGN:
|
||||
case S_SKIP:
|
||||
continue;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
err:
|
||||
rz_buf_free(b);
|
||||
return false;
|
||||
}
|
||||
|
||||
RZ_IPI bool PDBSymbolIter_seek(PDBSymbolIter *iter, PDBSymbolIndex index) {
|
||||
map_err(iter);
|
||||
rz_buf_seek(iter->b, index, RZ_BUF_SET);
|
||||
return true;
|
||||
err:
|
||||
return false;
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
// SPDX-FileCopyrightText: 2023 billow <[email protected]>
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
|
||||
#ifndef RIZIN_SYMBOL_H
|
||||
#define RIZIN_SYMBOL_H
|
||||
|
||||
#include "symbol_enum.h"
|
||||
#include <rz_util/rz_buf.h>
|
||||
#include <rz_pdb.h>
|
||||
|
||||
typedef ut16 PDBSymbolKind;
|
||||
typedef ut32 PDBTypeIndex;
|
||||
typedef ut32 PDBSymbolIndex;
|
||||
|
||||
RZ_IPI bool PDBSectionOffset_parse(RzBuffer *b, PDBSectionOffset *section_offset);
|
||||
|
||||
typedef struct {
|
||||
bool global : 1;
|
||||
bool managed : 1;
|
||||
PDBTypeIndex type_index;
|
||||
PDBSectionOffset offset;
|
||||
char *name;
|
||||
} PDBSData;
|
||||
|
||||
RZ_IPI bool PDBSData_parse(RzBuffer *b, PDBSymbolKind kind, PDBSData *sdata);
|
||||
|
||||
typedef struct {
|
||||
bool code : 1;
|
||||
bool function : 1;
|
||||
bool managed : 1;
|
||||
bool msil : 1;
|
||||
PDBSectionOffset offset;
|
||||
char *name;
|
||||
} PDBSPublic;
|
||||
|
||||
RZ_IPI bool PDBSPublic_parse(RzBuffer *b, PDBSymbolKind kind, PDBSPublic *s);
|
||||
|
||||
typedef struct {
|
||||
PDBSymbolIndex index;
|
||||
PDBSymbolKind raw_kind;
|
||||
ut16 length;
|
||||
enum {
|
||||
PDB_ScopeEnd, /// End of a scope, such as a procedure.
|
||||
PDB_ObjName, /// Name of the object file of this module.
|
||||
PDB_RegisterVariable, /// A Register variable.
|
||||
PDB_Constant, /// A constant value.
|
||||
PDB_UserDefinedType, /// A user defined type.
|
||||
PDB_MultiRegisterVariable, /// A Register variable spanning multiple registers.
|
||||
PDB_Data, /// Static data, such as a global variable.
|
||||
PDB_Public, /// A public symbol with a mangled name.
|
||||
PDB_Procedure, /// A procedure, such as a function or method.
|
||||
PDB_ThreadStorage, /// A thread local variable.
|
||||
PDB_CompileFlags, /// Flags used to compile a module.
|
||||
PDB_UsingNamespace, /// A using namespace directive.
|
||||
PDB_ProcedureReference, /// Reference to a [`ProcedureSymbol`].
|
||||
PDB_DataReference, /// Reference to an imported variable.
|
||||
PDB_AnnotationReference, /// Reference to an annotation.
|
||||
PDB_Trampoline, /// Trampoline thunk.
|
||||
PDB_Export, /// An exported symbol.
|
||||
PDB_Local, /// A local symbol in optimized code.
|
||||
PDB_BuildInfo, /// Reference to build information.
|
||||
PDB_InlineSite, /// The callsite of an inlined function.
|
||||
PDB_InlineSiteEnd, /// End of an inline callsite.
|
||||
PDB_ProcedureEnd, /// End of a procedure.
|
||||
PDB_Label, /// A label.
|
||||
PDB_Block, /// A block.
|
||||
PDB_RegisterRelative, /// Data allocated relative to a register.
|
||||
PDB_Thunk, /// A thunk.
|
||||
PDB_SeparatedCode, /// A block of separated code.
|
||||
PDB_DefRange, /// A live range of a variable.
|
||||
PDB_DefRangeSubField, /// A live range of a sub field of a variable.
|
||||
PDB_DefRangeRegister, /// A live range of a register variable.
|
||||
PDB_DefRangeFramePointerRelative, /// A live range of a frame pointer-relative variable.
|
||||
PDB_DefRangeFramePointerRelativeFullScope, /// A frame-pointer variable which is valid in the full scope of the function.
|
||||
PDB_DefRangeSubFieldRegister, /// A live range of a sub field of a register variable.
|
||||
PDB_DefRangeRegisterRelative, /// A live range of a variable related to a register.
|
||||
PDB_BasePointerRelative, /// A base pointer-relative variable.
|
||||
PDB_FrameProcedure, /// Extra frame and proc information.
|
||||
PDB_CallSiteInfo, /// Indirect call site information.
|
||||
} kind;
|
||||
void *data;
|
||||
} PDBSymbol;
|
||||
|
||||
RZ_IPI bool PDBSymbol_parse(RzBuffer *b, PDBSymbol *symbol);
|
||||
|
||||
typedef struct {
|
||||
RzBuffer *b;
|
||||
} PDBSymbolTable;
|
||||
|
||||
typedef struct {
|
||||
RzBuffer *b;
|
||||
} PDBSymbolIter;
|
||||
|
||||
RZ_IPI bool PDBSymbolTable_iter(PDBSymbolTable *symbol_table, PDBSymbolIter *iter);
|
||||
RZ_IPI PDBSymbol *PDBSymbolTable_symbol_by_index(PDBSymbolTable *symbol_table, PDBSymbolIndex index);
|
||||
|
||||
RZ_IPI bool PDBSymbolIter_next(PDBSymbolIter *iter, PDBSymbol *symbol);
|
||||
RZ_IPI bool PDBSymbolIter_seek(PDBSymbolIter *iter, PDBSymbolIndex index);
|
||||
|
||||
#endif // RIZIN_SYMBOL_H
|
||||
@@ -0,0 +1,283 @@
|
||||
// SPDX-FileCopyrightText: 2023 billow <[email protected]>
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
|
||||
#ifndef RIZIN_SYMBOL_CONSTANTS_H
|
||||
#define RIZIN_SYMBOL_CONSTANTS_H
|
||||
|
||||
/// A list of known symbol kinds.
|
||||
/// from: https://github.com/Microsoft/microsoft-pdb/blob/082c5290e5aff028ae84e43affa8be717aa7af73/include/cvinfo.h#L2735
|
||||
typedef enum SYM_ENUM_e {
|
||||
S_COMPILE = 0x0001, // Compile flags symbol
|
||||
S_REGISTER_16t = 0x0002, // Register variable
|
||||
S_CONSTANT_16t = 0x0003, // constant symbol
|
||||
S_UDT_16t = 0x0004, // User defined type
|
||||
S_SSEARCH = 0x0005, // Start Search
|
||||
S_END = 0x0006, // Block, procedure, "with" or thunk end
|
||||
S_SKIP = 0x0007, // Reserve symbol space in $$Symbols table
|
||||
S_CVRESERVE = 0x0008, // Reserved symbol for CV internal use
|
||||
S_OBJNAME_ST = 0x0009, // path to object file name
|
||||
S_ENDARG = 0x000a, // end of argument/return list
|
||||
S_COBOLUDT_16t = 0x000b, // special UDT for cobol that does not symbol pack
|
||||
S_MANYREG_16t = 0x000c, // multiple register variable
|
||||
S_RETURN = 0x000d, // return description symbol
|
||||
S_ENTRYTHIS = 0x000e, // description of this pointer on entry
|
||||
|
||||
S_BPREL16 = 0x0100, // BP-relative
|
||||
S_LDATA16 = 0x0101, // Module-local symbol
|
||||
S_GDATA16 = 0x0102, // Global data symbol
|
||||
S_PUB16 = 0x0103, // a public symbol
|
||||
S_LPROC16 = 0x0104, // Local procedure start
|
||||
S_GPROC16 = 0x0105, // Global procedure start
|
||||
S_THUNK16 = 0x0106, // Thunk Start
|
||||
S_BLOCK16 = 0x0107, // block start
|
||||
S_WITH16 = 0x0108, // with start
|
||||
S_LABEL16 = 0x0109, // code label
|
||||
S_CEXMODEL16 = 0x010a, // change execution model
|
||||
S_VFTABLE16 = 0x010b, // address of virtual function table
|
||||
S_REGREL16 = 0x010c, // register relative address
|
||||
|
||||
S_BPREL32_16t = 0x0200, // BP-relative
|
||||
S_LDATA32_16t = 0x0201, // Module-local symbol
|
||||
S_GDATA32_16t = 0x0202, // Global data symbol
|
||||
S_PUB32_16t = 0x0203, // a public symbol (CV internal reserved)
|
||||
S_LPROC32_16t = 0x0204, // Local procedure start
|
||||
S_GPROC32_16t = 0x0205, // Global procedure start
|
||||
S_THUNK32_ST = 0x0206, // Thunk Start
|
||||
S_BLOCK32_ST = 0x0207, // block start
|
||||
S_WITH32_ST = 0x0208, // with start
|
||||
S_LABEL32_ST = 0x0209, // code label
|
||||
S_CEXMODEL32 = 0x020a, // change execution model
|
||||
S_VFTABLE32_16t = 0x020b, // address of virtual function table
|
||||
S_REGREL32_16t = 0x020c, // register relative address
|
||||
S_LTHREAD32_16t = 0x020d, // local thread storage
|
||||
S_GTHREAD32_16t = 0x020e, // global thread storage
|
||||
S_SLINK32 = 0x020f, // static link for MIPS EH implementation
|
||||
|
||||
S_LPROCMIPS_16t = 0x0300, // Local procedure start
|
||||
S_GPROCMIPS_16t = 0x0301, // Global procedure start
|
||||
|
||||
// if these ref symbols have names following then the names are in ST format
|
||||
S_PROCREF_ST = 0x0400, // Reference to a procedure
|
||||
S_DATAREF_ST = 0x0401, // Reference to data
|
||||
S_ALIGN = 0x0402, // Used for page alignment of symbols
|
||||
|
||||
S_LPROCREF_ST = 0x0403, // Local Reference to a procedure
|
||||
S_OEM = 0x0404, // OEM defined symbol
|
||||
|
||||
// sym records with 32-bit types embedded instead of 16-bit
|
||||
// all have 0x1000 bit set for easy identification
|
||||
// only do the 32-bit target versions since we don't really
|
||||
// care about 16-bit ones anymore.
|
||||
S_TI16_MAX = 0x1000,
|
||||
|
||||
S_REGISTER_ST = 0x1001, // Register variable
|
||||
S_CONSTANT_ST = 0x1002, // constant symbol
|
||||
S_UDT_ST = 0x1003, // User defined type
|
||||
S_COBOLUDT_ST = 0x1004, // special UDT for cobol that does not symbol pack
|
||||
S_MANYREG_ST = 0x1005, // multiple register variable
|
||||
S_BPREL32_ST = 0x1006, // BP-relative
|
||||
S_LDATA32_ST = 0x1007, // Module-local symbol
|
||||
S_GDATA32_ST = 0x1008, // Global data symbol
|
||||
S_PUB32_ST = 0x1009, // a public symbol (CV internal reserved)
|
||||
S_LPROC32_ST = 0x100a, // Local procedure start
|
||||
S_GPROC32_ST = 0x100b, // Global procedure start
|
||||
S_VFTABLE32 = 0x100c, // address of virtual function table
|
||||
S_REGREL32_ST = 0x100d, // register relative address
|
||||
S_LTHREAD32_ST = 0x100e, // local thread storage
|
||||
S_GTHREAD32_ST = 0x100f, // global thread storage
|
||||
|
||||
S_LPROCMIPS_ST = 0x1010, // Local procedure start
|
||||
S_GPROCMIPS_ST = 0x1011, // Global procedure start
|
||||
|
||||
S_FRAMEPROC = 0x1012, // extra frame and proc information
|
||||
S_COMPILE2_ST = 0x1013, // extended compile flags and info
|
||||
|
||||
// new symbols necessary for 16-bit enumerates of IA64 registers
|
||||
// and IA64 specific symbols
|
||||
|
||||
S_MANYREG2_ST = 0x1014, // multiple register variable
|
||||
S_LPROCIA64_ST = 0x1015, // Local procedure start (IA64)
|
||||
S_GPROCIA64_ST = 0x1016, // Global procedure start (IA64)
|
||||
|
||||
// Local symbols for IL
|
||||
S_LOCALSLOT_ST = 0x1017, // local IL sym with field for local slot index
|
||||
S_PARAMSLOT_ST = 0x1018, // local IL sym with field for parameter slot index
|
||||
|
||||
S_ANNOTATION = 0x1019, // Annotation string literals
|
||||
|
||||
// symbols to support managed code debugging
|
||||
S_GMANPROC_ST = 0x101a, // Global proc
|
||||
S_LMANPROC_ST = 0x101b, // Local proc
|
||||
S_RESERVED1 = 0x101c, // reserved
|
||||
S_RESERVED2 = 0x101d, // reserved
|
||||
S_RESERVED3 = 0x101e, // reserved
|
||||
S_RESERVED4 = 0x101f, // reserved
|
||||
S_LMANDATA_ST = 0x1020,
|
||||
S_GMANDATA_ST = 0x1021,
|
||||
S_MANFRAMEREL_ST = 0x1022,
|
||||
S_MANREGISTER_ST = 0x1023,
|
||||
S_MANSLOT_ST = 0x1024,
|
||||
S_MANMANYREG_ST = 0x1025,
|
||||
S_MANREGREL_ST = 0x1026,
|
||||
S_MANMANYREG2_ST = 0x1027,
|
||||
S_MANTYPREF = 0x1028, // Index for type referenced by name from metadata
|
||||
S_UNAMESPACE_ST = 0x1029, // Using namespace
|
||||
|
||||
// Symbols w/ SZ name fields. All name fields contain utf8 encoded strings.
|
||||
S_ST_MAX = 0x1100, // starting point for SZ name symbols
|
||||
|
||||
S_OBJNAME = 0x1101, // path to object file name
|
||||
S_THUNK32 = 0x1102, // Thunk Start
|
||||
S_BLOCK32 = 0x1103, // block start
|
||||
S_WITH32 = 0x1104, // with start
|
||||
S_LABEL32 = 0x1105, // code label
|
||||
S_REGISTER = 0x1106, // Register variable
|
||||
S_CONSTANT = 0x1107, // constant symbol
|
||||
S_UDT = 0x1108, // User defined type
|
||||
S_COBOLUDT = 0x1109, // special UDT for cobol that does not symbol pack
|
||||
S_MANYREG = 0x110a, // multiple register variable
|
||||
S_BPREL32 = 0x110b, // BP-relative
|
||||
S_LDATA32 = 0x110c, // Module-local symbol
|
||||
S_GDATA32 = 0x110d, // Global data symbol
|
||||
S_PUB32 = 0x110e, // a public symbol (CV internal reserved)
|
||||
S_LPROC32 = 0x110f, // Local procedure start
|
||||
S_GPROC32 = 0x1110, // Global procedure start
|
||||
S_REGREL32 = 0x1111, // register relative address
|
||||
S_LTHREAD32 = 0x1112, // local thread storage
|
||||
S_GTHREAD32 = 0x1113, // global thread storage
|
||||
|
||||
S_LPROCMIPS = 0x1114, // Local procedure start
|
||||
S_GPROCMIPS = 0x1115, // Global procedure start
|
||||
S_COMPILE2 = 0x1116, // extended compile flags and info
|
||||
S_MANYREG2 = 0x1117, // multiple register variable
|
||||
S_LPROCIA64 = 0x1118, // Local procedure start (IA64)
|
||||
S_GPROCIA64 = 0x1119, // Global procedure start (IA64)
|
||||
S_LOCALSLOT = 0x111a, // local IL sym with field for local slot index
|
||||
S_SLOT = S_LOCALSLOT, // alias for LOCALSLOT
|
||||
S_PARAMSLOT = 0x111b, // local IL sym with field for parameter slot index
|
||||
|
||||
// symbols to support managed code debugging
|
||||
S_LMANDATA = 0x111c,
|
||||
S_GMANDATA = 0x111d,
|
||||
S_MANFRAMEREL = 0x111e,
|
||||
S_MANREGISTER = 0x111f,
|
||||
S_MANSLOT = 0x1120,
|
||||
S_MANMANYREG = 0x1121,
|
||||
S_MANREGREL = 0x1122,
|
||||
S_MANMANYREG2 = 0x1123,
|
||||
S_UNAMESPACE = 0x1124, // Using namespace
|
||||
|
||||
// ref symbols with name fields
|
||||
S_PROCREF = 0x1125, // Reference to a procedure
|
||||
S_DATAREF = 0x1126, // Reference to data
|
||||
S_LPROCREF = 0x1127, // Local Reference to a procedure
|
||||
S_ANNOTATIONREF = 0x1128, // Reference to an S_ANNOTATION symbol
|
||||
S_TOKENREF = 0x1129, // Reference to one of the many MANPROCSYM's
|
||||
|
||||
// continuation of managed symbols
|
||||
S_GMANPROC = 0x112a, // Global proc
|
||||
S_LMANPROC = 0x112b, // Local proc
|
||||
|
||||
// short, light-weight thunks
|
||||
S_TRAMPOLINE = 0x112c, // trampoline thunks
|
||||
S_MANCONSTANT = 0x112d, // constants with metadata type info
|
||||
|
||||
// native attributed local/parms
|
||||
S_ATTR_FRAMEREL = 0x112e, // relative to virtual frame ptr
|
||||
S_ATTR_REGISTER = 0x112f, // stored in a register
|
||||
S_ATTR_REGREL = 0x1130, // relative to register (alternate frame ptr)
|
||||
S_ATTR_MANYREG = 0x1131, // stored in >1 register
|
||||
|
||||
// Separated code (from the compiler) support
|
||||
S_SEPCODE = 0x1132,
|
||||
|
||||
S_LOCAL_2005 = 0x1133, // defines a local symbol in optimized code
|
||||
S_DEFRANGE_2005 = 0x1134, // defines a single range of addresses in which symbol can be evaluated
|
||||
S_DEFRANGE2_2005 = 0x1135, // defines ranges of addresses in which symbol can be evaluated
|
||||
|
||||
S_SECTION = 0x1136, // A COFF section in a PE executable
|
||||
S_COFFGROUP = 0x1137, // A COFF group
|
||||
S_EXPORT = 0x1138, // A export
|
||||
|
||||
S_CALLSITEINFO = 0x1139, // Indirect call site information
|
||||
S_FRAMECOOKIE = 0x113a, // Security cookie information
|
||||
|
||||
S_DISCARDED = 0x113b, // Discarded by LINK /OPT:REF (experimental, see richards)
|
||||
|
||||
S_COMPILE3 = 0x113c, // Replacement for S_COMPILE2
|
||||
S_ENVBLOCK = 0x113d, // Environment block split off from S_COMPILE2
|
||||
|
||||
S_LOCAL = 0x113e, // defines a local symbol in optimized code
|
||||
S_DEFRANGE = 0x113f, // defines a single range of addresses in which symbol can be evaluated
|
||||
S_DEFRANGE_SUBFIELD = 0x1140, // ranges for a subfield
|
||||
|
||||
S_DEFRANGE_REGISTER = 0x1141, // ranges for en-registered symbol
|
||||
S_DEFRANGE_FRAMEPOINTER_REL = 0x1142, // range for stack symbol.
|
||||
S_DEFRANGE_SUBFIELD_REGISTER = 0x1143, // ranges for en-registered field of symbol
|
||||
S_DEFRANGE_FRAMEPOINTER_REL_FULL_SCOPE = 0x1144, // range for stack symbol span valid full scope of function body, gap might apply.
|
||||
S_DEFRANGE_REGISTER_REL = 0x1145, // range for symbol address as register + offset.
|
||||
|
||||
// S_PROC symbols that reference ID instead of type
|
||||
S_LPROC32_ID = 0x1146,
|
||||
S_GPROC32_ID = 0x1147,
|
||||
S_LPROCMIPS_ID = 0x1148,
|
||||
S_GPROCMIPS_ID = 0x1149,
|
||||
S_LPROCIA64_ID = 0x114a,
|
||||
S_GPROCIA64_ID = 0x114b,
|
||||
|
||||
S_BUILDINFO = 0x114c, // build information.
|
||||
S_INLINESITE = 0x114d, // inlined function callsite.
|
||||
S_INLINESITE_END = 0x114e,
|
||||
S_PROC_ID_END = 0x114f,
|
||||
|
||||
S_DEFRANGE_HLSL = 0x1150,
|
||||
S_GDATA_HLSL = 0x1151,
|
||||
S_LDATA_HLSL = 0x1152,
|
||||
|
||||
S_FILESTATIC = 0x1153,
|
||||
|
||||
#if defined(CC_DP_CXX) && CC_DP_CXX
|
||||
|
||||
S_LOCAL_DPC_GROUPSHARED = 0x1154, // DPC groupshared variable
|
||||
S_LPROC32_DPC = 0x1155, // DPC local procedure start
|
||||
S_LPROC32_DPC_ID = 0x1156,
|
||||
S_DEFRANGE_DPC_PTR_TAG = 0x1157, // DPC pointer tag definition range
|
||||
S_DPC_SYM_TAG_MAP = 0x1158, // DPC pointer tag value to symbol record map
|
||||
|
||||
#endif // CC_DP_CXX
|
||||
|
||||
S_ARMSWITCHTABLE = 0x1159,
|
||||
S_CALLEES = 0x115a,
|
||||
S_CALLERS = 0x115b,
|
||||
S_POGODATA = 0x115c,
|
||||
S_INLINESITE2 = 0x115d, // extended inline site information
|
||||
|
||||
S_HEAPALLOCSITE = 0x115e, // heap allocation site
|
||||
|
||||
S_MOD_TYPEREF = 0x115f, // only generated at link time
|
||||
|
||||
S_REF_MINIPDB = 0x1160, // only generated at link time for mini PDB
|
||||
S_PDBMAP = 0x1161, // only generated at link time for mini PDB
|
||||
|
||||
S_GDATA_HLSL32 = 0x1162,
|
||||
S_LDATA_HLSL32 = 0x1163,
|
||||
|
||||
S_GDATA_HLSL32_EX = 0x1164,
|
||||
S_LDATA_HLSL32_EX = 0x1165,
|
||||
|
||||
S_RECTYPE_MAX, // one greater than last
|
||||
S_RECTYPE_LAST = S_RECTYPE_MAX - 1,
|
||||
S_RECTYPE_PAD = S_RECTYPE_MAX + 0x100 // Used *only* to verify symbol record types so that current PDB code can potentially read
|
||||
// future PDBs (assuming no format change, etc).
|
||||
|
||||
} SYM_ENUM_e;
|
||||
|
||||
typedef enum CV_PUBSYMFLAGS_e {
|
||||
cvpsfNone = 0,
|
||||
cvpsfCode = 0x00000001,
|
||||
cvpsfFunction = 0x00000002,
|
||||
cvpsfManaged = 0x00000004,
|
||||
cvpsfMSIL = 0x00000008,
|
||||
} CV_PUBSYMFLAGS_e;
|
||||
|
||||
#endif // RIZIN_SYMBOL_CONSTANTS_H
|
||||
+426
-483
File diff suppressed because it is too large
Load Diff
+125
-62
@@ -6,6 +6,40 @@
|
||||
|
||||
#include <rz_util.h>
|
||||
|
||||
/// enumeration for virtual shape table entries
|
||||
typedef enum {
|
||||
VTS_near = 0x00,
|
||||
VTS_far = 0x01,
|
||||
VTS_thin = 0x02,
|
||||
VTS_outer = 0x03,
|
||||
VTS_meta = 0x04,
|
||||
VTS_near32 = 0x05,
|
||||
VTS_far32 = 0x06,
|
||||
VTS_unused = 0x07
|
||||
} TpiCV_VTS_desc;
|
||||
|
||||
/// enumeration for LF_LABEL address modes
|
||||
typedef enum {
|
||||
LABEL_NEAR = 0, // near return
|
||||
LABEL_FAR = 4 // far return
|
||||
} TpiCV_LABEL_TYPE;
|
||||
|
||||
/// enumeration for HFA kinds
|
||||
typedef enum {
|
||||
CV_HFA_none = 0,
|
||||
CV_HFA_float = 1,
|
||||
CV_HFA_double = 2,
|
||||
CV_HFA_other = 3
|
||||
} TpiCV_HFA;
|
||||
|
||||
/// enumeration for MoCOM UDT kinds
|
||||
typedef enum CV_MOCOM_UDT_e {
|
||||
CV_MOCOM_UDT_none = 0,
|
||||
CV_MOCOM_UDT_ref = 1,
|
||||
CV_MOCOM_UDT_value = 2,
|
||||
CV_MOCOM_UDT_interface = 3
|
||||
} TpiCV_MOCOM_UDT;
|
||||
|
||||
typedef enum {
|
||||
DIRECT = 0, // Not a pointer
|
||||
NEAR_POINTER = 1, // Near pointer
|
||||
@@ -95,6 +129,7 @@ typedef enum {
|
||||
LF_ENDPRECOMP = 0x0014, // type record describing end of precompiled types that can be
|
||||
LF_OEM_16t = 0x0015, // type record for OEM definable type strings
|
||||
LF_TYPESERVER_ST = 0x0016, // type record describing using of a type server
|
||||
|
||||
LF_SKIP_16t = 0x0200,
|
||||
LF_ARGLIST_16t = 0x0201,
|
||||
LF_DEFARG_16t = 0x0202,
|
||||
@@ -108,6 +143,7 @@ typedef enum {
|
||||
LF_DIMVARU_16t = 0x020A, // type record for dimensioned array with variable bounds
|
||||
LF_DIMVARLU_16t = 0x020B, // type record for dimensioned array with variable bounds
|
||||
LF_REFSYM = 0x020C, // type record for referenced symbol
|
||||
|
||||
LF_BCLASS_16t = 0x0400, // subfield record for base class field
|
||||
LF_VBCLASS_16t = 0x0401, // subfield record for direct and indirect virtual base class field
|
||||
LF_IVBCLASS_16t = 0x0402,
|
||||
@@ -122,6 +158,7 @@ typedef enum {
|
||||
LF_FRIENDCLS_16t = 0x040B, // subfield record for friend class
|
||||
LF_ONEMETHOD_16t = 0x040C, // subfield record for nonoverloaded method
|
||||
LF_VFUNCOFF_16t = 0x040D, // subfield record for virtual function table pointer with offset
|
||||
|
||||
LF_TI16_MAX = 0x1000,
|
||||
LF_MODIFIER = 0x1001, // type record for a generalized built-in type modifier
|
||||
LF_POINTER = 0x1002,
|
||||
@@ -140,6 +177,7 @@ typedef enum {
|
||||
LF_OEM = 0x100F, // type record for OEM definable type strings
|
||||
LF_ALIAS_ST = 0x1010,
|
||||
LF_OEM2 = 0x1011, // type record for OEM definable type strings
|
||||
|
||||
LF_SKIP = 0x1200,
|
||||
LF_ARGLIST = 0x1201,
|
||||
LF_DEFARG_ST = 0x1202,
|
||||
@@ -151,6 +189,7 @@ typedef enum {
|
||||
LF_DIMCONLU = 0x1208, // type record for dimensioned array with constant bounds
|
||||
LF_DIMVARU = 0x1209, // type record for dimensioned array with variable bounds
|
||||
LF_DIMVARLU = 0x120A, // type record for dimensioned array with variable bounds
|
||||
|
||||
LF_BCLASS = 0x1400, // subfield record for base class field
|
||||
LF_VBCLASS = 0x1401, // subfield record for direct and indirect virtual base class field
|
||||
LF_IVBCLASS = 0x1402,
|
||||
@@ -167,6 +206,7 @@ typedef enum {
|
||||
LF_NESTTYPEEX_ST = 0x140D, // type record for nested (scoped) type definition, with attributes
|
||||
LF_MEMBERMODIFY_ST = 0x140E, // type record for modifications to members
|
||||
LF_MANAGED_ST = 0x140F,
|
||||
|
||||
LF_ST_MAX = 0x1500,
|
||||
LF_TYPESERVER = 0x1501, // type record describing using of a type server
|
||||
LF_ENUMERATE = 0x1502, // subfield record for enumerate
|
||||
@@ -189,6 +229,18 @@ typedef enum {
|
||||
LF_MEMBERMODIFY = 0x1513, // type record for modifications to members
|
||||
LF_MANAGED = 0x1514,
|
||||
LF_TYPESERVER2 = 0x1515, // type record describing using of a type server with v7 (GUID) signatures
|
||||
LF_STRIDED_ARRAY = 0x1516, // same as LF_ARRAY, but with stride between adjacent elements
|
||||
LF_HLSL = 0x1517,
|
||||
LF_MODIFIER_EX = 0x1518,
|
||||
LF_INTERFACE = 0x1519,
|
||||
LF_BINTERFACE = 0x151a,
|
||||
LF_VECTOR = 0x151b,
|
||||
LF_MATRIX = 0x151c,
|
||||
LF_VFTABLE = 0x151d, // a virtual function table
|
||||
LF_ENDOFLEAFRECORD = LF_VFTABLE,
|
||||
LF_TYPE_LAST, // one greater than the last type record
|
||||
LF_TYPE_MAX = LF_TYPE_LAST - 1,
|
||||
|
||||
LF_FUNCTION_ID = 0x1601,
|
||||
LF_MEMBER_FUNCTION_ID = 0x1602,
|
||||
LF_BUILD_INFO = 0x1603,
|
||||
@@ -198,6 +250,8 @@ typedef enum {
|
||||
LF_USER_DEFINED_TYPE_MODULE_SOURCE_AND_LINE = 0x1607,
|
||||
LF_CLASS_19 = 0x1608,
|
||||
LF_STRUCTURE_19 = 0x1609,
|
||||
LF_ID_LAST, // one greater than the last ID record
|
||||
LF_ID_MAX = LF_ID_LAST - 1,
|
||||
|
||||
/** the following are numeric leaves. They are used to indicate the
|
||||
* size of the following variable length data. When the numeric
|
||||
@@ -227,6 +281,8 @@ typedef enum {
|
||||
LF_DECIMAL = 0x8019,
|
||||
LF_DATE = 0x801A,
|
||||
LF_UTF8STRING = 0x801B,
|
||||
LF_REAL16 = 0x801c,
|
||||
|
||||
LF_PAD0 = 0x00F0,
|
||||
LF_PAD1 = 0x00F1,
|
||||
LF_PAD2 = 0x00F2,
|
||||
@@ -247,23 +303,21 @@ typedef enum {
|
||||
LF_MAX = 0xFFFF
|
||||
} TpiLeafType;
|
||||
|
||||
typedef union {
|
||||
struct {
|
||||
ut16 packed : 1; // true if structure is packed
|
||||
ut16 ctor : 1; // true if constructors or destructors present
|
||||
ut16 ovlops : 1; // true if overloaded operators present
|
||||
ut16 isnested : 1; // true if this is a nested class
|
||||
ut16 cnested : 1; // true if this class contains nested types
|
||||
ut16 opassign : 1; // true if overloaded assignment (=)
|
||||
ut16 opcast : 1; // true if casting methods
|
||||
ut16 fwdref : 1; // true if forward reference (incomplete defn)
|
||||
ut16 scoped : 1; // scoped definition
|
||||
ut16 hasuniquename : 1; // true if there is a decorated name following the regular name
|
||||
ut16 sealed : 1; // true if class cannot be used as a base class
|
||||
ut16 hfa : 2; // CV_HFA_e
|
||||
ut16 intrinsic : 1; // true if class is an intrinsic type (e.g. __m128d)
|
||||
ut16 mocom : 2; // CV_MOCOM_UDT_e
|
||||
} bits;
|
||||
typedef struct {
|
||||
ut16 packed : 1; // true if structure is packed
|
||||
ut16 ctor : 1; // true if constructors or destructors present
|
||||
ut16 ovlops : 1; // true if overloaded operators present
|
||||
ut16 isnested : 1; // true if this is a nested class
|
||||
ut16 cnested : 1; // true if this class contains nested types
|
||||
ut16 opassign : 1; // true if overloaded assignment (=)
|
||||
ut16 opcast : 1; // true if casting methods
|
||||
ut16 fwdref : 1; // true if forward reference (incomplete defn)
|
||||
ut16 scoped : 1; // scoped definition
|
||||
ut16 has_uniquename : 1; // true if there is a decorated name following the regular name
|
||||
ut16 sealed : 1; // true if class cannot be used as a base class
|
||||
ut16 hfa : 2; // CV_HFA_e
|
||||
ut16 intrinsic : 1; // true if class is an intrinsic type (e.g. __m128d)
|
||||
ut16 mocom : 2; // CV_MOCOM_UDT_e
|
||||
} TpiCVProperty;
|
||||
|
||||
typedef enum {
|
||||
@@ -284,26 +338,22 @@ typedef enum {
|
||||
AccessMax
|
||||
} TpiCVAccess;
|
||||
|
||||
typedef union {
|
||||
struct {
|
||||
ut16 access : 2; // access protection CV_access_t
|
||||
ut16 mprop : 3; // method properties CV_methodprop_t
|
||||
ut16 pseudo : 1; // compiler generated fcn and does not exist
|
||||
ut16 noinherit : 1; // true if class cannot be inherited
|
||||
ut16 noconstruct : 1; // true if class cannot be constructed
|
||||
ut16 compgenx : 1; // compiler generated fcn and does exist
|
||||
ut16 sealed : 1; // true if method cannot be overridden
|
||||
ut16 unused : 6; // unused
|
||||
} bits;
|
||||
typedef struct {
|
||||
ut16 access : 2; // access protection CV_access_t
|
||||
ut16 mprop : 3; // method properties CV_methodprop_t
|
||||
ut16 pseudo : 1; // compiler generated fcn and does not exist
|
||||
ut16 noinherit : 1; // true if class cannot be inherited
|
||||
ut16 noconstruct : 1; // true if class cannot be constructed
|
||||
ut16 compgenx : 1; // compiler generated fcn and does exist
|
||||
ut16 sealed : 1; // true if method cannot be overridden
|
||||
ut16 unused : 6; // unused
|
||||
} TpiCVFldattr;
|
||||
|
||||
typedef union {
|
||||
struct cv_funcattr {
|
||||
unsigned char cxxreturnudt : 1; // true if C++ style ReturnUDT
|
||||
unsigned char ctor : 1; // true if func is an instance constructor
|
||||
unsigned char ctorvbase : 1; // true if func is an instance constructor of a class with virtual bases
|
||||
unsigned char unused : 5; // unused
|
||||
} bits;
|
||||
typedef struct cv_funcattr {
|
||||
unsigned char cxxreturnudt : 1; // true if C++ style ReturnUDT
|
||||
unsigned char ctor : 1; // true if func is an instance constructor
|
||||
unsigned char ctorvbase : 1; // true if func is an instance constructor of a class with virtual bases
|
||||
unsigned char unused : 5; // unused
|
||||
} TpiCVFuncattr;
|
||||
|
||||
typedef struct {
|
||||
@@ -333,13 +383,11 @@ typedef struct {
|
||||
ut8 pad;
|
||||
} Tpi_LF_Arglist;
|
||||
|
||||
typedef union {
|
||||
struct {
|
||||
ut16 const_ : 1;
|
||||
ut16 volatile_ : 1;
|
||||
ut16 unaligned : 1;
|
||||
ut16 unused : 13;
|
||||
} bits;
|
||||
typedef struct {
|
||||
ut16 const_ : 1;
|
||||
ut16 volatile_ : 1;
|
||||
ut16 unaligned : 1;
|
||||
ut16 unused : 13;
|
||||
} TpiCVModifier;
|
||||
|
||||
typedef struct {
|
||||
@@ -355,7 +403,7 @@ typedef enum {
|
||||
PTR_MODE_PMFUNC = 0x00000003, // pointer to member function
|
||||
PTR_MODE_RVREF = 0x00000004, // r-value reference
|
||||
PTR_MODE_RESERVED = 0x00000005, // first unused pointer mode
|
||||
ModeMax
|
||||
PTR_MODE_Max
|
||||
} TpiCVPtrMode;
|
||||
|
||||
typedef enum {
|
||||
@@ -385,24 +433,22 @@ typedef enum {
|
||||
PTR_FAR32 = 0x0000000B,
|
||||
PTR_64 = 0x0000000C,
|
||||
PTR_UNUSEDPTR = 0x0000000D,
|
||||
TypeMax
|
||||
PTR_Max
|
||||
} TpiCVType;
|
||||
|
||||
typedef union {
|
||||
struct {
|
||||
ut32 ptrtype : 5; // ordinal specifying pointer type
|
||||
ut32 ptrmode : 3; // ordinal specifying pointer mode
|
||||
ut32 flat32 : 1; // true if 0:32 pointer
|
||||
ut32 volatile_ : 1; // TRUE if volatile pointer
|
||||
ut32 const_ : 1; // TRUE if const pointer
|
||||
ut32 unaligned : 1; // TRUE if unaligned pointer
|
||||
ut32 restrict_ : 1; // TRUE if restricted pointer (allow agressive opts)
|
||||
ut32 size : 6; // size of pointer (in bytes)
|
||||
ut32 mocom : 1; // TRUE if it is a MoCOM pointer (^ or %)
|
||||
ut32 lref : 1; // TRUE if it is this pointer of member function with & ref-qualifier
|
||||
ut32 rref : 1; // TRUE if it is this pointer of member function with && ref-qualifier
|
||||
ut32 unused : 10; // pad out to 32-bits for following cv_typ_t's
|
||||
} bits;
|
||||
typedef struct {
|
||||
ut32 ptrtype : 5; // ordinal specifying pointer type
|
||||
ut32 ptrmode : 3; // ordinal specifying pointer mode
|
||||
ut32 flat32 : 1; // true if 0:32 pointer
|
||||
ut32 volatile_ : 1; // TRUE if volatile pointer
|
||||
ut32 const_ : 1; // TRUE if const pointer
|
||||
ut32 unaligned : 1; // TRUE if unaligned pointer
|
||||
ut32 restrict_ : 1; // TRUE if restricted pointer (allow agressive opts)
|
||||
ut32 size : 6; // size of pointer (in bytes)
|
||||
ut32 mocom : 1; // TRUE if it is a MoCOM pointer (^ or %)
|
||||
ut32 lref : 1; // TRUE if it is this pointer of member function with & ref-qualifier
|
||||
ut32 rref : 1; // TRUE if it is this pointer of member function with && ref-qualifier
|
||||
ut32 unused : 10; // pad out to 32-bits for following cv_typ_t's
|
||||
} TpiCVPointerAttr;
|
||||
|
||||
typedef struct {
|
||||
@@ -434,6 +480,7 @@ typedef struct {
|
||||
typedef struct {
|
||||
ut32 element_type;
|
||||
ut32 index_type;
|
||||
ut32 stride;
|
||||
Tpi_Type_Numeric size;
|
||||
Tpi_Type_String name;
|
||||
ut8 pad;
|
||||
@@ -487,6 +534,22 @@ typedef struct {
|
||||
ut8 pad;
|
||||
} Tpi_LF_Vtshape;
|
||||
|
||||
/// type record for a virtual function table
|
||||
typedef struct {
|
||||
ut16 leaf; // LF_VFTABLE
|
||||
TpiCVType complete_class; // class/structure that owns the vftable
|
||||
TpiCVType override_vftable; // vftable from which this vftable is derived
|
||||
ut32 vfptr_offset; // offset of the vfptr to this table, relative to the start of the object layout.
|
||||
RzPVector /*<char *>*/ method_names; // array of names.
|
||||
// The first is the name of the vtable.
|
||||
// The others are the names of the methods.
|
||||
} Tpi_LF_Vftable;
|
||||
|
||||
// LF_LABEL
|
||||
typedef struct {
|
||||
TpiCV_LABEL_TYPE mode;
|
||||
} Tpi_LF_Label;
|
||||
|
||||
typedef struct {
|
||||
ut16 count;
|
||||
TpiCVProperty prop;
|
||||
@@ -549,9 +612,9 @@ typedef struct {
|
||||
} Tpi_LF_StaticMember;
|
||||
|
||||
typedef struct {
|
||||
TpiCVFldattr fldattr;
|
||||
ut16 leaf;
|
||||
ut16 pad0;
|
||||
ut32 index;
|
||||
ut8 pad;
|
||||
} Tpi_LF_Index;
|
||||
|
||||
typedef struct {
|
||||
@@ -578,7 +641,7 @@ typedef struct {
|
||||
} Tpi_LF_VBClass, Tpi_LF_IVBClass;
|
||||
|
||||
typedef struct {
|
||||
RzList /*<RzPdbTpiType *>*/ *substructs;
|
||||
RzPVector /*<RzPdbTpiType *>*/ *substructs;
|
||||
} Tpi_LF_FieldList;
|
||||
|
||||
typedef struct {
|
||||
|
||||
+1
-2
@@ -5276,12 +5276,11 @@ RZ_API bool rz_core_bin_pdb_load(RZ_NONNULL RzCore *core, RZ_NONNULL const char
|
||||
}
|
||||
rz_bin_pdb_free(pdb);
|
||||
const char *buf = rz_cons_get_buffer();
|
||||
rz_cons_pop();
|
||||
if (!buf) {
|
||||
rz_cons_pop();
|
||||
return false;
|
||||
}
|
||||
char *s = strdup(buf);
|
||||
rz_cons_pop();
|
||||
if (!s) {
|
||||
return false;
|
||||
}
|
||||
|
||||
+71
-43
@@ -18,7 +18,6 @@ static void pdb_types_print_standard(const RzTypeDB *db, const RzPdb *pdb, const
|
||||
}
|
||||
rz_cons_print(rz_strbuf_get(buf));
|
||||
rz_strbuf_free(buf);
|
||||
return;
|
||||
}
|
||||
|
||||
static void pdb_types_print_json(const RzTypeDB *db, const RzPdb *pdb, const RzList /*<RzBaseType *>*/ *types, PJ *pj) {
|
||||
@@ -42,7 +41,7 @@ static void pdb_types_print_json(const RzTypeDB *db, const RzPdb *pdb, const RzL
|
||||
pj_ks(pj, "member_type", typ);
|
||||
RZ_FREE(typ);
|
||||
pj_ks(pj, "member_name", memb->name);
|
||||
pj_kN(pj, "offset", memb->offset);
|
||||
pj_kn(pj, "offset", memb->offset);
|
||||
pj_end(pj);
|
||||
}
|
||||
pj_end(pj);
|
||||
@@ -62,7 +61,7 @@ static void pdb_types_print_json(const RzTypeDB *db, const RzPdb *pdb, const RzL
|
||||
pj_ks(pj, "member_type", typ);
|
||||
RZ_FREE(typ);
|
||||
pj_ks(pj, "member_name", memb->name);
|
||||
pj_kN(pj, "offset", memb->offset);
|
||||
pj_kn(pj, "offset", memb->offset);
|
||||
pj_end(pj);
|
||||
}
|
||||
pj_end(pj);
|
||||
@@ -94,7 +93,6 @@ static void pdb_types_print_json(const RzTypeDB *db, const RzPdb *pdb, const RzL
|
||||
}
|
||||
pj_end(pj);
|
||||
pj_end(pj);
|
||||
return;
|
||||
}
|
||||
|
||||
static void rz_core_bin_pdb_types_print(const RzTypeDB *db, const RzPdb *pdb, const RzCmdStateOutput *state) {
|
||||
@@ -127,13 +125,8 @@ static void rz_core_bin_pdb_types_print(const RzTypeDB *db, const RzPdb *pdb, co
|
||||
*/
|
||||
RZ_API char *rz_core_bin_pdb_gvars_as_string(RZ_NONNULL const RzPdb *pdb, const ut64 img_base, PJ *pj, const RzOutputMode mode) {
|
||||
rz_return_val_if_fail(pdb, NULL);
|
||||
PeImageSectionHeader *sctn_header = 0;
|
||||
RzPdbGDataStream *gsym_data_stream = 0;
|
||||
RzPdbPeStream *pe_stream = 0;
|
||||
RzPdbOmapStream *omap_stream;
|
||||
GDataGlobal *gdata = 0;
|
||||
RzListIter *it = 0;
|
||||
char *name;
|
||||
RzStrBuf *buf = rz_strbuf_new(NULL);
|
||||
if (!buf) {
|
||||
return NULL;
|
||||
@@ -144,29 +137,43 @@ RZ_API char *rz_core_bin_pdb_gvars_as_string(RZ_NONNULL const RzPdb *pdb, const
|
||||
}
|
||||
gsym_data_stream = pdb->s_gdata;
|
||||
pe_stream = pdb->s_pe;
|
||||
omap_stream = pdb->s_omap;
|
||||
if (!pe_stream) {
|
||||
rz_strbuf_free(buf);
|
||||
return NULL;
|
||||
}
|
||||
rz_list_foreach (gsym_data_stream->global_list, it, gdata) {
|
||||
sctn_header = rz_list_get_n(pe_stream->sections_hdrs, (gdata->segment - 1));
|
||||
if (sctn_header) {
|
||||
name = rz_demangler_msvc(gdata->name, RZ_DEMANGLER_FLAG_BASE);
|
||||
name = (name) ? name : strdup(gdata->name);
|
||||
void **it;
|
||||
rz_pvector_foreach (gsym_data_stream->global_symbols, it) {
|
||||
PDBSymbol *symbol = *it;
|
||||
if (symbol->kind == PDB_Public) {
|
||||
PDBSPublic *public = symbol->data;
|
||||
PeImageSectionHeader *sctn_header = pdb_section_hdr_by_index(pdb->s_pe, public->offset.section_index);
|
||||
if (!sctn_header) {
|
||||
continue;
|
||||
}
|
||||
ut64 addr = rz_bin_pdb_to_rva(pdb, &public->offset);
|
||||
if (addr == UT64_MAX) {
|
||||
continue;
|
||||
}
|
||||
if (img_base != UT64_MAX) {
|
||||
addr += img_base;
|
||||
}
|
||||
|
||||
char *name = rz_demangler_msvc(public->name, RZ_DEMANGLER_FLAG_BASE);
|
||||
name = (name) ? name : strdup(public->name);
|
||||
|
||||
switch (mode) {
|
||||
case RZ_OUTPUT_MODE_JSON: // JSON
|
||||
pj_o(pj);
|
||||
pj_kN(pj, "address", (img_base + rz_bin_pdb_omap_remap(omap_stream, gdata->offset + sctn_header->virtual_address)));
|
||||
pj_kN(pj, "symtype", gdata->symtype);
|
||||
pj_kn(pj, "address", addr);
|
||||
pj_kN(pj, "symtype", symbol->raw_kind);
|
||||
pj_ks(pj, "section_name", sctn_header->name);
|
||||
pj_ks(pj, "gdata_name", name);
|
||||
pj_end(pj);
|
||||
break;
|
||||
case RZ_OUTPUT_MODE_STANDARD:
|
||||
rz_strbuf_appendf(buf, "0x%08" PFMT64x " %d %.*s %s\n",
|
||||
(ut64)(img_base + rz_bin_pdb_omap_remap(omap_stream, gdata->offset + sctn_header->virtual_address)),
|
||||
gdata->symtype, PDB_SIZEOF_SECTION_NAME, sctn_header->name, name);
|
||||
addr,
|
||||
symbol->raw_kind, PDB_SIZEOF_SECTION_NAME, sctn_header->name, name);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -193,47 +200,69 @@ static void rz_core_bin_pdb_gvars_print(const RzPdb *pdb, const ut64 img_base, c
|
||||
rz_cons_print(str);
|
||||
}
|
||||
free(str);
|
||||
return;
|
||||
}
|
||||
|
||||
static void pdb_set_symbols(const RzCore *core, const RzPdb *pdb, const ut64 img_base, const char *pdbfile) {
|
||||
static void pdb_set_symbols(
|
||||
const RzCore *core, const RzPdb *pdb, const ut64 img_base, const char *pdbfile) {
|
||||
rz_return_if_fail(core && pdb);
|
||||
PeImageSectionHeader *sctn_header = 0;
|
||||
RzPdbGDataStream *gsym_data_stream = 0;
|
||||
RzPdbPeStream *pe_stream = 0;
|
||||
RzPdbOmapStream *omap_stream;
|
||||
GDataGlobal *gdata = 0;
|
||||
RzListIter *it = 0;
|
||||
char *name;
|
||||
char *filtered_name;
|
||||
gsym_data_stream = pdb->s_gdata;
|
||||
pe_stream = pdb->s_pe;
|
||||
omap_stream = pdb->s_omap;
|
||||
if (!pe_stream) {
|
||||
if (!(pdb->s_pe && pdb->s_gdata)) {
|
||||
return;
|
||||
}
|
||||
RzDemanglerFlag dflags = rz_demangler_get_flags(core->bin->demangler);
|
||||
char *file = rz_str_replace(strdup(pdbfile), ".pdb", "", 0);
|
||||
rz_flag_space_push(core->flags, RZ_FLAGS_FS_SYMBOLS);
|
||||
rz_list_foreach (gsym_data_stream->global_list, it, gdata) {
|
||||
sctn_header = rz_list_get_n(pe_stream->sections_hdrs, (gdata->segment - 1));
|
||||
if (sctn_header) {
|
||||
name = rz_demangler_msvc(gdata->name, dflags);
|
||||
name = (name) ? name : strdup(gdata->name);
|
||||
filtered_name = rz_name_filter2(name, true);
|
||||
|
||||
void **it;
|
||||
rz_pvector_foreach (pdb->s_gdata->global_symbols, it) {
|
||||
PDBSymbol *symbol = *it;
|
||||
if (symbol->kind == PDB_Public) {
|
||||
PDBSPublic *public = symbol->data;
|
||||
|
||||
char *name = rz_demangler_msvc(public->name, dflags);
|
||||
name = (name) ? name : strdup(public->name);
|
||||
char *filtered_name = rz_name_filter2(name, true);
|
||||
char *fname = rz_str_newf("pdb.%s.%s", file, filtered_name);
|
||||
ut64 addr = (ut64)(img_base + rz_bin_pdb_omap_remap(omap_stream, gdata->offset + sctn_header->virtual_address));
|
||||
|
||||
ut64 addr = rz_bin_pdb_to_rva(pdb, &public->offset);
|
||||
if (addr == UT64_MAX) {
|
||||
continue;
|
||||
}
|
||||
if (img_base != UT64_MAX) {
|
||||
addr += img_base;
|
||||
}
|
||||
|
||||
RzFlagItem *item = rz_flag_set(core->flags, fname, addr, 0);
|
||||
if (item) {
|
||||
rz_flag_item_set_realname(item, name);
|
||||
}
|
||||
free(filtered_name);
|
||||
free(name);
|
||||
} else if (symbol->kind == PDB_Data) {
|
||||
const PDBSData *data = symbol->data;
|
||||
if (!data->global) {
|
||||
continue;
|
||||
}
|
||||
ut64 addr = rz_bin_pdb_to_rva(pdb, &data->offset);
|
||||
if (addr == UT64_MAX) {
|
||||
continue;
|
||||
}
|
||||
if (img_base != UT64_MAX) {
|
||||
addr += img_base;
|
||||
}
|
||||
|
||||
RzPdbTpiType *t = rz_bin_pdb_get_type_by_index(pdb->s_tpi, data->type_index);
|
||||
if (!t) {
|
||||
continue;
|
||||
}
|
||||
RzType *rt = rz_type_db_pdb_parse(core->analysis->typedb, pdb->s_tpi, t);
|
||||
if (!rt) {
|
||||
continue;
|
||||
}
|
||||
rz_analysis_var_global_create(core->analysis, data->name, rt, addr);
|
||||
}
|
||||
}
|
||||
rz_flag_space_pop(core->flags);
|
||||
free(file);
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -258,7 +287,7 @@ RZ_API RzPdb *rz_core_pdb_load_info(RZ_NONNULL RzCore *core, RZ_NONNULL const ch
|
||||
}
|
||||
|
||||
// Save compound types into types database
|
||||
rz_parse_pdb_types(core->analysis->typedb, pdb);
|
||||
rz_type_db_pdb_load(core->analysis->typedb, pdb);
|
||||
pdb_set_symbols(core, pdb, baddr, rz_file_basename(file));
|
||||
return pdb;
|
||||
}
|
||||
@@ -285,5 +314,4 @@ RZ_API void rz_core_pdb_info_print(RZ_NONNULL RzCore *core, RZ_NONNULL RzTypeDB
|
||||
rz_core_bin_pdb_types_print(core->analysis->typedb, pdb, state);
|
||||
rz_core_bin_pdb_gvars_print(pdb, baddr, state);
|
||||
rz_cmd_state_output_array_end(state);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2282,7 +2282,8 @@ RZ_API RZ_OWN RzList /*<RzType *>*/ *rz_analysis_types_from_fcn(RzAnalysis *anal
|
||||
RZ_API RZ_OWN RzCallable *rz_analysis_function_derive_type(RzAnalysis *analysis, RzAnalysisFunction *f);
|
||||
|
||||
/* PDB */
|
||||
RZ_API void rz_parse_pdb_types(const RzTypeDB *typedb, const RzPdb *pdb);
|
||||
RZ_API RzType *rz_type_db_pdb_parse(const RzTypeDB *typedb, RzPdbTpiStream *stream, RzPdbTpiType *type);
|
||||
RZ_API void rz_type_db_pdb_load(const RzTypeDB *typedb, const RzPdb *pdb);
|
||||
|
||||
/* DWARF */
|
||||
RZ_API void rz_analysis_dwarf_preprocess_info(
|
||||
|
||||
+12
-7
@@ -65,14 +65,14 @@ typedef struct dbi_stream_t {
|
||||
|
||||
// GDATA
|
||||
typedef struct {
|
||||
RzList /*<GDataGlobal *>*/ *global_list;
|
||||
RzPVector /*<GDataGlobal *>*/ *global_symbols;
|
||||
} RzPdbGDataStream;
|
||||
|
||||
// OMAP
|
||||
typedef struct
|
||||
{
|
||||
RzList /*<OmapEntry *>*/ *entries;
|
||||
ut32 *froms;
|
||||
ut64 *froms;
|
||||
} RzPdbOmapStream;
|
||||
|
||||
// PE Stream
|
||||
@@ -143,10 +143,10 @@ typedef struct tpi_stream_header_t {
|
||||
|
||||
typedef struct tpi_types {
|
||||
RBNode rb;
|
||||
ut32 type_index;
|
||||
ut16 leaf_type;
|
||||
ut32 index;
|
||||
ut16 leaf;
|
||||
ut16 length;
|
||||
void *type_data;
|
||||
void *data;
|
||||
bool parsed;
|
||||
} RzPdbTpiType;
|
||||
|
||||
@@ -250,6 +250,11 @@ typedef struct rz_pdb_t {
|
||||
RzPdbPeStream *s_pe;
|
||||
} RzPdb;
|
||||
|
||||
typedef struct {
|
||||
ut32 offset;
|
||||
ut16 section_index;
|
||||
} PDBSectionOffset;
|
||||
|
||||
// PDB
|
||||
RZ_API bool rz_bin_pdb_extract_in_folder(RZ_NONNULL const char *file_cab, RZ_NONNULL const char *output_dir);
|
||||
RZ_API RZ_OWN RzPdb *rz_bin_pdb_parse_from_file(RZ_NONNULL const char *filename);
|
||||
@@ -260,12 +265,12 @@ RZ_API void rz_bin_pdb_free(RzPdb *pdb);
|
||||
RZ_API RZ_BORROW RzPdbTpiType *rz_bin_pdb_get_type_by_index(RZ_NONNULL RzPdbTpiStream *stream, ut32 index);
|
||||
RZ_API RZ_OWN char *rz_bin_pdb_calling_convention_as_string(RZ_NONNULL RzPdbTpiCallingConvention idx);
|
||||
RZ_API bool rz_bin_pdb_type_is_fwdref(RZ_NONNULL RzPdbTpiType *t);
|
||||
RZ_API RZ_BORROW RzList /*<RzPdbTpiType *>*/ *rz_bin_pdb_get_type_members(RZ_NONNULL RzPdbTpiStream *stream, RzPdbTpiType *t);
|
||||
RZ_API RZ_BORROW RzPVector /*<RzPdbTpiType *>*/ *rz_bin_pdb_get_type_members(RZ_NONNULL RzPdbTpiStream *stream, RzPdbTpiType *t);
|
||||
RZ_API RZ_BORROW char *rz_bin_pdb_get_type_name(RZ_NONNULL RzPdbTpiType *type);
|
||||
RZ_API ut64 rz_bin_pdb_get_type_val(RZ_NONNULL RzPdbTpiType *type);
|
||||
|
||||
// OMAP
|
||||
RZ_API int rz_bin_pdb_omap_remap(RZ_NONNULL RzPdbOmapStream *omap_stream, int address);
|
||||
RZ_API ut64 rz_bin_pdb_to_rva(RZ_NONNULL const RzPdb *pdb, const PDBSectionOffset *section_offset);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -107,6 +107,7 @@ RZ_API RZ_OWN RzBuffer *rz_buf_new_with_string(RZ_NONNULL const char *msg);
|
||||
|
||||
RZ_API RZ_OWN char *rz_buf_get_nstring(RZ_NONNULL RzBuffer *b, ut64 addr, size_t size);
|
||||
RZ_API RZ_OWN char *rz_buf_get_string(RZ_NONNULL RzBuffer *b, ut64 addr);
|
||||
RZ_API ut64 rz_buf_read_string(RZ_NONNULL RzBuffer *b, RZ_BORROW RZ_NULLABLE char **s);
|
||||
RZ_API RZ_OWN char *rz_buf_to_string(RZ_NONNULL RzBuffer *b);
|
||||
RZ_API RzBuffer *rz_buf_ref(RzBuffer *b);
|
||||
RZ_API bool rz_buf_append_buf(RZ_NONNULL RzBuffer *b, RZ_NONNULL RzBuffer *a);
|
||||
@@ -349,6 +350,35 @@ DEFINE_RZ_BUF_WRITE_OFFSET_BLE(64)
|
||||
#undef DEFINE_RZ_BUF_READ_OFFSET_BLE
|
||||
#undef DEFINE_RZ_BUF_WRITE_OFFSET_BLE
|
||||
|
||||
/**
|
||||
* \brief Peeks at the next byte in the buffer without modify the buffer position.
|
||||
*
|
||||
* It assumes that the buffer contains at least one byte beyond the current position.
|
||||
* otherwise, the behavior of the function is undefined.
|
||||
* \param b The buffer
|
||||
* \return The byte value
|
||||
*/
|
||||
static inline ut8 rz_buf_peek(RZ_NONNULL RzBuffer *b) {
|
||||
ut8 x = 0;
|
||||
rz_buf_read8_at(b, rz_buf_tell(b), &x);
|
||||
return x;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Peeks at the next two bytes in the buffer as a little-endian
|
||||
* unsigned 16-bit integer without modify the buffer position.
|
||||
*
|
||||
* It assumes that the buffer contains at least two bytes beyond the current position,
|
||||
* otherwise the behavior of the function is undefined.
|
||||
* \param b The buffer
|
||||
* \return The ut16 value
|
||||
*/
|
||||
static inline ut8 rz_buf_peek_u16(RZ_NONNULL RzBuffer *b) {
|
||||
ut16 x = 0;
|
||||
rz_buf_read_le16_at(b, rz_buf_tell(b), &x);
|
||||
return x;
|
||||
}
|
||||
|
||||
// sparse-specific
|
||||
|
||||
RZ_API const RzBufferSparseChunk *rz_buf_sparse_get_chunks(RzBuffer *b, RZ_NONNULL size_t *count);
|
||||
|
||||
@@ -627,6 +627,30 @@ RZ_API RZ_OWN char *rz_buf_get_string(RZ_NONNULL RzBuffer *b, ut64 addr) {
|
||||
return rz_buf_get_nstring(b, addr, rz_buf_size(b));
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Get a string from the buffer
|
||||
* \param b RzBuffer pointer
|
||||
* \param s The pointer to output the string
|
||||
* \return Length of the string
|
||||
*
|
||||
* Return an heap-allocated string read from \p b. The length depends on
|
||||
* the first '\0' found in the buffer. If there is no '\0' in
|
||||
* the buffer, there is no string, thus 0 is returned.
|
||||
*/
|
||||
RZ_API ut64 rz_buf_read_string(RZ_NONNULL RzBuffer *b, RZ_BORROW RZ_NULLABLE char **s) {
|
||||
rz_return_val_if_fail(b, 0);
|
||||
char *tmp = rz_buf_get_string(b, rz_buf_tell(b));
|
||||
if (!tmp) {
|
||||
return 0;
|
||||
}
|
||||
ut64 string_len = strlen(tmp) + 1;
|
||||
rz_buf_seek(b, string_len, RZ_BUF_CUR);
|
||||
if (s) {
|
||||
*s = tmp;
|
||||
}
|
||||
return string_len;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Stringify the buffer
|
||||
* \param b RzBuffer pointer
|
||||
|
||||
+6
-6
@@ -21,7 +21,7 @@ e asm.bits
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
32
|
||||
0x00401000 2 .text void __cdecl SomeCoolFunction(void)
|
||||
0x00401000 4366 .text void __cdecl SomeCoolFunction(void)
|
||||
32
|
||||
EOF
|
||||
RUN
|
||||
@@ -125,31 +125,31 @@ EXPECT=<<EOF
|
||||
"gvars": [
|
||||
{
|
||||
"address": 12292,
|
||||
"symtype": 0,
|
||||
"symtype": 4366,
|
||||
"section_name": ".data",
|
||||
"gdata_name": "_uninitialized_global"
|
||||
},
|
||||
{
|
||||
"address": 12288,
|
||||
"symtype": 0,
|
||||
"symtype": 4366,
|
||||
"section_name": ".data",
|
||||
"gdata_name": "_initialized_global"
|
||||
},
|
||||
{
|
||||
"address": 4096,
|
||||
"symtype": 2,
|
||||
"symtype": 4366,
|
||||
"section_name": ".text",
|
||||
"gdata_name": "_function"
|
||||
},
|
||||
{
|
||||
"address": 4182,
|
||||
"symtype": 2,
|
||||
"symtype": 4366,
|
||||
"section_name": ".text",
|
||||
"gdata_name": "_mainCRTStartup"
|
||||
},
|
||||
{
|
||||
"address": 4120,
|
||||
"symtype": 2,
|
||||
"symtype": 4366,
|
||||
"section_name": ".text",
|
||||
"gdata_name": "_main"
|
||||
}
|
||||
|
||||
+126
-2
@@ -1,3 +1,127 @@
|
||||
NAME=Load global variables from PDB
|
||||
FILE=
|
||||
CMDS=<<EOF
|
||||
idp bins/pdb/Project1.pdb
|
||||
avg
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
global int32_t (*type_0x1028)() (*type_0x1097)(int32_t (*type_0x1028)()) _imp___onexit @ 0x19248
|
||||
global void * __native_startup_lock @ 0x18548
|
||||
global int32_t __native_startup_state @ 0x18558
|
||||
global struct IMAGE_LOAD_CONFIG_DIRECTORY32_2 _load_config_used @ 0x165b0
|
||||
global int32_t __defaultmatherr @ 0x18018
|
||||
global void (*type_0x1093)() [4] __xi_a @ 0x1530c
|
||||
global void (*type_0x1093)() [4] __rtc_tzz @ 0x16df4
|
||||
global void (**type_0x1093)() __onexitend @ 0x1855c
|
||||
global int32_t [20] _RTC_ErrorLevels @ 0x18000
|
||||
global void (*type_0x1093)() [4] __rtc_taa @ 0x16bec
|
||||
global int32_t _commode @ 0x1816c
|
||||
global int32_t __globallocalestatus @ 0x1801c
|
||||
global void (*type_0x1093)() [4] __xi_z @ 0x1571c
|
||||
global uint32_t __security_cookie @ 0x18024
|
||||
global int32_t (*type_0x1450)(char *arg0, const char *arg1) wsprintffp @ 0x1815c
|
||||
global int32_t * _imp___fmode @ 0x19280
|
||||
global void (*type_0x1093)() [4] __rtc_izz @ 0x16ae8
|
||||
global void (*type_0x1093)() [4] __xc_z @ 0x15208
|
||||
global void (**type_0x1093)() __onexitbegin @ 0x1856c
|
||||
global int32_t _fmode @ 0x18178
|
||||
global void (*type_0x1093)() [4] __xc_a @ 0x15000
|
||||
global int32_t * _imp___commode @ 0x1927c
|
||||
global uint32_t __native_dllmain_reason @ 0x18020
|
||||
global void (*type_0x1093)() [4] __rtc_iaa @ 0x168e0
|
||||
global int32_t _newmode @ 0x18174
|
||||
global int32_t _dowildcard @ 0x18170
|
||||
global uint32_t __security_cookie_complement @ 0x18028
|
||||
EOF
|
||||
RUN
|
||||
|
||||
NAME=xrefs to global variables from PDB
|
||||
FILE=bins/pdb/exceptions-test.exe
|
||||
CMDS=<<EOF
|
||||
idp
|
||||
aaa
|
||||
avg
|
||||
echo ---
|
||||
avgx __guard_check_icall_fptr
|
||||
pd 3 @ 0x140007344
|
||||
avgx __scrt_debugger_hook_flag
|
||||
pdr @ fcn.140001366
|
||||
avgx __dyn_tls_init_callback
|
||||
pdr @ fcn.140001316
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
global void * __guard_check_icall_fptr @ 0x140015000
|
||||
global int32_t __favor @ 0x140011bc4
|
||||
global void (*type_0x1079)(void *arg0) __castguard_check_failure_os_handled_fptr @ 0x140011ba8
|
||||
global int32_t __scrt_debugger_hook_flag @ 0x140011ba4
|
||||
global void (*type_0x1001)() [8] __rtc_iaa @ 0x14000ed88
|
||||
global struct _RS5_IMAGE_LOAD_CONFIG_DIRECTORY64 _load_config_used @ 0x14000e2d0
|
||||
global struct __type_info_node __type_info_root_node @ 0x140011b80
|
||||
global void (*type_0x1001)() [8] __rtc_taa @ 0x14000f0b8
|
||||
global void (*type_0x1de8)(void *arg0, uint32_t arg1, void *arg2) __dyn_tls_init_callback @ 0x140011be0
|
||||
global void (*type_0x1001)() [24] std::runtime_error::`vftable' @ 0x14000d808
|
||||
global void (*type_0x1001)() [8] __rtc_izz @ 0x14000efa8
|
||||
global int32_t __scrt_current_native_startup_state @ 0x140011b28
|
||||
global void * __guard_xfg_check_icall_fptr @ 0x140015010
|
||||
global const unsigned char _guard_dispatch_icall_nop @ 0x140009a80
|
||||
global void (*type_0x1001)() [8] __rtc_tzz @ 0x14000f2d8
|
||||
global const int32_t __scrt_ucrt_dll_is_in_use @ 0x140011060
|
||||
global void * __guard_dispatch_icall_fptr @ 0x140015020
|
||||
global void * __guard_xfg_dispatch_icall_fptr @ 0x140015030
|
||||
global int32_t __isa_available @ 0x140011038
|
||||
global int32_t (*type_0x1015)() [8] __xi_a @ 0x14000c330
|
||||
global void (*type_0x1001)() [8] __xc_z @ 0x14000c220
|
||||
global int32_t [20] _RTC_ErrorLevels @ 0x140011018
|
||||
global int32_t (*type_0x1015)() [8] __xi_z @ 0x14000c660
|
||||
global uint64_t __security_cookie @ 0x140011008
|
||||
global void * __CastGuardVftablesEnd @ 0x14000e710
|
||||
global void (*type_0x1001)() [24] std::exception::`vftable' @ 0x14000d530
|
||||
global void (*type_0x1de8)(void *arg0, uint32_t arg1, void *arg2) __dyn_tls_dtor_callback @ 0x140011bc8
|
||||
global void * __CastGuardVftablesStart @ 0x14000e600
|
||||
global void (*type_0x1001)() [8] __xt_z @ 0x14000caa0
|
||||
global void (*type_0x1001)() [8] __xp_z @ 0x14000c880
|
||||
global const unsigned char _guard_xfg_dispatch_icall_nop @ 0x140009aa0
|
||||
global void * __guard_xfg_table_dispatch_icall_fptr @ 0x140015040
|
||||
global int32_t __isa_enabled @ 0x14001103c
|
||||
global int32_t __scrt_default_matherr @ 0x140011034
|
||||
global void (*type_0x1001)() [8] __xt_a @ 0x14000c990
|
||||
global int64_t __memset_nt_iters @ 0x140011048
|
||||
global void * __scrt_native_startup_lock @ 0x140011b30
|
||||
global void (*type_0x1001)() [8] __xc_a @ 0x14000c000
|
||||
global uint64_t __security_cookie_complement @ 0x140011000
|
||||
global int64_t __memcpy_nt_iters @ 0x140011040
|
||||
global void (*type_0x1001)() [8] __xp_a @ 0x14000c770
|
||||
---
|
||||
(nofunc) 0x140007344 [DATA] lea rcx, pdb.exceptions_test.__guard_check_icall_fptr
|
||||
0x140007344 lea rcx, pdb.exceptions_test.__guard_check_icall_fptr ; 0x140015000
|
||||
0x14000734b call fcn.1400011db
|
||||
0x140007350 lea rcx, [0x14000101e]
|
||||
fcn.140001366 0x140006a64 [DATA] mov dword [pdb.exceptions_test.__scrt_debugger_hook_flag], 0
|
||||
; CALL XREFS from fcn.140001398 @ 0x140006aab, 0x140006c04
|
||||
/ fcn.140001366(int64_t arg1);
|
||||
| ; arg int64_t arg1 @ rcx
|
||||
| ; var int64_t var_8h @ stack + 0x8
|
||||
| 0x140001366 jmp pdb.exceptions_test.__crt_debugger_hook
|
||||
| ----------- true: 0x140006a60
|
||||
| ; CODE XREF from fcn.140001366 @ 0x140001366
|
||||
| ;-- __crt_debugger_hook:
|
||||
| 0x140006a60 mov dword [var_8h], ecx ; arg1
|
||||
| 0x140006a64 mov dword [pdb.exceptions_test.__scrt_debugger_hook_flag], 0 ; [0x140011ba4:4]=0
|
||||
\ 0x140006a6e ret
|
||||
|
||||
fcn.140001316 0x140006a40 [DATA] lea rax, pdb.exceptions_test.__dyn_tls_init_callback
|
||||
; CALL XREF from fcn.1400049a0 @ 0x140004a44
|
||||
/ fcn.140001316();
|
||||
| 0x140001316 jmp pdb.exceptions_test.__scrt_get_dyn_tls_init_callback
|
||||
| ----------- true: 0x140006a40
|
||||
| ; CODE XREF from fcn.140001316 @ 0x140001316
|
||||
| ;-- __scrt_get_dyn_tls_init_callback:
|
||||
| 0x140006a40 lea rax, pdb.exceptions_test.__dyn_tls_init_callback ; 0x140011be0
|
||||
\ 0x140006a47 ret
|
||||
|
||||
EOF
|
||||
RUN
|
||||
|
||||
NAME=PDB downloader check
|
||||
FILE=bins/pdb/user32.dll
|
||||
CMDS=<<EOF
|
||||
@@ -251,7 +375,7 @@ NAME=symbol _towlower_internal of arm PDB
|
||||
FILE=bins/pe/hello_world_arm/hello_world_arm_ZiZoO2.pdb
|
||||
CMDS=!rz-bin -P ${RZ_FILE} | grep "_towlower_internal"
|
||||
EXPECT=<<EOF
|
||||
0x00066da4 2 .text _towlower_internal
|
||||
0x00066da4 4366 .text _towlower_internal
|
||||
EOF
|
||||
RUN
|
||||
|
||||
@@ -259,7 +383,7 @@ NAME=complex symbol of arm PDB
|
||||
FILE=bins/pe/hello_world_arm/hello_world_arm_ZiZoO2.pdb
|
||||
CMDS=!rz-bin -P ${RZ_FILE} | grep "south africa"
|
||||
EXPECT=<<EOF
|
||||
0x000820c4 0 .rdata `string'::1484532236::"south africa"
|
||||
0x000820c4 4366 .rdata `string'::1484532236::"south africa"
|
||||
EOF
|
||||
RUN
|
||||
|
||||
|
||||
@@ -1162,31 +1162,31 @@ EXPECT=<<EOF
|
||||
"gvars": [
|
||||
{
|
||||
"address": 12292,
|
||||
"symtype": 0,
|
||||
"symtype": 4366,
|
||||
"section_name": ".data",
|
||||
"gdata_name": "_uninitialized_global"
|
||||
},
|
||||
{
|
||||
"address": 12288,
|
||||
"symtype": 0,
|
||||
"symtype": 4366,
|
||||
"section_name": ".data",
|
||||
"gdata_name": "_initialized_global"
|
||||
},
|
||||
{
|
||||
"address": 4096,
|
||||
"symtype": 2,
|
||||
"symtype": 4366,
|
||||
"section_name": ".text",
|
||||
"gdata_name": "_function"
|
||||
},
|
||||
{
|
||||
"address": 4182,
|
||||
"symtype": 2,
|
||||
"symtype": 4366,
|
||||
"section_name": ".text",
|
||||
"gdata_name": "_mainCRTStartup"
|
||||
},
|
||||
{
|
||||
"address": 4120,
|
||||
"symtype": 2,
|
||||
"symtype": 4366,
|
||||
"section_name": ".text",
|
||||
"gdata_name": "_main"
|
||||
}
|
||||
|
||||
+328
-366
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user