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 <anton.kochkov@gmail.com>
This commit is contained in:
NOT XVilka 2026-06-07 13:58:54 +08:00 committed by GitHub
parent d723950a62
commit 92972bcfb2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 41 additions and 6 deletions

View file

@ -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 /*<RzCallableArg *>*/ *vec);
static RzType *mfunction_parse(const RzTypeDB *typedb, RzPdbTpiStream *stream, RzPdbTpiType *type_info, char *name);
static RzType *onemethod_parse(const RzTypeDB *typedb, RzPdbTpiStream *stream, RzPdbTpiType *type_info);
static RzType *member_parse(const RzTypeDB *typedb, RzPdbTpiStream *stream, RzPdbTpiType *type_info, char *name);
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);

View file

@ -110,7 +110,21 @@ EXPECT=<<EOF
"name": "bitfield_typedef",
"size": 1,
"members": [
{
"member_type": "unsigned char",
"member_name": "a",
"offset": 0
},
{
"member_type": "unsigned char",
"member_name": "b",
"offset": 0
},
{
"member_type": "unsigned char",
"member_name": "c",
"offset": 0
}
]
},
{

View file

@ -1306,7 +1306,7 @@ FILE=bins/pdb/minimal.pdb
TOOL=rz-bin
ARGS=-Pj
EXPECT=<<EOF
{"pdb":[{"modules":[{"module_name":"* Linker Generated Manifest RES *","object_file_name":""},{"module_name":"C:\\Users\\emuresan\\source\\repos\\minimal\\Release\\main.obj","object_file_name":"C:\\Users\\emuresan\\source\\repos\\minimal\\Release\\main.obj"},{"module_name":"* Linker *","object_file_name":""}]},{"types":[{"type":"structure","name":"struct_typedef","size":7,"members":[{"member_type":"char","member_name":"a","offset":0},{"member_type":"int16_t","member_name":"b","offset":1},{"member_type":"int32_t","member_name":"c","offset":3}]},{"type":"union","name":"type_0x1004","size":4,"members":[{"member_type":"char [4]","member_name":"a","offset":0},{"member_type":"int32_t","member_name":"b","offset":0}]},{"type":"structure","name":"unnamed_member_types_typedef","size":8,"members":[{"member_type":"union type_0x1004","member_name":"a","offset":0},{"member_type":"int32_t","member_name":"b","offset":4}]},{"type":"structure","name":"bitfield_typedef","size":1,"members":[]},{"type":"union","name":"union_typedef","size":4,"members":[{"member_type":"char","member_name":"a","offset":0},{"member_type":"int16_t","member_name":"b","offset":0},{"member_type":"int32_t","member_name":"c","offset":0}]}]},{"gvars":[{"address":12292,"symtype":4366,"section_name":".data","gdata_name":"_uninitialized_global"},{"address":12288,"symtype":4366,"section_name":".data","gdata_name":"_initialized_global"},{"address":4096,"symtype":4366,"section_name":".text","gdata_name":"_function"},{"address":4182,"symtype":4366,"section_name":".text","gdata_name":"_mainCRTStartup"},{"address":4120,"symtype":4366,"section_name":".text","gdata_name":"_main"}]}]}
{"pdb":[{"modules":[{"module_name":"* Linker Generated Manifest RES *","object_file_name":""},{"module_name":"C:\\Users\\emuresan\\source\\repos\\minimal\\Release\\main.obj","object_file_name":"C:\\Users\\emuresan\\source\\repos\\minimal\\Release\\main.obj"},{"module_name":"* Linker *","object_file_name":""}]},{"types":[{"type":"structure","name":"struct_typedef","size":7,"members":[{"member_type":"char","member_name":"a","offset":0},{"member_type":"int16_t","member_name":"b","offset":1},{"member_type":"int32_t","member_name":"c","offset":3}]},{"type":"union","name":"type_0x1004","size":4,"members":[{"member_type":"char [4]","member_name":"a","offset":0},{"member_type":"int32_t","member_name":"b","offset":0}]},{"type":"structure","name":"unnamed_member_types_typedef","size":8,"members":[{"member_type":"union type_0x1004","member_name":"a","offset":0},{"member_type":"int32_t","member_name":"b","offset":4}]},{"type":"structure","name":"bitfield_typedef","size":1,"members":[{"member_type":"unsigned char","member_name":"a","offset":0},{"member_type":"unsigned char","member_name":"b","offset":0},{"member_type":"unsigned char","member_name":"c","offset":0}]},{"type":"union","name":"union_typedef","size":4,"members":[{"member_type":"char","member_name":"a","offset":0},{"member_type":"int16_t","member_name":"b","offset":0},{"member_type":"int32_t","member_name":"c","offset":0}]}]},{"gvars":[{"address":12292,"symtype":4366,"section_name":".data","gdata_name":"_uninitialized_global"},{"address":12288,"symtype":4366,"section_name":".data","gdata_name":"_initialized_global"},{"address":4096,"symtype":4366,"section_name":".text","gdata_name":"_function"},{"address":4182,"symtype":4366,"section_name":".text","gdata_name":"_mainCRTStartup"},{"address":4120,"symtype":4366,"section_name":".text","gdata_name":"_main"}]}]}
EOF
RUN