summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMason Reed <mason@vector35.com>2025-09-21 21:51:37 -0400
committerMason Reed <mason@vector35.com>2025-10-23 14:29:15 -0400
commitc35849163319ee606a28eb4d8bfd585b1f4eddcf (patch)
treeb6e2269effe50de773aecf71fca3f71801067ead
parent24e66649863d724aa532ff884c6f27c141a0eede (diff)
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.
-rw-r--r--binaryninjaapi.h21
-rw-r--r--binaryninjacore.h4
-rw-r--r--plugins/dwarf/dwarf_import/src/types.rs94
-rw-r--r--plugins/pdb-ng/src/struct_grouper.rs87
-rw-r--r--plugins/pdb-ng/src/type_parser.rs86
-rw-r--r--plugins/svd/src/mapper.rs79
-rw-r--r--python/types.py43
-rw-r--r--rust/src/types.rs198
-rw-r--r--rust/tests/types.rs2
-rw-r--r--type.cpp31
10 files changed, 397 insertions, 248 deletions
diff --git a/binaryninjaapi.h b/binaryninjaapi.h
index 395ba154..4a270cda 100644
--- a/binaryninjaapi.h
+++ b/binaryninjaapi.h
@@ -10911,6 +10911,8 @@ namespace BinaryNinja {
uint64_t offset;
BNMemberAccess access;
BNMemberScope scope;
+ uint8_t bitPosition;
+ uint8_t bitWidth;
};
/*!
@@ -11112,6 +11114,7 @@ namespace BinaryNinja {
\return Whether a StructureMember was successfully retrieved
*/
bool GetMemberByName(const std::string& name, StructureMember& result) const;
+ // TODO: GetMember at offset also needs to pass a bit position.
bool GetMemberAtOffset(int64_t offset, StructureMember& result) const;
bool GetMemberAtOffset(int64_t offset, StructureMember& result, size_t& idx) const;
uint64_t GetWidth() const;
@@ -11161,10 +11164,26 @@ namespace BinaryNinja {
\param overwriteExisting Whether to overwrite an existing member at that offset, Optional, default true
\param access One of NoAccess, PrivateAccess, ProtectedAccess, PublicAccess
\param scope One of NoScope, StaticScope, VirtualScope, ThunkScope, FriendScope
+ \param bitPosition The number of bits from the start of the `offset` to place this member, used for bitfields
+ \param bitWidth The number of bits wide to make the member, this is analogous to a bitfield width in C
\return Reference to the StructureBuilder
*/
StructureBuilder& AddMemberAtOffset(const Confidence<Ref<Type>>& type, const std::string& name, uint64_t offset,
- bool overwriteExisting = true, BNMemberAccess access = NoAccess, BNMemberScope scope = NoScope);
+ bool overwriteExisting = true, BNMemberAccess access = NoAccess, BNMemberScope scope = NoScope, uint8_t bitPosition = 0, uint8_t bitWidth = 0);
+
+ /*! AddMemberAtBitOffset adds a member at a specific bit offset within the struct
+
+ \param type Type of the Field
+ \param name Name of the field
+ \param bitOffset Offset, in bits, to add the member within the struct
+ \param bitWidth The number of bits wide to make the member, this is analogous to a bitfield width in C
+ \param overwriteExisting Whether to overwrite an existing member at that offset, Optional, default true
+ \param access One of NoAccess, PrivateAccess, ProtectedAccess, PublicAccess
+ \param scope One of NoScope, StaticScope, VirtualScope, ThunkScope, FriendScope
+ \return Reference to the StructureBuilder
+ */
+ StructureBuilder& AddMemberAtBitOffset(const Confidence<Ref<Type>>& type, const std::string& name, uint64_t bitOffset,
+ uint8_t bitWidth, bool overwriteExisting = true, BNMemberAccess access = NoAccess, BNMemberScope scope = NoScope);
/*! RemoveMember removes a member at a specified index
diff --git a/binaryninjacore.h b/binaryninjacore.h
index dd03910b..a5e2605b 100644
--- a/binaryninjacore.h
+++ b/binaryninjacore.h
@@ -2519,6 +2519,8 @@ extern "C"
uint8_t typeConfidence;
BNMemberAccess access;
BNMemberScope scope;
+ uint8_t bitPosition;
+ uint8_t bitWidth;
} BNStructureMember;
typedef struct BNInheritedStructureMember
@@ -7069,7 +7071,7 @@ extern "C"
const char* name, BNMemberAccess access, BNMemberScope scope);
BINARYNINJACOREAPI void BNAddStructureBuilderMemberAtOffset(BNStructureBuilder* s,
const BNTypeWithConfidence* const type, const char* name, uint64_t offset, bool overwriteExisting,
- BNMemberAccess access, BNMemberScope scope);
+ BNMemberAccess access, BNMemberScope scope, uint8_t bitPosition, uint8_t bitWidth);
BINARYNINJACOREAPI void BNRemoveStructureBuilderMember(BNStructureBuilder* s, size_t idx);
BINARYNINJACOREAPI void BNReplaceStructureBuilderMember(BNStructureBuilder* s, size_t idx,
const BNTypeWithConfidence* const type, const char* name, bool overwriteExisting);
diff --git a/plugins/dwarf/dwarf_import/src/types.rs b/plugins/dwarf/dwarf_import/src/types.rs
index 38f0dbc0..f870707b 100644
--- a/plugins/dwarf/dwarf_import/src/types.rs
+++ b/plugins/dwarf/dwarf_import/src/types.rs
@@ -24,7 +24,7 @@ use binaryninja::{
},
};
-use gimli::{constants, AttributeValue, DebuggingInformationEntry, Dwarf, Operation, Unit};
+use gimli::{constants, AttributeValue, DebuggingInformationEntry, DwAt, Dwarf, Operation, Unit};
use log::{debug, error, warn};
@@ -220,10 +220,6 @@ fn do_structure_parse<R: ReaderType>(
continue;
};
- /*
- TODO: apply correct member size when that's supported
- let child_type_width = get_size_as_u64(child_entry).unwrap_or_else(|| child_type.width());
- */
if let Ok(Some(raw_struct_offset)) =
child_entry.attr(constants::DW_AT_data_member_location)
{
@@ -246,36 +242,65 @@ fn do_structure_parse<R: ReaderType>(
MemberAccess::NoAccess, // TODO : Resolve actual scopes, if possible
MemberScope::NoScope,
);
- } else if let Ok(Some(raw_struct_offset_bits)) =
- child_entry.attr(constants::DW_AT_data_bit_offset)
- {
- //TODO: support misaligned offsets when bitwise data structures get in
- let Some(struct_offset_bits) = get_attr_as_u64(&raw_struct_offset_bits)
- .or_else(|| get_expr_value(unit, raw_struct_offset_bits))
- else {
- log::warn!(
- "Failed to get DW_AT_data_bit_offset for offset {:#x} in unit {:?}",
- child_entry.offset().0,
- unit.header.offset()
- );
- continue;
- };
-
- structure_builder.insert(
- &child_type,
- &child_name,
- struct_offset_bits / 8,
- false,
- MemberAccess::NoAccess, // TODO : Resolve actual scopes, if possible
- MemberScope::NoScope,
- );
} else {
- structure_builder.append(
- &child_type,
- &child_name,
- MemberAccess::NoAccess,
- MemberScope::NoScope,
- );
+ let select_value =
+ |e: &DebuggingInformationEntry<R>, attr: DwAt| -> Option<u64> {
+ get_attr_as_u64(&e.attr(attr).ok()??)
+ };
+
+ // If no byte offset, try the bitfield using DW_AT_bit_offset/DW_AT_data_bit_offset + DW_AT_bit_size
+ let bit_size = select_value(child_entry, constants::DW_AT_bit_size);
+ let bit_offset = select_value(child_entry, constants::DW_AT_bit_offset);
+ let data_bit_offset =
+ select_value(child_entry, constants::DW_AT_data_bit_offset);
+
+ match (bit_size, bit_offset, data_bit_offset) {
+ (Some(bit_size), Some(bit_offset), _) => {
+ // Heuristic storage unit bits from the member type width (bytes -> bits). Fallback to 8.
+ let storage_bits = {
+ let w = child_type.width();
+ if w > 0 {
+ w * 8
+ } else {
+ 8
+ }
+ };
+
+ // DW_AT_bit_offset is from the MSB of the storage unit:
+ // absolute = base_byte_off*8 + storage_bits - (boffs + bit_sz)
+ // With no base_byte_off available here, treat base as 0.
+ let total_bit_off = storage_bits.saturating_sub(bit_offset + bit_size);
+
+ structure_builder.insert_bitwise(
+ &child_type,
+ &child_name,
+ total_bit_off,
+ Some(bit_size as u8),
+ false,
+ MemberAccess::NoAccess,
+ MemberScope::NoScope,
+ );
+ }
+ (Some(bit_size), None, Some(data_bit_offset)) => {
+ structure_builder.insert_bitwise(
+ &child_type,
+ &child_name,
+ data_bit_offset,
+ Some(bit_size as u8),
+ false,
+ MemberAccess::NoAccess,
+ MemberScope::NoScope,
+ );
+ }
+ _ => {
+ structure_builder.append(
+ &child_type,
+ &child_name,
+ MemberAccess::NoAccess,
+ MemberScope::NoScope,
+ );
+ }
+ }
}
}
constants::DW_TAG_inheritance => {
@@ -316,7 +341,6 @@ fn do_structure_parse<R: ReaderType>(
structure_builder.base_structures(&base_structures);
let finalized_structure = Type::structure(&structure_builder.finalize());
-
if let Some(full_name) = full_name {
debug_info_builder.add_type(
get_uid(dwarf, unit, entry) + 1, // TODO : This is super broke (uid + 1 is not guaranteed to be unique)
diff --git a/plugins/pdb-ng/src/struct_grouper.rs b/plugins/pdb-ng/src/struct_grouper.rs
index ff911afd..fcaf88ef 100644
--- a/plugins/pdb-ng/src/struct_grouper.rs
+++ b/plugins/pdb-ng/src/struct_grouper.rs
@@ -340,8 +340,10 @@ pub fn group_structure(
.enumerate()
.map(|(i, member)| MemberSize {
index: i,
- offset: member.offset,
- width: member.ty.contents.width(),
+ offset: member.bitfield_position.unwrap_or(member.offset * 8),
+ width: member
+ .bitfield_size
+ .unwrap_or(member.ty.contents.width() * 8),
})
.collect::<Vec<_>>();
@@ -359,14 +361,29 @@ pub fn group_structure(
Err(e) => {
warn!("{} Could not resolve structure groups: {}", name, e);
for member in members {
- structure.insert(
- &member.ty,
- &member.name,
- member.offset,
- false,
- member.access,
- member.scope,
- );
+ match (member.bitfield_position, member.bitfield_size) {
+ (Some(bit_pos), bit_width) => {
+ structure.insert_bitwise(
+ &member.ty,
+ &member.name,
+ bit_pos,
+ bit_width.map(|w| w as u8),
+ false,
+ member.access,
+ member.scope,
+ );
+ }
+ (None, _) => {
+ structure.insert(
+ &member.ty,
+ &member.name,
+ member.offset,
+ false,
+ member.access,
+ member.scope,
+ );
+ }
+ }
}
}
}
@@ -387,24 +404,38 @@ fn apply_groups(
// TODO : Fix inner-offset being larger than `member.offset`
- if offset > member.offset {
- structure.insert(
- &member.ty,
- &member.name,
- 0,
- false,
- member.access,
- member.scope,
- );
- } else {
- structure.insert(
- &member.ty,
- &member.name,
- member.offset - offset,
- false,
- member.access,
- member.scope,
- );
+ match (member.bitfield_position, member.bitfield_size) {
+ (Some(bit_pos), bit_width) => {
+ structure.insert_bitwise(
+ &member.ty,
+ &member.name,
+ bit_pos,
+ bit_width.map(|w| w as u8),
+ false,
+ member.access,
+ member.scope,
+ );
+ }
+ (None, _) if offset > member.offset => {
+ structure.insert(
+ &member.ty,
+ &member.name,
+ 0,
+ false,
+ member.access,
+ member.scope,
+ );
+ }
+ (None, _) => {
+ structure.insert(
+ &member.ty,
+ &member.name,
+ member.offset - offset,
+ false,
+ member.access,
+ member.scope,
+ );
+ }
}
}
ResolvedGroup::Struct(inner_offset, children) => {
diff --git a/plugins/pdb-ng/src/type_parser.rs b/plugins/pdb-ng/src/type_parser.rs
index ba7f3cc9..dcb7234b 100644
--- a/plugins/pdb-ng/src/type_parser.rs
+++ b/plugins/pdb-ng/src/type_parser.rs
@@ -848,92 +848,6 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> {
None => {}
}
- // Combine bitfields into structures
- let mut combined_bitfield_members = vec![];
- let mut last_bitfield_offset = u64::MAX;
- let mut last_bitfield_pos = u64::MAX;
- let mut last_bitfield_idx = 0;
- let mut bitfield_builder: Option<StructureBuilder> = None;
-
- fn bitfield_name(offset: u64, idx: u64) -> String {
- if idx > 0 {
- format!("__bitfield{:x}_{}", offset, idx)
- } else {
- format!("__bitfield{:x}", offset)
- }
- }
-
- for m in members {
- match (m.bitfield_position, m.bitfield_size) {
- (Some(pos), Some(_size)) => {
- if last_bitfield_offset != m.offset || last_bitfield_pos >= pos {
- if let Some(builder) = bitfield_builder.take() {
- combined_bitfield_members.push(ParsedMember {
- ty: Conf::new(
- Type::structure(builder.finalize().as_ref()),
- MAX_CONFIDENCE,
- ),
- name: bitfield_name(last_bitfield_offset, last_bitfield_idx),
- offset: last_bitfield_offset,
- access: MemberAccess::PublicAccess,
- scope: MemberScope::NoScope,
- bitfield_size: None,
- bitfield_position: None,
- });
- }
- let mut new_builder = StructureBuilder::new();
- new_builder.structure_type(StructureType::UnionStructureType);
- new_builder.width(m.ty.contents.width());
- bitfield_builder = Some(new_builder);
-
- if last_bitfield_offset != m.offset {
- last_bitfield_idx = 0;
- } else {
- last_bitfield_idx += 1;
- }
- }
-
- last_bitfield_pos = pos;
- last_bitfield_offset = m.offset;
- bitfield_builder
- .as_mut()
- .expect("Invariant")
- .insert(&m.ty, &m.name, 0, false, m.access, m.scope);
- }
- (None, None) => {
- if let Some(builder) = bitfield_builder.take() {
- combined_bitfield_members.push(ParsedMember {
- ty: Conf::new(
- Type::structure(builder.finalize().as_ref()),
- MAX_CONFIDENCE,
- ),
- name: bitfield_name(last_bitfield_offset, last_bitfield_idx),
- offset: last_bitfield_offset,
- access: MemberAccess::PublicAccess,
- scope: MemberScope::NoScope,
- bitfield_size: None,
- bitfield_position: None,
- });
- }
- last_bitfield_offset = u64::MAX;
- last_bitfield_pos = u64::MAX;
- combined_bitfield_members.push(m);
- }
- e => return Err(anyhow!("Unexpected bitfield parameters {:?}", e)),
- }
- }
- if let Some(builder) = bitfield_builder.take() {
- combined_bitfield_members.push(ParsedMember {
- ty: Conf::new(Type::structure(builder.finalize().as_ref()), MAX_CONFIDENCE),
- name: bitfield_name(last_bitfield_offset, last_bitfield_idx),
- offset: last_bitfield_offset,
- access: MemberAccess::PublicAccess,
- scope: MemberScope::NoScope,
- bitfield_size: None,
- bitfield_position: None,
- });
- }
- members = combined_bitfield_members;
group_structure(
&format!(
"`{}`",
diff --git a/plugins/svd/src/mapper.rs b/plugins/svd/src/mapper.rs
index 8846c858..4c49145b 100644
--- a/plugins/svd/src/mapper.rs
+++ b/plugins/svd/src/mapper.rs
@@ -8,7 +8,7 @@ use binaryninja::segment::{SegmentBuilder, SegmentFlags};
use binaryninja::symbol::{SymbolBuilder, SymbolType};
use binaryninja::types::{
BaseStructure, EnumerationBuilder, MemberAccess, MemberScope, NamedTypeReference,
- NamedTypeReferenceClass, StructureBuilder, StructureMember, StructureType, Type, TypeBuilder,
+ NamedTypeReferenceClass, StructureBuilder, StructureMember, Type, TypeBuilder,
};
use std::collections::HashMap;
use std::num::NonZeroUsize;
@@ -442,53 +442,12 @@ impl DeviceMapper {
let type_builder = match &register.fields {
Some(fields) => {
- // Separate bitfields from regular fields.
- let (fields, bitfield_items): (Vec<_>, Vec<_>) =
- fields.iter().partition(|f| {
- byte_aligned(f.bit_range.width) && byte_aligned(f.bit_range.offset)
- });
-
for field in fields {
let field_member = self.field_member(field);
let overwrites = true; // TODO: Handle overwrites?
register_struct.insert_member(field_member, overwrites);
}
- if self.settings.add_bitfields {
- // The bitfield items need to be coalesced to a map of byte offset to vec of fields.
- let mut bitfield_map: HashMap<u64, Vec<&Field>> = HashMap::new();
-
- // Sort bitfields by their offset
- let mut sorted_bitfields = bitfield_items.iter().collect::<Vec<_>>();
- sorted_bitfields.sort_by_key(|f| f.bit_range.offset);
-
- // Group bitfields by overlapping bit offsets
- let mut current_bit_start = 0;
- let mut current_bit_end = 0;
- for field in sorted_bitfields {
- let bit_start = field.bit_range.offset;
- let byte_start = bit_start / 8;
- let current_byte_start = current_bit_start / 8;
- if current_byte_start != byte_start && current_bit_end < bit_start {
- // Make a new bitfield, only if the current field is in a new byte.
- current_bit_start = bit_start;
- }
- current_bit_end = bit_start + field.bit_range.width;
- bitfield_map
- .entry(current_bit_start as u64)
- .or_insert_with(Vec::new)
- .push(field);
- }
-
- for (bit_start, fields) in bitfield_map {
- // Add each bitfield to the structure!
- let byte_start = bit_start / 8;
- let bitfield_member = self.bitfield_member(byte_start, fields);
- let overwrites = true; // TODO: Handle overwrites?
- register_struct.insert_member(bitfield_member, overwrites);
- }
- }
-
TypeBuilder::structure(&register_struct.finalize())
}
None if register.derived_from.is_some() => {
@@ -559,43 +518,14 @@ impl DeviceMapper {
}
}
- pub fn bitfield_member(&self, byte_offset: u64, fields: Vec<&Field>) -> StructureMember {
- let field_ty = self.bitfield_type(byte_offset, fields);
- // TODO: Create bitfield name from the fields?
- let field_name = format!("bitfield_0x{:x}", byte_offset);
- // TODO: This should be like 120 confidence?
- let conf_field_ty = Conf::new(field_ty, MAX_CONFIDENCE);
- StructureMember::new(
- conf_field_ty,
- field_name,
- byte_offset,
- MemberAccess::PublicAccess,
- MemberScope::NoScope,
- )
- }
-
- pub fn bitfield_type(&self, byte_offset: u64, fields: Vec<&Field>) -> Ref<Type> {
- let mut union_builder = StructureBuilder::new();
- union_builder.structure_type(StructureType::UnionStructureType);
- for field in fields {
- let mut field_member = self.field_member(field);
- // Field members are relative to the union member, so we must remove the union member offset
- // from the field member offset to make it relative to the union member.
- field_member.offset -= byte_offset;
- let overwrites = false; // TODO: Handle overwrites?
- union_builder.insert_member(field_member, overwrites);
- }
- Type::structure(&union_builder.finalize())
- }
-
pub fn field_member(&self, field: &Field) -> StructureMember {
let field_ty = self.field_type(field);
let conf_field_ty = Conf::new(field_ty, MAX_CONFIDENCE);
- let byte_offset = field.bit_offset() / 8;
- StructureMember::new(
+ StructureMember::new_bitfield(
conf_field_ty,
field.name.to_owned(),
- byte_offset as u64,
+ field.bit_offset() as u64,
+ field.bit_width() as u8,
MemberAccess::PublicAccess,
MemberScope::NoScope,
)
@@ -633,7 +563,6 @@ impl DeviceMapper {
}
pub fn single_field_int_type(&self, field: &FieldInfo) -> Ref<Type> {
- // TODO: Binary Ninja is unable to handle bit fields, so we abuse unions.
// Get the closest 8-bit aligned integer and use that.
let width = field.bit_width();
let byte_aligned_width = byte_width(width);
diff --git a/python/types.py b/python/types.py
index 5ad589e0..222f256e 100644
--- a/python/types.py
+++ b/python/types.py
@@ -1332,11 +1332,18 @@ class StructureMember:
offset: int
access: MemberAccess = MemberAccess.NoAccess
scope: MemberScope = MemberScope.NoScope
+ bit_position: int = 0
+ bit_width: int = 0
def __repr__(self):
if len(self.name) == 0:
- return f"<member: {self.type}, offset {self.offset:#x}>"
- return f"<{self.type.get_string_before_name()} {self.name}{self.type.get_string_after_name()}, offset {self.offset:#x}>"
+ base = f"<member: {self.type}, offset {self.offset:#x}>"
+ else:
+ base = f"<{self.type.get_string_before_name()} {self.name}{self.type.get_string_after_name()}, offset {self.offset:#x}>"
+ # Append bit position/width only if bit_width is not zero (indicates a bitfield)
+ if self.bit_width != 0:
+ return base[:-1] + f", bit {self.bit_position}:{self.bit_width}>"
+ return base
def __len__(self):
return len(self.type)
@@ -1416,7 +1423,7 @@ class StructureBuilder(TypeBuilder):
elif isinstance(member, StructureMember):
core.BNAddStructureBuilderMemberAtOffset(
structure_builder_handle, member.type._to_core_struct(), member.name, member.offset, False,
- member.access, member.scope
+ member.access, member.scope, member.bit_position, member.bit_width
)
elif isinstance(member, (TypeBuilder, Type)):
core.BNAddStructureBuilderMember(
@@ -1464,7 +1471,7 @@ class StructureBuilder(TypeBuilder):
result.append(
StructureMember(
t, members[i].name, members[i].offset, MemberAccess(members[i].access),
- MemberScope(members[i].scope)
+ MemberScope(members[i].scope), members[i].bitPosition, members[i].bitWidth
)
)
return result
@@ -1560,7 +1567,7 @@ class StructureBuilder(TypeBuilder):
return StructureMember(
Type.create(core.BNNewTypeReference(member.contents.type), confidence=member.contents.typeConfidence),
member.contents.name, member.contents.offset, MemberAccess(member.contents.access),
- MemberScope(member.contents.scope)
+ MemberScope(member.contents.scope), member.contents.bitPosition, member.contents.bitWidth
)
finally:
core.BNFreeStructureMember(member)
@@ -1600,10 +1607,11 @@ class StructureBuilder(TypeBuilder):
def insert(
self, offset: int, type: SomeType, name: str = "", overwrite_existing: bool = True,
- access: MemberAccess = MemberAccess.NoAccess, scope: MemberScope = MemberScope.NoScope
+ access: MemberAccess = MemberAccess.NoAccess, scope: MemberScope = MemberScope.NoScope, bit_position: int = 0, bit_width: int = 0
):
core.BNAddStructureBuilderMemberAtOffset(
- self.builder_handle, type._to_core_struct(), name, offset, overwrite_existing, access, scope
+ self.builder_handle, type._to_core_struct(), name, offset, overwrite_existing, access, scope, bit_position,
+ bit_width
)
def append(
@@ -1616,11 +1624,12 @@ class StructureBuilder(TypeBuilder):
def add_member_at_offset(
self, name: MemberName, type: SomeType, offset: MemberOffset, overwrite_existing: bool = True,
- access: MemberAccess = MemberAccess.NoAccess, scope: MemberScope = MemberScope.NoScope
+ access: MemberAccess = MemberAccess.NoAccess, scope: MemberScope = MemberScope.NoScope, bit_position: int = 0, bit_width: int = 0
) -> 'StructureBuilder':
# Adds structure member to the given offset optionally clearing any members within the range offset-offset+len(type)
core.BNAddStructureBuilderMemberAtOffset(
- self.builder_handle, type._to_core_struct(), name, offset, overwrite_existing, access, scope
+ self.builder_handle, type._to_core_struct(), name, offset, overwrite_existing, access, scope, bit_position,
+ bit_width
)
return self
@@ -2616,7 +2625,7 @@ class StructureType(Type):
return StructureMember(
Type.create(core.BNNewTypeReference(member.contents.type), confidence=member.contents.typeConfidence),
member.contents.name, member.contents.offset, MemberAccess(member.contents.access),
- MemberScope(member.contents.scope)
+ MemberScope(member.contents.scope), member.contents.bitPosition, member.contents.bitWidth
)
finally:
if member is not None:
@@ -2631,14 +2640,17 @@ class StructureType(Type):
return StructureMember(
Type.create(core.BNNewTypeReference(member.contents.type), confidence=member.contents.typeConfidence),
member.contents.name, member.contents.offset, MemberAccess(member.contents.access),
- MemberScope(member.contents.scope)
+ MemberScope(member.contents.scope), member.contents.bitPosition, member.contents.bitWidth
)
finally:
core.BNFreeStructureMember(member)
@property
def members(self):
- """Structure member list (read-only)"""
+ """
+ Structure member list (read-only). This list will **not** contain members inherited from base structures.
+ To get members including inherited ones, call `members_including_inherited`.
+ """
count = ctypes.c_ulonglong()
members = core.BNGetStructureMembers(self.struct_handle, count)
assert members is not None, "core.BNGetStructureMembers returned None"
@@ -2649,7 +2661,7 @@ class StructureType(Type):
StructureMember(
Type.create(core.BNNewTypeReference(members[i].type), confidence=members[i].typeConfidence),
members[i].name, members[i].offset, MemberAccess(members[i].access),
- MemberScope(members[i].scope)
+ MemberScope(members[i].scope), members[i].bitPosition, members[i].bitWidth
)
)
finally:
@@ -2732,7 +2744,7 @@ class StructureType(Type):
StructureMember(
Type.create(core.BNNewTypeReference(members[i].member.type), confidence=members[i].member.typeConfidence),
members[i].member.name, members[i].member.offset, MemberAccess(members[i].member.access),
- MemberScope(members[i].member.scope)
+ MemberScope(members[i].member.scope), members[i].member.bitPosition, members[i].member.bitWidth
),
members[i].memberIndex
)
@@ -2746,7 +2758,6 @@ class StructureType(Type):
member = None
try:
member = core.BNGetMemberIncludingInheritedAtOffset(self.struct_handle, view.handle, offset)
- result = None
if member is None:
raise ValueError(f"No member exists at offset {offset}")
@@ -2764,7 +2775,7 @@ class StructureType(Type):
StructureMember(
Type.create(core.BNNewTypeReference(member[0].member.type), confidence=member[0].member.typeConfidence),
member[0].member.name, member[0].member.offset, MemberAccess(member[0].member.access),
- MemberScope(member[0].member.scope)
+ MemberScope(member[0].member.scope), member[0].member.bitPosition, member[0].member.bitWidth
),
member[0].memberIndex
)
diff --git a/rust/src/types.rs b/rust/src/types.rs
index 036e53f5..7566cff8 100644
--- a/rust/src/types.rs
+++ b/rust/src/types.rs
@@ -29,6 +29,7 @@ use crate::{
use crate::confidence::{Conf, MAX_CONFIDENCE, MIN_CONFIDENCE};
use crate::string::{raw_to_string, strings_to_string_list};
+use crate::type_container::TypeContainer;
use crate::variable::{Variable, VariableSourceType};
use std::borrow::Cow;
use std::num::NonZeroUsize;
@@ -1460,6 +1461,13 @@ impl StructureBuilder {
self
}
+ /// Append a member at the next available byte offset.
+ ///
+ /// Otherwise, consider using:
+ ///
+ /// - [`StructureBuilder::insert_member`]
+ /// - [`StructureBuilder::insert`]
+ /// - [`StructureBuilder::insert_bitwise`]
pub fn append<'a, T: Into<Conf<&'a Type>>>(
&mut self,
ty: T,
@@ -1481,15 +1489,23 @@ impl StructureBuilder {
self
}
+ /// Insert an already constructed [`StructureMember`].
+ ///
+ /// Otherwise, consider using:
+ ///
+ /// - [`StructureBuilder::append`]
+ /// - [`StructureBuilder::insert`]
+ /// - [`StructureBuilder::insert_bitwise`]
pub fn insert_member(
&mut self,
member: StructureMember,
overwrite_existing: bool,
) -> &mut Self {
- self.insert(
+ self.insert_bitwise(
&member.ty,
&member.name,
- member.offset,
+ member.bit_offset(),
+ member.bit_width,
overwrite_existing,
member.access,
member.scope,
@@ -1497,6 +1513,10 @@ impl StructureBuilder {
self
}
+ /// Inserts a member at the `offset` (in bytes).
+ ///
+ /// If you need to insert a member at a specific bit within a given byte (like a bitfield), you
+ /// can use [`StructureBuilder::insert_bitwise`].
pub fn insert<'a, T: Into<Conf<&'a Type>>>(
&mut self,
ty: T,
@@ -1506,17 +1526,46 @@ impl StructureBuilder {
access: MemberAccess,
scope: MemberScope,
) -> &mut Self {
+ self.insert_bitwise(
+ ty,
+ name,
+ offset * 8,
+ None,
+ overwrite_existing,
+ access,
+ scope,
+ )
+ }
+
+ /// Inserts a member at `bit_offset` with an optional `bit_width`.
+ ///
+ /// NOTE: The `bit_offset` is relative to the start of the structure, for example, passing `8` will place
+ /// the field at the start of the byte `0x1`.
+ pub fn insert_bitwise<'a, T: Into<Conf<&'a Type>>>(
+ &mut self,
+ ty: T,
+ name: &str,
+ bit_offset: u64,
+ bit_width: Option<u8>,
+ overwrite_existing: bool,
+ access: MemberAccess,
+ scope: MemberScope,
+ ) -> &mut Self {
let name = name.to_cstr();
let owned_raw_ty = Conf::<&Type>::into_raw(ty.into());
+ let byte_offset = bit_offset / 8;
+ let bit_position = bit_offset % 8;
unsafe {
BNAddStructureBuilderMemberAtOffset(
self.handle,
&owned_raw_ty,
name.as_ref().as_ptr() as _,
- offset,
+ byte_offset,
overwrite_existing,
access,
scope,
+ bit_position as u8,
+ bit_width.unwrap_or(0),
);
}
self
@@ -1543,6 +1592,7 @@ impl StructureBuilder {
self
}
+ /// Removes the member at a given index.
pub fn remove(&mut self, index: usize) -> &mut Self {
unsafe { BNRemoveStructureBuilderMember(self.handle, index) };
self
@@ -1608,6 +1658,33 @@ impl Structure {
unsafe { BNGetStructureType(self.handle) }
}
+ /// Retrieve the members that are accessible at a given offset.
+ ///
+ /// The reason for this being plural is that members may overlap and the offset is in bytes
+ /// where a bitfield may contain multiple members at the given byte.
+ ///
+ /// Unions are also represented as structures and will cause this function to return
+ /// **all** members that can reach that offset.
+ ///
+ /// We must pass a [`TypeContainer`] here so that we can resolve base structure members, as they
+ /// are treated as members through this function. Typically, you get the [`TypeContainer`]
+ /// through the binary view with [`BinaryView::get_type_container`].
+ pub fn members_at_offset(
+ &self,
+ container: &TypeContainer,
+ offset: u64,
+ ) -> Vec<StructureMember> {
+ self.members_including_inherited(container)
+ .into_iter()
+ .filter(|m| m.member.is_offset_valid(offset))
+ .map(|m| m.member)
+ .collect()
+ }
+
+ /// Return the list of non-inherited structure members.
+ ///
+ /// If you want to get all members, including ones inherited from base structures,
+ /// use [`Structure::members_including_inherited`] instead.
pub fn members(&self) -> Vec<StructureMember> {
unsafe {
let mut count = 0;
@@ -1621,6 +1698,35 @@ impl Structure {
}
}
+ /// Returns the list of all structure members, including inherited ones.
+ ///
+ /// Because we must traverse through base structures, we have to provide the [`TypeContainer`];
+ /// in most cases it is ok to provide the binary views container via [`BinaryView::type_container`].
+ pub fn members_including_inherited(
+ &self,
+ container: &TypeContainer,
+ ) -> Vec<InheritedStructureMember> {
+ unsafe {
+ let mut count = 0;
+ let members_raw_ptr: *mut BNInheritedStructureMember =
+ BNGetStructureMembersIncludingInherited(
+ self.handle,
+ container.handle.as_ptr(),
+ &mut count,
+ );
+ debug_assert!(!members_raw_ptr.is_null());
+ let members_raw = std::slice::from_raw_parts(members_raw_ptr, count);
+ let members = members_raw
+ .iter()
+ .map(InheritedStructureMember::from_raw)
+ .collect();
+ BNFreeInheritedStructureMemberList(members_raw_ptr, count);
+ members
+ }
+ }
+
+ /// Retrieve the list of base structures for the structure. These base structures are what give
+ /// a structure inherited members.
pub fn base_structures(&self) -> Vec<BaseStructure> {
let mut count = 0;
let bases_raw_ptr = unsafe { BNGetBaseStructuresForStructure(self.handle, &mut count) };
@@ -1631,13 +1737,22 @@ impl Structure {
bases
}
- // TODO : The other methods in the python version (alignment, packed, type, members, remove, replace, etc)
+ /// Whether the structure is packed or not.
+ pub fn is_packed(&self) -> bool {
+ unsafe { BNIsStructurePacked(self.handle) }
+ }
+
+ pub fn alignment(&self) -> usize {
+ unsafe { BNGetStructureAlignment(self.handle) }
+ }
}
impl Debug for Structure {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Structure")
.field("width", &self.width())
+ .field("alignment", &self.alignment())
+ .field("packed", &self.is_packed())
.field("structure_type", &self.structure_type())
.field("base_structures", &self.base_structures())
.field("members", &self.members())
@@ -1668,9 +1783,13 @@ pub struct StructureMember {
pub ty: Conf<Ref<Type>>,
// TODO: Shouldnt this be a QualifiedName? The ffi says no...
pub name: String,
+ /// The byte offset of the member.
pub offset: u64,
pub access: MemberAccess,
pub scope: MemberScope,
+ /// The bit position relative to the byte offset.
+ pub bit_position: Option<u8>,
+ pub bit_width: Option<u8>,
}
impl StructureMember {
@@ -1685,6 +1804,14 @@ impl StructureMember {
offset: value.offset,
access: value.access,
scope: value.scope,
+ bit_position: match value.bitPosition {
+ 0 => None,
+ _ => Some(value.bitPosition),
+ },
+ bit_width: match value.bitWidth {
+ 0 => None,
+ _ => Some(value.bitWidth),
+ },
}
}
@@ -1703,6 +1830,8 @@ impl StructureMember {
typeConfidence: value.ty.confidence,
access: value.access,
scope: value.scope,
+ bitPosition: value.bit_position.unwrap_or(0),
+ bitWidth: value.bit_width.unwrap_or(0),
}
}
@@ -1724,8 +1853,40 @@ impl StructureMember {
offset,
access,
scope,
+ bit_position: None,
+ bit_width: None,
}
}
+
+ pub fn new_bitfield(
+ ty: Conf<Ref<Type>>,
+ name: String,
+ bit_offset: u64,
+ bit_width: u8,
+ access: MemberAccess,
+ scope: MemberScope,
+ ) -> Self {
+ Self {
+ ty,
+ name,
+ offset: bit_offset / 8,
+ access,
+ scope,
+ bit_position: Some((bit_offset % 8) as u8),
+ bit_width: Some(bit_width),
+ }
+ }
+
+ // TODO: Do we count bitwidth here?
+ /// Whether the offset within the accessible range of the member.
+ pub fn is_offset_valid(&self, offset: u64) -> bool {
+ self.offset <= offset && offset < self.offset + self.ty.contents.width()
+ }
+
+ /// Member offset in bits.
+ pub fn bit_offset(&self) -> u64 {
+ (self.offset * 8) + self.bit_position.unwrap_or(0) as u64
+ }
}
impl CoreArrayProvider for StructureMember {
@@ -1753,6 +1914,35 @@ pub struct InheritedStructureMember {
}
impl InheritedStructureMember {
+ pub(crate) fn from_raw(value: &BNInheritedStructureMember) -> Self {
+ Self {
+ base: unsafe { NamedTypeReference::from_raw(value.base) }.to_owned(),
+ base_offset: value.baseOffset,
+ member: StructureMember::from_raw(&value.member),
+ member_index: value.memberIndex,
+ }
+ }
+
+ pub(crate) fn from_owned_raw(value: BNInheritedStructureMember) -> Self {
+ let owned = Self::from_raw(&value);
+ Self::free_raw(value);
+ owned
+ }
+
+ pub(crate) fn into_raw(value: Self) -> BNInheritedStructureMember {
+ BNInheritedStructureMember {
+ base: unsafe { Ref::into_raw(value.base) }.handle,
+ baseOffset: value.base_offset,
+ member: StructureMember::into_raw(value.member),
+ memberIndex: value.member_index,
+ }
+ }
+
+ pub(crate) fn free_raw(value: BNInheritedStructureMember) {
+ let _ = unsafe { NamedTypeReference::ref_from_raw(value.base) };
+ StructureMember::free_raw(value.member);
+ }
+
pub fn new(
base: Ref<NamedTypeReference>,
base_offset: u64,
diff --git a/rust/tests/types.rs b/rust/tests/types.rs
index 17682777..962df630 100644
--- a/rust/tests/types.rs
+++ b/rust/tests/types.rs
@@ -52,6 +52,8 @@ fn test_structure_builder() {
offset: 0,
access: MemberAccess::PrivateAccess,
scope: MemberScope::FriendScope,
+ bit_position: None,
+ bit_width: None,
}
);
}
diff --git a/type.cpp b/type.cpp
index 0625f8e0..17f78840 100644
--- a/type.cpp
+++ b/type.cpp
@@ -2515,6 +2515,8 @@ vector<StructureMember> Structure::GetMembers() const
member.offset = members[i].offset;
member.access = members[i].access;
member.scope = members[i].scope;
+ member.bitPosition = members[i].bitPosition;
+ member.bitWidth = members[i].bitWidth;
result.push_back(member);
}
@@ -2540,6 +2542,8 @@ vector<InheritedStructureMember> Structure::GetMembersIncludingInherited(const T
member.member.offset = members[i].member.offset;
member.member.access = members[i].member.access;
member.member.scope = members[i].member.scope;
+ member.member.bitPosition = members[i].member.bitPosition;
+ member.member.bitWidth = members[i].member.bitWidth;
member.memberIndex = members[i].memberIndex;
result.push_back(member);
}
@@ -2563,6 +2567,8 @@ bool Structure::GetMemberIncludingInheritedAtOffset(BinaryView* view, int64_t of
result.member.offset = member->member.offset;
result.member.access = member->member.access;
result.member.scope = member->member.scope;
+ result.member.bitPosition = member->member.bitPosition;
+ result.member.bitWidth = member->member.bitWidth;
result.memberIndex = member->memberIndex;
BNFreeInheritedStructureMember(member);
@@ -2580,6 +2586,8 @@ bool Structure::GetMemberByName(const string& name, StructureMember& result) con
result.offset = member->offset;
result.access = member->access;
result.scope = member->scope;
+ result.bitPosition = member->bitPosition;
+ result.bitWidth = member->bitWidth;
BNFreeStructureMember(member);
return true;
}
@@ -2604,6 +2612,8 @@ bool Structure::GetMemberAtOffset(int64_t offset, StructureMember& result, size_
result.offset = member->offset;
result.access = member->access;
result.scope = member->scope;
+ result.bitPosition = member->bitPosition;
+ result.bitWidth = member->bitWidth;
BNFreeStructureMember(member);
return true;
}
@@ -2708,6 +2718,8 @@ static void ResolveMemberCallback(void* ctxt, BNNamedTypeReference* baseName, BN
apiMember.offset = member.offset;
apiMember.access = member.access;
apiMember.scope = member.scope;
+ apiMember.bitPosition = member.bitPosition;
+ apiMember.bitWidth = member.bitWidth;
(*resolveFunc->callback)(baseNameRef, resolvedStructRef, memberIndex, structOffset, adjustedOffset, apiMember);
}
@@ -2848,6 +2860,8 @@ vector<StructureMember> StructureBuilder::GetMembers() const
member.offset = members[i].offset;
member.access = members[i].access;
member.scope = members[i].scope;
+ member.bitPosition = members[i].bitPosition;
+ member.bitWidth = members[i].bitWidth;
result.push_back(member);
}
@@ -2866,6 +2880,8 @@ bool StructureBuilder::GetMemberByName(const string& name, StructureMember& resu
result.offset = member->offset;
result.access = member->access;
result.scope = member->scope;
+ result.bitPosition = member->bitPosition;
+ result.bitWidth = member->bitWidth;
BNFreeStructureMember(member);
return true;
}
@@ -2890,6 +2906,8 @@ bool StructureBuilder::GetMemberAtOffset(int64_t offset, StructureMember& result
result.offset = member->offset;
result.access = member->access;
result.scope = member->scope;
+ result.bitPosition = member->bitPosition;
+ result.bitWidth = member->bitWidth;
BNFreeStructureMember(member);
return true;
}
@@ -2993,16 +3011,25 @@ StructureBuilder& StructureBuilder::AddMember(
StructureBuilder& StructureBuilder::AddMemberAtOffset(const Confidence<Ref<Type>>& type, const string& name,
- uint64_t offset, bool overwriteExisting, BNMemberAccess access, BNMemberScope scope)
+ uint64_t offset, bool overwriteExisting, BNMemberAccess access, BNMemberScope scope, uint8_t bitPosition, uint8_t bitWidth)
{
BNTypeWithConfidence tc;
tc.type = type->GetObject();
tc.confidence = type.GetConfidence();
- BNAddStructureBuilderMemberAtOffset(m_object, &tc, name.c_str(), offset, overwriteExisting, access, scope);
+ BNAddStructureBuilderMemberAtOffset(m_object, &tc, name.c_str(), offset, overwriteExisting, access, scope, bitPosition, bitWidth);
return *this;
}
+StructureBuilder& StructureBuilder::AddMemberAtBitOffset(const Confidence<Ref<Type>>& type, const string& name,
+ uint64_t bitOffset, uint8_t bitWidth, bool overwriteExisting, BNMemberAccess access, BNMemberScope scope)
+{
+ const uint64_t byteOffset = bitOffset / 8;
+ const uint8_t bitPosition = bitOffset % 8;
+ return AddMemberAtOffset(type, name, byteOffset, overwriteExisting, access, scope, bitPosition, bitWidth);
+}
+
+
StructureBuilder& StructureBuilder::RemoveMember(size_t idx)
{
BNRemoveStructureBuilderMember(m_object, idx);