Skip to content

Commit e562852

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 420d622 commit e562852

File tree

10 files changed

+192
-195
lines changed

10 files changed

+192
-195
lines changed

binaryninjaapi.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10741,6 +10741,9 @@ namespace BinaryNinja {
1074110741
uint64_t offset;
1074210742
BNMemberAccess access;
1074310743
BNMemberScope scope;
10744+
uint8_t bitPosition;
10745+
// TODO: See the comment in the core about this.
10746+
uint8_t bitWidth;
1074410747
};
1074510748

1074610749
/*!
@@ -10942,6 +10945,7 @@ namespace BinaryNinja {
1094210945
\return Whether a StructureMember was successfully retrieved
1094310946
*/
1094410947
bool GetMemberByName(const std::string& name, StructureMember& result) const;
10948+
// TODO: GetMember at offset also needs to pass a bit position.
1094510949
bool GetMemberAtOffset(int64_t offset, StructureMember& result) const;
1094610950
bool GetMemberAtOffset(int64_t offset, StructureMember& result, size_t& idx) const;
1094710951
uint64_t GetWidth() const;
@@ -10994,7 +10998,7 @@ namespace BinaryNinja {
1099410998
\return Reference to the StructureBuilder
1099510999
*/
1099611000
StructureBuilder& AddMemberAtOffset(const Confidence<Ref<Type>>& type, const std::string& name, uint64_t offset,
10997-
bool overwriteExisting = true, BNMemberAccess access = NoAccess, BNMemberScope scope = NoScope);
11001+
bool overwriteExisting = true, BNMemberAccess access = NoAccess, BNMemberScope scope = NoScope, uint8_t bitPosition = 0, uint8_t bitWidth = 0);
1099811002

1099911003
/*! RemoveMember removes a member at a specified index
1100011004

binaryninjacore.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2474,6 +2474,8 @@ extern "C"
24742474
uint8_t typeConfidence;
24752475
BNMemberAccess access;
24762476
BNMemberScope scope;
2477+
uint8_t bitPosition;
2478+
uint8_t bitWidth;
24772479
} BNStructureMember;
24782480

24792481
typedef struct BNInheritedStructureMember
@@ -6955,7 +6957,7 @@ extern "C"
69556957
const char* name, BNMemberAccess access, BNMemberScope scope);
69566958
BINARYNINJACOREAPI void BNAddStructureBuilderMemberAtOffset(BNStructureBuilder* s,
69576959
const BNTypeWithConfidence* const type, const char* name, uint64_t offset, bool overwriteExisting,
6958-
BNMemberAccess access, BNMemberScope scope);
6960+
BNMemberAccess access, BNMemberScope scope, uint8_t bitPosition, uint8_t bitWidth);
69596961
BINARYNINJACOREAPI void BNRemoveStructureBuilderMember(BNStructureBuilder* s, size_t idx);
69606962
BINARYNINJACOREAPI void BNReplaceStructureBuilderMember(BNStructureBuilder* s, size_t idx,
69616963
const BNTypeWithConfidence* const type, const char* name, bool overwriteExisting);

plugins/dwarf/dwarf_import/src/types.rs

Lines changed: 48 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,6 @@ fn do_structure_parse<R: ReaderType>(
205205
continue;
206206
};
207207

208-
// TODO : support DW_AT_data_bit_offset for offset as well
209208
if let Ok(Some(raw_struct_offset)) =
210209
child.entry().attr(constants::DW_AT_data_member_location)
211210
{
@@ -223,12 +222,54 @@ fn do_structure_parse<R: ReaderType>(
223222
MemberScope::NoScope,
224223
);
225224
} else {
226-
structure_builder.append(
227-
&child_type,
228-
&child_name,
229-
MemberAccess::NoAccess,
230-
MemberScope::NoScope,
231-
);
225+
// If no byte offset, try bitfield using DW_AT_bit_offset + DW_AT_bit_size
226+
let bit_size = child
227+
.entry()
228+
.attr(constants::DW_AT_bit_size)
229+
.ok()
230+
.and_then(|a| a)
231+
.and_then(|a| get_attr_as_u64(&a));
232+
233+
let bit_offset = child
234+
.entry()
235+
.attr(constants::DW_AT_bit_offset)
236+
.ok()
237+
.and_then(|a| a)
238+
.and_then(|a| get_attr_as_u64(&a));
239+
240+
if let (Some(bit_sz), Some(boffs)) = (bit_size, bit_offset) {
241+
// Heuristic storage unit bits from the member type width (bytes -> bits). Fallback to 8.
242+
let storage_bits = {
243+
let w = child_type.width() as u64;
244+
if w > 0 {
245+
w * 8
246+
} else {
247+
8
248+
}
249+
};
250+
251+
// DW_AT_bit_offset is from the MSB of the storage unit:
252+
// absolute = base_byte_off*8 + storage_bits - (boffs + bit_sz)
253+
// With no base_byte_off available here, treat base as 0.
254+
let total_bit_off = storage_bits.saturating_sub(boffs + bit_sz);
255+
256+
structure_builder.insert_bitwise(
257+
&child_type,
258+
&child_name,
259+
total_bit_off,
260+
Some(bit_sz as u8),
261+
false,
262+
MemberAccess::NoAccess,
263+
MemberScope::NoScope,
264+
);
265+
} else {
266+
structure_builder.append(
267+
&child_type,
268+
&child_name,
269+
MemberAccess::NoAccess,
270+
MemberScope::NoScope,
271+
);
272+
}
232273
}
233274
}
234275
constants::DW_TAG_inheritance => {

plugins/pdb-ng/src/struct_grouper.rs

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -359,14 +359,29 @@ pub fn group_structure(
359359
Err(e) => {
360360
warn!("{} Could not resolve structure groups: {}", name, e);
361361
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-
);
362+
match (member.bitfield_position, member.bitfield_size) {
363+
(Some(bit_pos), bit_width) => {
364+
structure.insert_bitwise(
365+
&member.ty,
366+
&member.name,
367+
bit_pos,
368+
bit_width.map(|w| w as u8),
369+
false,
370+
member.access,
371+
member.scope,
372+
);
373+
}
374+
(None, _) => {
375+
structure.insert(
376+
&member.ty,
377+
&member.name,
378+
member.offset,
379+
false,
380+
member.access,
381+
member.scope,
382+
);
383+
}
384+
}
370385
}
371386
}
372387
}

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
"`{}`",

plugins/svd/src/mapper.rs

Lines changed: 4 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use binaryninja::segment::{SegmentBuilder, SegmentFlags};
88
use binaryninja::symbol::{SymbolBuilder, SymbolType};
99
use binaryninja::types::{
1010
BaseStructure, EnumerationBuilder, MemberAccess, MemberScope, NamedTypeReference,
11-
NamedTypeReferenceClass, StructureBuilder, StructureMember, StructureType, Type, TypeBuilder,
11+
NamedTypeReferenceClass, StructureBuilder, StructureMember, Type, TypeBuilder,
1212
};
1313
use std::collections::HashMap;
1414
use std::num::NonZeroUsize;
@@ -442,53 +442,12 @@ impl DeviceMapper {
442442

443443
let type_builder = match &register.fields {
444444
Some(fields) => {
445-
// Separate bitfields from regular fields.
446-
let (fields, bitfield_items): (Vec<_>, Vec<_>) =
447-
fields.iter().partition(|f| {
448-
byte_aligned(f.bit_range.width) && byte_aligned(f.bit_range.offset)
449-
});
450-
451445
for field in fields {
452446
let field_member = self.field_member(field);
453447
let overwrites = true; // TODO: Handle overwrites?
454448
register_struct.insert_member(field_member, overwrites);
455449
}
456450

457-
if self.settings.add_bitfields {
458-
// The bitfield items need to be coalesced to a map of byte offset to vec of fields.
459-
let mut bitfield_map: HashMap<u64, Vec<&Field>> = HashMap::new();
460-
461-
// Sort bitfields by their offset
462-
let mut sorted_bitfields = bitfield_items.iter().collect::<Vec<_>>();
463-
sorted_bitfields.sort_by_key(|f| f.bit_range.offset);
464-
465-
// Group bitfields by overlapping bit offsets
466-
let mut current_bit_start = 0;
467-
let mut current_bit_end = 0;
468-
for field in sorted_bitfields {
469-
let bit_start = field.bit_range.offset;
470-
let byte_start = bit_start / 8;
471-
let current_byte_start = current_bit_start / 8;
472-
if current_byte_start != byte_start && current_bit_end < bit_start {
473-
// Make a new bitfield, only if the current field is in a new byte.
474-
current_bit_start = bit_start;
475-
}
476-
current_bit_end = bit_start + field.bit_range.width;
477-
bitfield_map
478-
.entry(current_bit_start as u64)
479-
.or_insert_with(Vec::new)
480-
.push(field);
481-
}
482-
483-
for (bit_start, fields) in bitfield_map {
484-
// Add each bitfield to the structure!
485-
let byte_start = bit_start / 8;
486-
let bitfield_member = self.bitfield_member(byte_start, fields);
487-
let overwrites = true; // TODO: Handle overwrites?
488-
register_struct.insert_member(bitfield_member, overwrites);
489-
}
490-
}
491-
492451
TypeBuilder::structure(&register_struct.finalize())
493452
}
494453
None if register.derived_from.is_some() => {
@@ -559,43 +518,14 @@ impl DeviceMapper {
559518
}
560519
}
561520

562-
pub fn bitfield_member(&self, byte_offset: u64, fields: Vec<&Field>) -> StructureMember {
563-
let field_ty = self.bitfield_type(byte_offset, fields);
564-
// TODO: Create bitfield name from the fields?
565-
let field_name = format!("bitfield_0x{:x}", byte_offset);
566-
// TODO: This should be like 120 confidence?
567-
let conf_field_ty = Conf::new(field_ty, MAX_CONFIDENCE);
568-
StructureMember::new(
569-
conf_field_ty,
570-
field_name,
571-
byte_offset,
572-
MemberAccess::PublicAccess,
573-
MemberScope::NoScope,
574-
)
575-
}
576-
577-
pub fn bitfield_type(&self, byte_offset: u64, fields: Vec<&Field>) -> Ref<Type> {
578-
let mut union_builder = StructureBuilder::new();
579-
union_builder.structure_type(StructureType::UnionStructureType);
580-
for field in fields {
581-
let mut field_member = self.field_member(field);
582-
// Field members are relative to the union member, so we must remove the union member offset
583-
// from the field member offset to make it relative to the union member.
584-
field_member.offset -= byte_offset;
585-
let overwrites = false; // TODO: Handle overwrites?
586-
union_builder.insert_member(field_member, overwrites);
587-
}
588-
Type::structure(&union_builder.finalize())
589-
}
590-
591521
pub fn field_member(&self, field: &Field) -> StructureMember {
592522
let field_ty = self.field_type(field);
593523
let conf_field_ty = Conf::new(field_ty, MAX_CONFIDENCE);
594-
let byte_offset = field.bit_offset() / 8;
595-
StructureMember::new(
524+
StructureMember::new_bitfield(
596525
conf_field_ty,
597526
field.name.to_owned(),
598-
byte_offset as u64,
527+
field.bit_offset() as u64,
528+
field.bit_width() as u8,
599529
MemberAccess::PublicAccess,
600530
MemberScope::NoScope,
601531
)
@@ -633,7 +563,6 @@ impl DeviceMapper {
633563
}
634564

635565
pub fn single_field_int_type(&self, field: &FieldInfo) -> Ref<Type> {
636-
// TODO: Binary Ninja is unable to handle bit fields, so we abuse unions.
637566
// Get the closest 8-bit aligned integer and use that.
638567
let width = field.bit_width();
639568
let byte_aligned_width = byte_width(width);

0 commit comments

Comments
 (0)