diff options
author | Emilio Cobos Álvarez <emilio@crisal.io> | 2020-11-28 01:33:32 +0100 |
---|---|---|
committer | Emilio Cobos Álvarez <emilio@crisal.io> | 2020-11-28 03:14:51 +0100 |
commit | 19142ac6b3d8ee4fc5686d6f30b77e660026e528 (patch) | |
tree | 7be336cb2bfd34e6a8c844b868efb4332aba9c4f /src | |
parent | 6a5726eac514b49ec8a9f8360ed5d0d73da9feb7 (diff) |
struct_layout: Fix field offset computation for packed(n) structs.
This can cause unnecessary padding to be computed otherwise at the end
of the struct.
With repr(packed(n)), a field can have padding to adjacent fields as
long as its alignment is less than n. So reuse the code we have to align
to a field layout, aligning to the struct layout instead.
Fixes #1934
Diffstat (limited to 'src')
-rw-r--r-- | src/codegen/mod.rs | 2 | ||||
-rw-r--r-- | src/codegen/struct_layout.rs | 35 | ||||
-rw-r--r-- | src/ir/comp.rs | 6 |
3 files changed, 28 insertions, 15 deletions
diff --git a/src/codegen/mod.rs b/src/codegen/mod.rs index 15aea22a..0d93c491 100644 --- a/src/codegen/mod.rs +++ b/src/codegen/mod.rs @@ -1650,7 +1650,7 @@ impl CodeGenerator for CompInfo { let ty = item.expect_type(); let layout = ty.layout(ctx); - let mut packed = self.is_packed(ctx, &layout); + let mut packed = self.is_packed(ctx, layout.as_ref()); let canonical_name = item.canonical_name(ctx); let canonical_ident = ctx.rust_ident(&canonical_name); diff --git a/src/codegen/struct_layout.rs b/src/codegen/struct_layout.rs index 54dfd86e..4536e889 100644 --- a/src/codegen/struct_layout.rs +++ b/src/codegen/struct_layout.rs @@ -18,6 +18,7 @@ pub struct StructLayoutTracker<'a> { ctx: &'a BindgenContext, comp: &'a CompInfo, is_packed: bool, + known_type_layout: Option<Layout>, latest_offset: usize, padding_count: usize, latest_field_layout: Option<Layout>, @@ -86,11 +87,14 @@ impl<'a> StructLayoutTracker<'a> { ty: &'a Type, name: &'a str, ) -> Self { + let known_type_layout = ty.layout(ctx); + let is_packed = comp.is_packed(ctx, known_type_layout.as_ref()); StructLayoutTracker { name, ctx, comp, - is_packed: comp.is_packed(ctx, &ty.layout(ctx)), + is_packed, + known_type_layout, latest_offset: 0, padding_count: 0, latest_field_layout: None, @@ -180,23 +184,32 @@ impl<'a> StructLayoutTracker<'a> { let will_merge_with_bitfield = self.align_to_latest_field(field_layout); + let padding_bytes = match field_offset { + Some(offset) if offset / 8 > self.latest_offset => { + offset / 8 - self.latest_offset + } + _ => { + if will_merge_with_bitfield || field_layout.align == 0 { + 0 + } else if !self.is_packed { + self.padding_bytes(field_layout) + } else if let Some(l) = self.known_type_layout { + self.padding_bytes(l) + } else { + 0 + } + } + }; + + self.latest_offset += padding_bytes; + let padding_layout = if self.is_packed { None } else { - let padding_bytes = match field_offset { - Some(offset) if offset / 8 > self.latest_offset => { - offset / 8 - self.latest_offset - } - _ if will_merge_with_bitfield || field_layout.align == 0 => 0, - _ => self.padding_bytes(field_layout), - }; - // Otherwise the padding is useless. let need_padding = padding_bytes >= field_layout.align || field_layout.align > MAX_GUARANTEED_ALIGN; - self.latest_offset += padding_bytes; - debug!( "Offset: <padding>: {} -> {}", self.latest_offset - padding_bytes, diff --git a/src/ir/comp.rs b/src/ir/comp.rs index 22c124fa..1d257d20 100644 --- a/src/ir/comp.rs +++ b/src/ir/comp.rs @@ -1576,7 +1576,7 @@ impl CompInfo { pub fn is_packed( &self, ctx: &BindgenContext, - layout: &Option<Layout>, + layout: Option<&Layout>, ) -> bool { if self.packed_attr { return true; @@ -1584,7 +1584,7 @@ impl CompInfo { // Even though `libclang` doesn't expose `#pragma packed(...)`, we can // detect it through its effects. - if let Some(ref parent_layout) = *layout { + if let Some(parent_layout) = layout { if self.fields().iter().any(|f| match *f { Field::Bitfields(ref unit) => { unit.layout().align > parent_layout.align @@ -1739,7 +1739,7 @@ impl IsOpaque for CompInfo { // // See https://github.com/rust-lang/rust-bindgen/issues/537 and // https://github.com/rust-lang/rust/issues/33158 - if self.is_packed(ctx, layout) && + if self.is_packed(ctx, layout.as_ref()) && layout.map_or(false, |l| l.align > 1) { warn!("Found a type that is both packed and aligned to greater than \ |