Skip to content

Commit fa75724

Browse files
committed
Support bitfields in type system
Also adds support for parsing bitfields in PDB, DWARF and SVD plugins WIP: API needs to be considered more, also need to find type related apis that may need to be rethought.
1 parent 058800a commit fa75724

File tree

10 files changed

+373
-248
lines changed

10 files changed

+373
-248
lines changed

binaryninjaapi.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10890,6 +10890,9 @@ namespace BinaryNinja {
1089010890
uint64_t offset;
1089110891
BNMemberAccess access;
1089210892
BNMemberScope scope;
10893+
uint8_t bitPosition;
10894+
// TODO: See the comment in the core about this.
10895+
uint8_t bitWidth;
1089310896
};
1089410897

1089510898
/*!
@@ -11091,6 +11094,7 @@ namespace BinaryNinja {
1109111094
\return Whether a StructureMember was successfully retrieved
1109211095
*/
1109311096
bool GetMemberByName(const std::string& name, StructureMember& result) const;
11097+
// TODO: GetMember at offset also needs to pass a bit position.
1109411098
bool GetMemberAtOffset(int64_t offset, StructureMember& result) const;
1109511099
bool GetMemberAtOffset(int64_t offset, StructureMember& result, size_t& idx) const;
1109611100
uint64_t GetWidth() const;
@@ -11143,7 +11147,7 @@ namespace BinaryNinja {
1114311147
\return Reference to the StructureBuilder
1114411148
*/
1114511149
StructureBuilder& AddMemberAtOffset(const Confidence<Ref<Type>>& type, const std::string& name, uint64_t offset,
11146-
bool overwriteExisting = true, BNMemberAccess access = NoAccess, BNMemberScope scope = NoScope);
11150+
bool overwriteExisting = true, BNMemberAccess access = NoAccess, BNMemberScope scope = NoScope, uint8_t bitPosition = 0, uint8_t bitWidth = 0);
1114711151

1114811152
/*! RemoveMember removes a member at a specified index
1114911153

binaryninjacore.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2519,6 +2519,8 @@ extern "C"
25192519
uint8_t typeConfidence;
25202520
BNMemberAccess access;
25212521
BNMemberScope scope;
2522+
uint8_t bitPosition;
2523+
uint8_t bitWidth;
25222524
} BNStructureMember;
25232525

25242526
typedef struct BNInheritedStructureMember
@@ -7072,7 +7074,7 @@ extern "C"
70727074
const char* name, BNMemberAccess access, BNMemberScope scope);
70737075
BINARYNINJACOREAPI void BNAddStructureBuilderMemberAtOffset(BNStructureBuilder* s,
70747076
const BNTypeWithConfidence* const type, const char* name, uint64_t offset, bool overwriteExisting,
7075-
BNMemberAccess access, BNMemberScope scope);
7077+
BNMemberAccess access, BNMemberScope scope, uint8_t bitPosition, uint8_t bitWidth);
70767078
BINARYNINJACOREAPI void BNRemoveStructureBuilderMember(BNStructureBuilder* s, size_t idx);
70777079
BINARYNINJACOREAPI void BNReplaceStructureBuilderMember(BNStructureBuilder* s, size_t idx,
70787080
const BNTypeWithConfidence* const type, const char* name, bool overwriteExisting);

plugins/dwarf/dwarf_import/src/types.rs

Lines changed: 59 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ use binaryninja::{
2424
},
2525
};
2626

27-
use gimli::{constants, AttributeValue, DebuggingInformationEntry, Dwarf, Operation, Unit};
27+
use gimli::{constants, AttributeValue, DebuggingInformationEntry, DwAt, Dwarf, Operation, Unit};
2828

2929
use log::{debug, error, warn};
3030

@@ -220,10 +220,6 @@ fn do_structure_parse<R: ReaderType>(
220220
continue;
221221
};
222222

223-
/*
224-
TODO: apply correct member size when that's supported
225-
let child_type_width = get_size_as_u64(child_entry).unwrap_or_else(|| child_type.width());
226-
*/
227223
if let Ok(Some(raw_struct_offset)) =
228224
child_entry.attr(constants::DW_AT_data_member_location)
229225
{
@@ -246,36 +242,65 @@ fn do_structure_parse<R: ReaderType>(
246242
MemberAccess::NoAccess, // TODO : Resolve actual scopes, if possible
247243
MemberScope::NoScope,
248244
);
249-
} else if let Ok(Some(raw_struct_offset_bits)) =
250-
child_entry.attr(constants::DW_AT_data_bit_offset)
251-
{
252-
//TODO: support misaligned offsets when bitwise data structures get in
253-
let Some(struct_offset_bits) = get_attr_as_u64(&raw_struct_offset_bits)
254-
.or_else(|| get_expr_value(unit, raw_struct_offset_bits))
255-
else {
256-
log::warn!(
257-
"Failed to get DW_AT_data_bit_offset for offset {:#x} in unit {:?}",
258-
child_entry.offset().0,
259-
unit.header.offset()
260-
);
261-
continue;
262-
};
263-
264-
structure_builder.insert(
265-
&child_type,
266-
&child_name,
267-
struct_offset_bits / 8,
268-
false,
269-
MemberAccess::NoAccess, // TODO : Resolve actual scopes, if possible
270-
MemberScope::NoScope,
271-
);
272245
} else {
273-
structure_builder.append(
274-
&child_type,
275-
&child_name,
276-
MemberAccess::NoAccess,
277-
MemberScope::NoScope,
278-
);
246+
let select_value =
247+
|e: &DebuggingInformationEntry<R>, attr: DwAt| -> Option<u64> {
248+
get_attr_as_u64(&e.attr(attr).ok()??)
249+
};
250+
251+
// If no byte offset, try the bitfield using DW_AT_bit_offset/DW_AT_data_bit_offset + DW_AT_bit_size
252+
let bit_size = select_value(child_entry, constants::DW_AT_bit_size);
253+
let bit_offset = select_value(child_entry, constants::DW_AT_bit_offset);
254+
let data_bit_offset =
255+
select_value(child_entry, constants::DW_AT_data_bit_offset);
256+
257+
match (bit_size, bit_offset, data_bit_offset) {
258+
(Some(bit_size), Some(bit_offset), _) => {
259+
// Heuristic storage unit bits from the member type width (bytes -> bits). Fallback to 8.
260+
let storage_bits = {
261+
let w = child_type.width();
262+
if w > 0 {
263+
w * 8
264+
} else {
265+
8
266+
}
267+
};
268+
269+
// DW_AT_bit_offset is from the MSB of the storage unit:
270+
// absolute = base_byte_off*8 + storage_bits - (boffs + bit_sz)
271+
// With no base_byte_off available here, treat base as 0.
272+
let total_bit_off = storage_bits.saturating_sub(bit_offset + bit_size);
273+
274+
structure_builder.insert_bitwise(
275+
&child_type,
276+
&child_name,
277+
total_bit_off,
278+
Some(bit_size as u8),
279+
false,
280+
MemberAccess::NoAccess,
281+
MemberScope::NoScope,
282+
);
283+
}
284+
(Some(bit_size), None, Some(data_bit_offset)) => {
285+
structure_builder.insert_bitwise(
286+
&child_type,
287+
&child_name,
288+
data_bit_offset,
289+
Some(bit_size as u8),
290+
false,
291+
MemberAccess::NoAccess,
292+
MemberScope::NoScope,
293+
);
294+
}
295+
_ => {
296+
structure_builder.append(
297+
&child_type,
298+
&child_name,
299+
MemberAccess::NoAccess,
300+
MemberScope::NoScope,
301+
);
302+
}
303+
}
279304
}
280305
}
281306
constants::DW_TAG_inheritance => {
@@ -316,7 +341,6 @@ fn do_structure_parse<R: ReaderType>(
316341

317342
structure_builder.base_structures(&base_structures);
318343
let finalized_structure = Type::structure(&structure_builder.finalize());
319-
320344
if let Some(full_name) = full_name {
321345
debug_info_builder.add_type(
322346
get_uid(dwarf, unit, entry) + 1, // TODO : This is super broke (uid + 1 is not guaranteed to be unique)

plugins/pdb-ng/src/struct_grouper.rs

Lines changed: 59 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -340,8 +340,10 @@ pub fn group_structure(
340340
.enumerate()
341341
.map(|(i, member)| MemberSize {
342342
index: i,
343-
offset: member.offset,
344-
width: member.ty.contents.width(),
343+
offset: member.bitfield_position.unwrap_or(member.offset * 8),
344+
width: member
345+
.bitfield_size
346+
.unwrap_or(member.ty.contents.width() * 8),
345347
})
346348
.collect::<Vec<_>>();
347349

@@ -359,14 +361,29 @@ pub fn group_structure(
359361
Err(e) => {
360362
warn!("{} Could not resolve structure groups: {}", name, e);
361363
for member in members {
362-
structure.insert(
363-
&member.ty,
364-
&member.name,
365-
member.offset,
366-
false,
367-
member.access,
368-
member.scope,
369-
);
364+
match (member.bitfield_position, member.bitfield_size) {
365+
(Some(bit_pos), bit_width) => {
366+
structure.insert_bitwise(
367+
&member.ty,
368+
&member.name,
369+
bit_pos,
370+
bit_width.map(|w| w as u8),
371+
false,
372+
member.access,
373+
member.scope,
374+
);
375+
}
376+
(None, _) => {
377+
structure.insert(
378+
&member.ty,
379+
&member.name,
380+
member.offset,
381+
false,
382+
member.access,
383+
member.scope,
384+
);
385+
}
386+
}
370387
}
371388
}
372389
}
@@ -387,24 +404,38 @@ fn apply_groups(
387404

388405
// TODO : Fix inner-offset being larger than `member.offset`
389406

390-
if offset > member.offset {
391-
structure.insert(
392-
&member.ty,
393-
&member.name,
394-
0,
395-
false,
396-
member.access,
397-
member.scope,
398-
);
399-
} else {
400-
structure.insert(
401-
&member.ty,
402-
&member.name,
403-
member.offset - offset,
404-
false,
405-
member.access,
406-
member.scope,
407-
);
407+
match (member.bitfield_position, member.bitfield_size) {
408+
(Some(bit_pos), bit_width) => {
409+
structure.insert_bitwise(
410+
&member.ty,
411+
&member.name,
412+
bit_pos,
413+
bit_width.map(|w| w as u8),
414+
false,
415+
member.access,
416+
member.scope,
417+
);
418+
}
419+
(None, _) if offset > member.offset => {
420+
structure.insert(
421+
&member.ty,
422+
&member.name,
423+
0,
424+
false,
425+
member.access,
426+
member.scope,
427+
);
428+
}
429+
(None, _) => {
430+
structure.insert(
431+
&member.ty,
432+
&member.name,
433+
member.offset - offset,
434+
false,
435+
member.access,
436+
member.scope,
437+
);
438+
}
408439
}
409440
}
410441
ResolvedGroup::Struct(inner_offset, children) => {

plugins/pdb-ng/src/type_parser.rs

Lines changed: 0 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -848,92 +848,6 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> {
848848
None => {}
849849
}
850850

851-
// Combine bitfields into structures
852-
let mut combined_bitfield_members = vec![];
853-
let mut last_bitfield_offset = u64::MAX;
854-
let mut last_bitfield_pos = u64::MAX;
855-
let mut last_bitfield_idx = 0;
856-
let mut bitfield_builder: Option<StructureBuilder> = None;
857-
858-
fn bitfield_name(offset: u64, idx: u64) -> String {
859-
if idx > 0 {
860-
format!("__bitfield{:x}_{}", offset, idx)
861-
} else {
862-
format!("__bitfield{:x}", offset)
863-
}
864-
}
865-
866-
for m in members {
867-
match (m.bitfield_position, m.bitfield_size) {
868-
(Some(pos), Some(_size)) => {
869-
if last_bitfield_offset != m.offset || last_bitfield_pos >= pos {
870-
if let Some(builder) = bitfield_builder.take() {
871-
combined_bitfield_members.push(ParsedMember {
872-
ty: Conf::new(
873-
Type::structure(builder.finalize().as_ref()),
874-
MAX_CONFIDENCE,
875-
),
876-
name: bitfield_name(last_bitfield_offset, last_bitfield_idx),
877-
offset: last_bitfield_offset,
878-
access: MemberAccess::PublicAccess,
879-
scope: MemberScope::NoScope,
880-
bitfield_size: None,
881-
bitfield_position: None,
882-
});
883-
}
884-
let mut new_builder = StructureBuilder::new();
885-
new_builder.structure_type(StructureType::UnionStructureType);
886-
new_builder.width(m.ty.contents.width());
887-
bitfield_builder = Some(new_builder);
888-
889-
if last_bitfield_offset != m.offset {
890-
last_bitfield_idx = 0;
891-
} else {
892-
last_bitfield_idx += 1;
893-
}
894-
}
895-
896-
last_bitfield_pos = pos;
897-
last_bitfield_offset = m.offset;
898-
bitfield_builder
899-
.as_mut()
900-
.expect("Invariant")
901-
.insert(&m.ty, &m.name, 0, false, m.access, m.scope);
902-
}
903-
(None, None) => {
904-
if let Some(builder) = bitfield_builder.take() {
905-
combined_bitfield_members.push(ParsedMember {
906-
ty: Conf::new(
907-
Type::structure(builder.finalize().as_ref()),
908-
MAX_CONFIDENCE,
909-
),
910-
name: bitfield_name(last_bitfield_offset, last_bitfield_idx),
911-
offset: last_bitfield_offset,
912-
access: MemberAccess::PublicAccess,
913-
scope: MemberScope::NoScope,
914-
bitfield_size: None,
915-
bitfield_position: None,
916-
});
917-
}
918-
last_bitfield_offset = u64::MAX;
919-
last_bitfield_pos = u64::MAX;
920-
combined_bitfield_members.push(m);
921-
}
922-
e => return Err(anyhow!("Unexpected bitfield parameters {:?}", e)),
923-
}
924-
}
925-
if let Some(builder) = bitfield_builder.take() {
926-
combined_bitfield_members.push(ParsedMember {
927-
ty: Conf::new(Type::structure(builder.finalize().as_ref()), MAX_CONFIDENCE),
928-
name: bitfield_name(last_bitfield_offset, last_bitfield_idx),
929-
offset: last_bitfield_offset,
930-
access: MemberAccess::PublicAccess,
931-
scope: MemberScope::NoScope,
932-
bitfield_size: None,
933-
bitfield_position: None,
934-
});
935-
}
936-
members = combined_bitfield_members;
937851
group_structure(
938852
&format!(
939853
"`{}`",

0 commit comments

Comments
 (0)