Skip to content

Commit 761dffc

Browse files
author
bors-servo
authored
Auto merge of #1136 - fitzgen:pragma-pack-bandaid, r=emilio
Detect `#pragma pack(...)` and make `pack(n)` where `n > 1` opaque This is a bandaid for #537. It does *not* fix the underlying issue, which requires `#[repr(packed = "N")]` support in Rust. However, it does make sure that we don't generate type definitions with the wrong layout, or fail our generated layout tests. r? @emilio or @pepyakin
2 parents f059ede + 79dbe8b commit 761dffc

File tree

7 files changed

+109
-63
lines changed

7 files changed

+109
-63
lines changed

src/codegen/mod.rs

+5-3
Original file line numberDiff line numberDiff line change
@@ -1394,7 +1394,9 @@ impl CodeGenerator for CompInfo {
13941394

13951395
let used_template_params = item.used_template_params(ctx);
13961396

1397-
let mut packed = self.packed();
1397+
let ty = item.expect_type();
1398+
let layout = ty.layout(ctx);
1399+
let mut packed = self.is_packed(ctx, &layout);
13981400

13991401
// generate tuple struct if struct or union is a forward declaration,
14001402
// skip for now if template parameters are needed.
@@ -1431,7 +1433,7 @@ impl CodeGenerator for CompInfo {
14311433
let is_opaque = item.is_opaque(ctx, &());
14321434
let mut fields = vec![];
14331435
let mut struct_layout =
1434-
StructLayoutTracker::new(ctx, self, &canonical_name);
1436+
StructLayoutTracker::new(ctx, self, ty, &canonical_name);
14351437

14361438
if !is_opaque {
14371439
if item.has_vtable_ptr(ctx) {
@@ -1618,7 +1620,7 @@ impl CodeGenerator for CompInfo {
16181620
if let Some(comment) = item.comment(ctx) {
16191621
attributes.push(attributes::doc(comment));
16201622
}
1621-
if packed {
1623+
if packed && !is_opaque {
16221624
attributes.push(attributes::repr_list(&["C", "packed"]));
16231625
} else {
16241626
attributes.push(attributes::repr("C"));

src/codegen/struct_layout.rs

+6-3
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ pub struct StructLayoutTracker<'a> {
1616
name: &'a str,
1717
ctx: &'a BindgenContext,
1818
comp: &'a CompInfo,
19+
is_packed: bool,
1920
latest_offset: usize,
2021
padding_count: usize,
2122
latest_field_layout: Option<Layout>,
@@ -81,12 +82,14 @@ impl<'a> StructLayoutTracker<'a> {
8182
pub fn new(
8283
ctx: &'a BindgenContext,
8384
comp: &'a CompInfo,
85+
ty: &'a Type,
8486
name: &'a str,
8587
) -> Self {
8688
StructLayoutTracker {
8789
name: name,
8890
ctx: ctx,
8991
comp: comp,
92+
is_packed: comp.is_packed(ctx, &ty.layout(ctx)),
9093
latest_offset: 0,
9194
padding_count: 0,
9295
latest_field_layout: None,
@@ -180,7 +183,7 @@ impl<'a> StructLayoutTracker<'a> {
180183

181184
let will_merge_with_bitfield = self.align_to_latest_field(field_layout);
182185

183-
let padding_layout = if self.comp.packed() {
186+
let padding_layout = if self.is_packed {
184187
None
185188
} else {
186189
let padding_bytes = match field_offset {
@@ -269,7 +272,7 @@ impl<'a> StructLayoutTracker<'a> {
269272
self.latest_field_layout.unwrap().align) ||
270273
layout.align > mem::size_of::<*mut ()>())
271274
{
272-
let layout = if self.comp.packed() {
275+
let layout = if self.is_packed {
273276
Layout::new(padding_bytes, 1)
274277
} else if self.last_field_was_bitfield ||
275278
layout.align > mem::size_of::<*mut ()>()
@@ -316,7 +319,7 @@ impl<'a> StructLayoutTracker<'a> {
316319
///
317320
/// This is just to avoid doing the same check also in pad_field.
318321
fn align_to_latest_field(&mut self, new_field_layout: Layout) -> bool {
319-
if self.comp.packed() {
322+
if self.is_packed {
320323
// Skip to align fields when packed.
321324
return false;
322325
}

src/ir/comp.rs

+54-13
Original file line numberDiff line numberDiff line change
@@ -974,8 +974,8 @@ pub struct CompInfo {
974974
/// size_t)
975975
has_non_type_template_params: bool,
976976

977-
/// Whether this struct layout is packed.
978-
packed: bool,
977+
/// Whether we saw `__attribute__((packed))` on or within this type.
978+
packed_attr: bool,
979979

980980
/// Used to know if we've found an opaque attribute that could cause us to
981981
/// generate a type with invalid layout. This is explicitly used to avoid us
@@ -1007,7 +1007,7 @@ impl CompInfo {
10071007
has_destructor: false,
10081008
has_nonempty_base: false,
10091009
has_non_type_template_params: false,
1010-
packed: false,
1010+
packed_attr: false,
10111011
found_unknown_attr: false,
10121012
is_forward_declaration: false,
10131013
}
@@ -1261,7 +1261,7 @@ impl CompInfo {
12611261
}
12621262
}
12631263
CXCursor_PackedAttr => {
1264-
ci.packed = true;
1264+
ci.packed_attr = true;
12651265
}
12661266
CXCursor_TemplateTypeParameter => {
12671267
let param = Item::type_param(None, cur, ctx)
@@ -1439,8 +1439,31 @@ impl CompInfo {
14391439
}
14401440

14411441
/// Is this compound type packed?
1442-
pub fn packed(&self) -> bool {
1443-
self.packed
1442+
pub fn is_packed(&self, ctx: &BindgenContext, layout: &Option<Layout>) -> bool {
1443+
if self.packed_attr {
1444+
return true
1445+
}
1446+
1447+
// Even though `libclang` doesn't expose `#pragma packed(...)`, we can
1448+
// detect it through its effects.
1449+
if let Some(ref parent_layout) = *layout {
1450+
if self.fields().iter().any(|f| match *f {
1451+
Field::Bitfields(ref unit) => {
1452+
unit.layout().align > parent_layout.align
1453+
}
1454+
Field::DataMember(ref data) => {
1455+
let field_ty = ctx.resolve_type(data.ty());
1456+
field_ty.layout(ctx).map_or(false, |field_ty_layout| {
1457+
field_ty_layout.align > parent_layout.align
1458+
})
1459+
}
1460+
}) {
1461+
info!("Found a struct that was defined within `#pragma packed(...)`");
1462+
return true;
1463+
}
1464+
}
1465+
1466+
false
14441467
}
14451468

14461469
/// Returns true if compound type has been forward declared
@@ -1504,8 +1527,8 @@ impl DotAttributes for CompInfo {
15041527
)?;
15051528
}
15061529

1507-
if self.packed {
1508-
writeln!(out, "<tr><td>packed</td><td>true</td></tr>")?;
1530+
if self.packed_attr {
1531+
writeln!(out, "<tr><td>packed_attr</td><td>true</td></tr>")?;
15091532
}
15101533

15111534
if self.is_forward_declaration {
@@ -1528,15 +1551,17 @@ impl DotAttributes for CompInfo {
15281551
}
15291552

15301553
impl IsOpaque for CompInfo {
1531-
type Extra = ();
1554+
type Extra = Option<Layout>;
15321555

1533-
fn is_opaque(&self, ctx: &BindgenContext, _: &()) -> bool {
1534-
// Early return to avoid extra computation
1556+
fn is_opaque(&self, ctx: &BindgenContext, layout: &Option<Layout>) -> bool {
15351557
if self.has_non_type_template_params {
15361558
return true
15371559
}
15381560

1539-
self.fields().iter().any(|f| match *f {
1561+
// Bitfields with a width that is larger than their unit's width have
1562+
// some strange things going on, and the best we can do is make the
1563+
// whole struct opaque.
1564+
if self.fields().iter().any(|f| match *f {
15401565
Field::DataMember(_) => {
15411566
false
15421567
},
@@ -1548,7 +1573,23 @@ impl IsOpaque for CompInfo {
15481573
bf.width() / 8 > bitfield_layout.size as u32
15491574
})
15501575
}
1551-
})
1576+
}) {
1577+
return true;
1578+
}
1579+
1580+
// We don't have `#[repr(packed = "N")]` in Rust yet, so the best we can
1581+
// do is make this struct opaque.
1582+
//
1583+
// See https://github.com/rust-lang-nursery/rust-bindgen/issues/537 and
1584+
// https://github.com/rust-lang/rust/issues/33158
1585+
if self.is_packed(ctx, layout) && layout.map_or(false, |l| l.align > 1) {
1586+
warn!("Found a type that is both packed and aligned to greater than \
1587+
1; Rust doesn't have `#[repr(packed = \"N\")]` yet, so we \
1588+
are treating it as opaque");
1589+
return true;
1590+
}
1591+
1592+
false
15521593
}
15531594
}
15541595

src/ir/ty.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -378,7 +378,7 @@ impl IsOpaque for Type {
378378
TypeKind::TemplateInstantiation(ref inst) => {
379379
inst.is_opaque(ctx, item)
380380
}
381-
TypeKind::Comp(ref comp) => comp.is_opaque(ctx, &()),
381+
TypeKind::Comp(ref comp) => comp.is_opaque(ctx, &self.layout),
382382
TypeKind::ResolvedTypeRef(to) => to.is_opaque(ctx, &()),
383383
_ => false,
384384
}

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

+2-1
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,12 @@ impl WithBitfieldAndAttrPacked {
3030
0
3131
}
3232
}
33-
#[repr(C)]
33+
#[repr(C, packed)]
3434
#[derive(Debug, Default, Copy, Clone)]
3535
pub struct WithBitfieldAndPacked {
3636
pub _bitfield_1: [u8; 0usize],
3737
pub a: ::std::os::raw::c_uint,
38+
pub __bindgen_padding_0: u8,
3839
}
3940
impl WithBitfieldAndPacked {
4041
#[inline]

tests/expectations/tests/layout.rs

+6-42
Original file line numberDiff line numberDiff line change
@@ -5,49 +5,8 @@
55

66

77
#[repr(C)]
8-
#[derive(Default)]
9-
pub struct __IncompleteArrayField<T>(::std::marker::PhantomData<T>);
10-
impl<T> __IncompleteArrayField<T> {
11-
#[inline]
12-
pub fn new() -> Self {
13-
__IncompleteArrayField(::std::marker::PhantomData)
14-
}
15-
#[inline]
16-
pub unsafe fn as_ptr(&self) -> *const T {
17-
::std::mem::transmute(self)
18-
}
19-
#[inline]
20-
pub unsafe fn as_mut_ptr(&mut self) -> *mut T {
21-
::std::mem::transmute(self)
22-
}
23-
#[inline]
24-
pub unsafe fn as_slice(&self, len: usize) -> &[T] {
25-
::std::slice::from_raw_parts(self.as_ptr(), len)
26-
}
27-
#[inline]
28-
pub unsafe fn as_mut_slice(&mut self, len: usize) -> &mut [T] {
29-
::std::slice::from_raw_parts_mut(self.as_mut_ptr(), len)
30-
}
31-
}
32-
impl<T> ::std::fmt::Debug for __IncompleteArrayField<T> {
33-
fn fmt(&self, fmt: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
34-
fmt.write_str("__IncompleteArrayField")
35-
}
36-
}
37-
impl<T> ::std::clone::Clone for __IncompleteArrayField<T> {
38-
#[inline]
39-
fn clone(&self) -> Self {
40-
Self::new()
41-
}
42-
}
43-
impl<T> ::std::marker::Copy for __IncompleteArrayField<T> {}
44-
#[repr(C, packed)]
45-
#[derive(Debug, Default)]
468
pub struct header {
47-
pub proto: ::std::os::raw::c_char,
48-
pub size: ::std::os::raw::c_uint,
49-
pub data: __IncompleteArrayField<::std::os::raw::c_uchar>,
50-
pub __bindgen_padding_0: [u8; 11usize],
9+
pub _bindgen_opaque_blob: [u8; 16usize],
5110
}
5211
#[test]
5312
fn bindgen_test_layout_header() {
@@ -57,3 +16,8 @@ fn bindgen_test_layout_header() {
5716
concat!("Size of: ", stringify!(header))
5817
);
5918
}
19+
impl Default for header {
20+
fn default() -> Self {
21+
unsafe { ::std::mem::zeroed() }
22+
}
23+
}

tests/headers/issue-537.h

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/// This should not be opaque; we can see the attributes and can pack the
2+
/// struct.
3+
struct AlignedToOne {
4+
int i;
5+
} __attribute__ ((packed,aligned(1)));
6+
7+
/// This should be opaque because although we can see the attributes, Rust
8+
/// doesn't have `#[repr(packed = "N")]` yet.
9+
struct AlignedToTwo {
10+
int i;
11+
} __attribute__ ((packed,aligned(2)));
12+
13+
#pragma pack(1)
14+
15+
/// This should not be opaque because although `libclang` doesn't give us the
16+
/// `#pragma pack(1)`, we can detect that alignment is 1 and add
17+
/// `#[repr(packed)]` to the struct ourselves.
18+
struct PackedToOne {
19+
int x;
20+
int y;
21+
};
22+
23+
#pragma pack()
24+
25+
#pragma pack(2)
26+
27+
/// In this case, even if we can detect the weird alignment triggered by
28+
/// `#pragma pack(2)`, we can't do anything about it because Rust doesn't have
29+
/// `#[repr(packed = "N")]`. Therefore, we must make it opaque.
30+
struct PackedToTwo {
31+
int x;
32+
int y;
33+
};
34+
35+
#pragma pack()

0 commit comments

Comments
 (0)