Skip to content

Commit 0153dfe

Browse files
committed
ir: Account for packedness when computing bitfield sizes.
Fixes #1716
1 parent e0a0400 commit 0153dfe

File tree

5 files changed

+239
-40
lines changed

5 files changed

+239
-40
lines changed

src/ir/comp.rs

+50-34
Original file line numberDiff line numberDiff line change
@@ -496,6 +496,7 @@ impl FieldMethods for RawField {
496496
fn raw_fields_to_fields_and_bitfield_units<I>(
497497
ctx: &BindgenContext,
498498
raw_fields: I,
499+
packed: bool,
499500
) -> Result<Vec<Field>, ()>
500501
where
501502
I: IntoIterator<Item = RawField>,
@@ -533,6 +534,7 @@ where
533534
&mut bitfield_unit_count,
534535
&mut fields,
535536
bitfields,
537+
packed,
536538
)?;
537539
}
538540

@@ -551,6 +553,7 @@ fn bitfields_to_allocation_units<E, I>(
551553
bitfield_unit_count: &mut usize,
552554
fields: &mut E,
553555
raw_bitfields: I,
556+
packed: bool,
554557
) -> Result<(), ()>
555558
where
556559
E: Extend<Field>,
@@ -575,17 +578,22 @@ where
575578
unit_size_in_bits: usize,
576579
unit_align_in_bits: usize,
577580
bitfields: Vec<Bitfield>,
581+
packed: bool,
578582
) where
579583
E: Extend<Field>,
580584
{
581585
*bitfield_unit_count += 1;
582-
let align = bytes_from_bits_pow2(unit_align_in_bits);
586+
let align = if packed {
587+
1
588+
} else {
589+
bytes_from_bits_pow2(unit_align_in_bits)
590+
};
583591
let size = align_to(unit_size_in_bits, align * 8) / 8;
584592
let layout = Layout::new(size, align);
585593
fields.extend(Some(Field::Bitfields(BitfieldUnit {
586594
nth: *bitfield_unit_count,
587-
layout: layout,
588-
bitfields: bitfields,
595+
layout,
596+
bitfields,
589597
})));
590598
}
591599

@@ -607,34 +615,39 @@ where
607615
let bitfield_align = bitfield_layout.align;
608616

609617
let mut offset = unit_size_in_bits;
610-
if is_ms_struct {
611-
if unit_size_in_bits != 0 &&
612-
(bitfield_width == 0 ||
613-
bitfield_width > unfilled_bits_in_unit)
614-
{
615-
// We've reached the end of this allocation unit, so flush it
616-
// and its bitfields.
617-
unit_size_in_bits = align_to(unit_size_in_bits, unit_align * 8);
618-
flush_allocation_unit(
619-
fields,
620-
bitfield_unit_count,
621-
unit_size_in_bits,
622-
unit_align,
623-
mem::replace(&mut bitfields_in_unit, vec![]),
624-
);
618+
if !packed {
619+
if is_ms_struct {
620+
if unit_size_in_bits != 0 &&
621+
(bitfield_width == 0 ||
622+
bitfield_width > unfilled_bits_in_unit)
623+
{
624+
// We've reached the end of this allocation unit, so flush it
625+
// and its bitfields.
626+
unit_size_in_bits =
627+
align_to(unit_size_in_bits, unit_align * 8);
628+
flush_allocation_unit(
629+
fields,
630+
bitfield_unit_count,
631+
unit_size_in_bits,
632+
unit_align,
633+
mem::replace(&mut bitfields_in_unit, vec![]),
634+
packed,
635+
);
625636

626-
// Now we're working on a fresh bitfield allocation unit, so reset
627-
// the current unit size and alignment.
628-
offset = 0;
629-
unit_align = 0;
630-
}
631-
} else {
632-
if offset != 0 &&
633-
(bitfield_width == 0 ||
634-
(offset & (bitfield_align * 8 - 1)) + bitfield_width >
635-
bitfield_size * 8)
636-
{
637-
offset = align_to(offset, bitfield_align * 8);
637+
// Now we're working on a fresh bitfield allocation unit, so reset
638+
// the current unit size and alignment.
639+
offset = 0;
640+
unit_align = 0;
641+
}
642+
} else {
643+
if offset != 0 &&
644+
(bitfield_width == 0 ||
645+
(offset & (bitfield_align * 8 - 1)) +
646+
bitfield_width >
647+
bitfield_size * 8)
648+
{
649+
offset = align_to(offset, bitfield_align * 8);
650+
}
638651
}
639652
}
640653

@@ -677,6 +690,7 @@ where
677690
unit_size_in_bits,
678691
unit_align,
679692
bitfields_in_unit,
693+
packed,
680694
);
681695
}
682696

@@ -717,7 +731,7 @@ impl CompFields {
717731
}
718732
}
719733

720-
fn compute_bitfield_units(&mut self, ctx: &BindgenContext) {
734+
fn compute_bitfield_units(&mut self, ctx: &BindgenContext, packed: bool) {
721735
let raws = match *self {
722736
CompFields::BeforeComputingBitfieldUnits(ref mut raws) => {
723737
mem::replace(raws, vec![])
@@ -727,7 +741,7 @@ impl CompFields {
727741
}
728742
};
729743

730-
let result = raw_fields_to_fields_and_bitfield_units(ctx, raws);
744+
let result = raw_fields_to_fields_and_bitfield_units(ctx, raws, packed);
731745

732746
match result {
733747
Ok(fields_and_units) => {
@@ -1126,7 +1140,7 @@ impl CompInfo {
11261140
/// Do we see a virtual function during parsing?
11271141
/// Get the has_own_virtual_method boolean.
11281142
pub fn has_own_virtual_method(&self) -> bool {
1129-
return self.has_own_virtual_method;
1143+
self.has_own_virtual_method
11301144
}
11311145

11321146
/// Did we see a destructor when parsing this type?
@@ -1566,7 +1580,9 @@ impl CompInfo {
15661580

15671581
/// Compute this compound structure's bitfield allocation units.
15681582
pub fn compute_bitfield_units(&mut self, ctx: &BindgenContext) {
1569-
self.fields.compute_bitfield_units(ctx);
1583+
// TODO(emilio): If we could detect #pragma packed here we'd fix layout
1584+
// tests in divide-by-zero-in-struct-layout.rs
1585+
self.fields.compute_bitfield_units(ctx, self.packed_attr)
15701586
}
15711587

15721588
/// Assign for each anonymous field a generated name.

tests/expectations/tests/divide-by-zero-in-struct-layout.rs

+3-4
Original file line numberDiff line numberDiff line change
@@ -114,15 +114,14 @@ impl WithBitfield {
114114
#[repr(C, packed)]
115115
#[derive(Debug, Default, Copy, Clone)]
116116
pub struct WithBitfieldAndAttrPacked {
117-
pub _bitfield_1: __BindgenBitfieldUnit<[u8; 0usize], u8>,
117+
pub _bitfield_1: __BindgenBitfieldUnit<[u8; 1usize], u8>,
118118
pub a: ::std::os::raw::c_uint,
119-
pub __bindgen_padding_0: u8,
120119
}
121120
impl WithBitfieldAndAttrPacked {
122121
#[inline]
123-
pub fn new_bitfield_1() -> __BindgenBitfieldUnit<[u8; 0usize], u8> {
122+
pub fn new_bitfield_1() -> __BindgenBitfieldUnit<[u8; 1usize], u8> {
124123
let mut __bindgen_bitfield_unit: __BindgenBitfieldUnit<
125-
[u8; 0usize],
124+
[u8; 1usize],
126125
u8,
127126
> = Default::default();
128127
__bindgen_bitfield_unit
+179
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
/* automatically generated by rust-bindgen */
2+
3+
#![allow(
4+
dead_code,
5+
non_snake_case,
6+
non_camel_case_types,
7+
non_upper_case_globals
8+
)]
9+
10+
#[repr(C)]
11+
#[derive(Copy, Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
12+
pub struct __BindgenBitfieldUnit<Storage, Align> {
13+
storage: Storage,
14+
align: [Align; 0],
15+
}
16+
impl<Storage, Align> __BindgenBitfieldUnit<Storage, Align> {
17+
#[inline]
18+
pub const fn new(storage: Storage) -> Self {
19+
Self { storage, align: [] }
20+
}
21+
}
22+
impl<Storage, Align> __BindgenBitfieldUnit<Storage, Align>
23+
where
24+
Storage: AsRef<[u8]> + AsMut<[u8]>,
25+
{
26+
#[inline]
27+
pub fn get_bit(&self, index: usize) -> bool {
28+
debug_assert!(index / 8 < self.storage.as_ref().len());
29+
let byte_index = index / 8;
30+
let byte = self.storage.as_ref()[byte_index];
31+
let bit_index = if cfg!(target_endian = "big") {
32+
7 - (index % 8)
33+
} else {
34+
index % 8
35+
};
36+
let mask = 1 << bit_index;
37+
byte & mask == mask
38+
}
39+
#[inline]
40+
pub fn set_bit(&mut self, index: usize, val: bool) {
41+
debug_assert!(index / 8 < self.storage.as_ref().len());
42+
let byte_index = index / 8;
43+
let byte = &mut self.storage.as_mut()[byte_index];
44+
let bit_index = if cfg!(target_endian = "big") {
45+
7 - (index % 8)
46+
} else {
47+
index % 8
48+
};
49+
let mask = 1 << bit_index;
50+
if val {
51+
*byte |= mask;
52+
} else {
53+
*byte &= !mask;
54+
}
55+
}
56+
#[inline]
57+
pub fn get(&self, bit_offset: usize, bit_width: u8) -> u64 {
58+
debug_assert!(bit_width <= 64);
59+
debug_assert!(bit_offset / 8 < self.storage.as_ref().len());
60+
debug_assert!(
61+
(bit_offset + (bit_width as usize)) / 8 <=
62+
self.storage.as_ref().len()
63+
);
64+
let mut val = 0;
65+
for i in 0..(bit_width as usize) {
66+
if self.get_bit(i + bit_offset) {
67+
let index = if cfg!(target_endian = "big") {
68+
bit_width as usize - 1 - i
69+
} else {
70+
i
71+
};
72+
val |= 1 << index;
73+
}
74+
}
75+
val
76+
}
77+
#[inline]
78+
pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) {
79+
debug_assert!(bit_width <= 64);
80+
debug_assert!(bit_offset / 8 < self.storage.as_ref().len());
81+
debug_assert!(
82+
(bit_offset + (bit_width as usize)) / 8 <=
83+
self.storage.as_ref().len()
84+
);
85+
for i in 0..(bit_width as usize) {
86+
let mask = 1 << i;
87+
let val_bit_is_set = val & mask == mask;
88+
let index = if cfg!(target_endian = "big") {
89+
bit_width as usize - 1 - i
90+
} else {
91+
i
92+
};
93+
self.set_bit(index + bit_offset, val_bit_is_set);
94+
}
95+
}
96+
}
97+
#[repr(C, packed)]
98+
#[derive(Debug, Default, Copy, Clone)]
99+
pub struct Date {
100+
pub _bitfield_1: __BindgenBitfieldUnit<[u8; 3usize], u8>,
101+
}
102+
#[test]
103+
fn bindgen_test_layout_Date() {
104+
assert_eq!(
105+
::std::mem::size_of::<Date>(),
106+
3usize,
107+
concat!("Size of: ", stringify!(Date))
108+
);
109+
assert_eq!(
110+
::std::mem::align_of::<Date>(),
111+
1usize,
112+
concat!("Alignment of ", stringify!(Date))
113+
);
114+
}
115+
impl Date {
116+
#[inline]
117+
pub fn day(&self) -> ::std::os::raw::c_uchar {
118+
unsafe {
119+
::std::mem::transmute(self._bitfield_1.get(0usize, 5u8) as u8)
120+
}
121+
}
122+
#[inline]
123+
pub fn set_day(&mut self, val: ::std::os::raw::c_uchar) {
124+
unsafe {
125+
let val: u8 = ::std::mem::transmute(val);
126+
self._bitfield_1.set(0usize, 5u8, val as u64)
127+
}
128+
}
129+
#[inline]
130+
pub fn month(&self) -> ::std::os::raw::c_uchar {
131+
unsafe {
132+
::std::mem::transmute(self._bitfield_1.get(5usize, 4u8) as u8)
133+
}
134+
}
135+
#[inline]
136+
pub fn set_month(&mut self, val: ::std::os::raw::c_uchar) {
137+
unsafe {
138+
let val: u8 = ::std::mem::transmute(val);
139+
self._bitfield_1.set(5usize, 4u8, val as u64)
140+
}
141+
}
142+
#[inline]
143+
pub fn year(&self) -> ::std::os::raw::c_short {
144+
unsafe {
145+
::std::mem::transmute(self._bitfield_1.get(9usize, 15u8) as u16)
146+
}
147+
}
148+
#[inline]
149+
pub fn set_year(&mut self, val: ::std::os::raw::c_short) {
150+
unsafe {
151+
let val: u16 = ::std::mem::transmute(val);
152+
self._bitfield_1.set(9usize, 15u8, val as u64)
153+
}
154+
}
155+
#[inline]
156+
pub fn new_bitfield_1(
157+
day: ::std::os::raw::c_uchar,
158+
month: ::std::os::raw::c_uchar,
159+
year: ::std::os::raw::c_short,
160+
) -> __BindgenBitfieldUnit<[u8; 3usize], u8> {
161+
let mut __bindgen_bitfield_unit: __BindgenBitfieldUnit<
162+
[u8; 3usize],
163+
u8,
164+
> = Default::default();
165+
__bindgen_bitfield_unit.set(0usize, 5u8, {
166+
let day: u8 = unsafe { ::std::mem::transmute(day) };
167+
day as u64
168+
});
169+
__bindgen_bitfield_unit.set(5usize, 4u8, {
170+
let month: u8 = unsafe { ::std::mem::transmute(month) };
171+
month as u64
172+
});
173+
__bindgen_bitfield_unit.set(9usize, 15u8, {
174+
let year: u16 = unsafe { ::std::mem::transmute(year) };
175+
year as u64
176+
});
177+
__bindgen_bitfield_unit
178+
}
179+
}

tests/headers/divide-by-zero-in-struct-layout.h

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// bindgen-flags: --no-layout-tests
22
//
3-
// Unfortunately, we aren't translating the second and third structs correctly
4-
// yet. But we definitely shouldn't divide-by-zero when we see it...
3+
// Unfortunately, we aren't translating the third struct correctly yet. But we
4+
// definitely shouldn't divide-by-zero when we see it...
55
//
66
// Once we fix #981 we should remove the `--no-layout-tests`.
77

tests/headers/packed-bitfield.h

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
struct Date {
2+
unsigned char day: 5;
3+
unsigned char month: 4;
4+
signed short year: 15;
5+
} __attribute__((packed));

0 commit comments

Comments
 (0)