From 92972bcfb2bcfd9471f0f59de4895e80e0ca02ba Mon Sep 17 00:00:00 2001 From: NOT XVilka Date: Sun, 7 Jun 2026 13:58:54 +0800 Subject: [PATCH] librz/arch/pdb: wire LF_BITFIELD members to the bitfield width (#6452) Follow-up to the C bitfield support added in #6439 (#1240, #314): the PDB type parser dropped struct/union bitfield members because an LF_BITFIELD member type resolved to NULL in pdb_type_parse(), so "tc"/"ts"/"tp" on a PDB-loaded type lost every bitfield field. member_parse() now detects a TpiKind_BITFIELD field type, resolves the member to the bitfield's underlying integer (base_type) and reports the bit width (length) via an out-parameter. class_member_parse() and union_member_parse() store it in RzTypeStructMember.size / RzTypeUnionMember.size (the bitfield width in bits, 0 if not a bitfield), matching the convention used by the C and DWARF member paths. Update the PDB type expectations that previously asserted bitfield_typedef had no members: db/cmd/cmd_idp (idpij) and db/tools/rz_bin (rz-bin -Pj) now expect the three resolved "unsigned char" members of minimal.pdb. Co-authored-by: Anton Kochkov --- librz/arch/pdb_process.c | 29 +++++++++++++++++++++++++---- test/db/cmd/cmd_idp | 16 +++++++++++++++- test/db/tools/rz_bin | 2 +- 3 files changed, 41 insertions(+), 6 deletions(-) diff --git a/librz/arch/pdb_process.c b/librz/arch/pdb_process.c index 981e63c0e1..bc8d63e154 100644 --- a/librz/arch/pdb_process.c +++ b/librz/arch/pdb_process.c @@ -17,7 +17,7 @@ static RzType *array_parse(const RzTypeDB *typedb, RzPdbTpiStream *stream, RzPdb static void arglist_parse(const RzTypeDB *typedb, RzPdbTpiStream *stream, RzPdbTpiType *arglist, RzPVector /**/ *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); +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, char *name); static RzType *union_parse(const RzTypeDB *typedb, RzPdbTpiStream *stream, RzPdbTpiType *type); static RzTypeUnionMember *union_member_parse(const RzTypeDB *typedb, RzPdbTpiStream *stream, RzPdbTpiType *type_info); @@ -240,7 +240,7 @@ static RzType *onemethod_parse( return NULL; } -static RzType *member_parse(const RzTypeDB *typedb, RzPdbTpiStream *stream, RzPdbTpiType *type_info, char *name) { +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; @@ -248,6 +248,23 @@ static RzType *member_parse(const RzTypeDB *typedb, RzPdbTpiStream *stream, RzPd 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); } @@ -321,6 +338,7 @@ static RzTypeStructMember *class_member_parse( 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: { @@ -331,7 +349,7 @@ static RzTypeStructMember *class_member_parse( 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); + type = member_parse(typedb, stream, t, name, &bitfield_width); break; } case TpiKind_STMEMBER: { @@ -371,6 +389,7 @@ static RzTypeStructMember *class_member_parse( 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); @@ -512,6 +531,7 @@ static RzTypeUnionMember *union_member_parse(const RzTypeDB *typedb, RzPdbTpiStr 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: { @@ -522,7 +542,7 @@ static RzTypeUnionMember *union_member_parse(const RzTypeDB *typedb, RzPdbTpiStr 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); + type = member_parse(typedb, stream, type_info, name, &bitfield_width); break; } case TpiKind_NESTTYPE: { @@ -547,6 +567,7 @@ static RzTypeUnionMember *union_member_parse(const RzTypeDB *typedb, RzPdbTpiStr 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); diff --git a/test/db/cmd/cmd_idp b/test/db/cmd/cmd_idp index 603e63ee3a..8eb6c29fd8 100644 --- a/test/db/cmd/cmd_idp +++ b/test/db/cmd/cmd_idp @@ -110,7 +110,21 @@ EXPECT=<