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
This commit is contained in:
Emilio Cobos Álvarez
2020-11-28 01:33:32 +01:00
parent 5d3de3d76c
commit efc166106d
6 changed files with 84 additions and 16 deletions
+1 -1
View File
@@ -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);
+24 -11
View File
@@ -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,
+3 -3
View File
@@ -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 \
@@ -130,7 +130,6 @@ impl WithBitfieldAndAttrPacked {
pub struct WithBitfieldAndPacked {
pub _bitfield_1: __BindgenBitfieldUnit<[u8; 0usize], u8>,
pub a: ::std::os::raw::c_uint,
pub __bindgen_padding_0: u8,
}
impl WithBitfieldAndPacked {
#[inline]
@@ -0,0 +1,48 @@
#![allow(
dead_code,
non_snake_case,
non_camel_case_types,
non_upper_case_globals
)]
#[repr(C, packed(2))]
#[derive(Debug, Default, Copy, Clone)]
pub struct Packed {
pub a: ::std::os::raw::c_char,
pub b: ::std::os::raw::c_short,
pub c: ::std::os::raw::c_char,
pub d: ::std::os::raw::c_int,
}
#[test]
fn bindgen_test_layout_Packed() {
assert_eq!(
::std::mem::size_of::<Packed>(),
10usize,
concat!("Size of: ", stringify!(Packed))
);
assert_eq!(
::std::mem::align_of::<Packed>(),
2usize,
concat!("Alignment of ", stringify!(Packed))
);
assert_eq!(
unsafe { &(*(::std::ptr::null::<Packed>())).a as *const _ as usize },
0usize,
concat!("Offset of field: ", stringify!(Packed), "::", stringify!(a))
);
assert_eq!(
unsafe { &(*(::std::ptr::null::<Packed>())).b as *const _ as usize },
2usize,
concat!("Offset of field: ", stringify!(Packed), "::", stringify!(b))
);
assert_eq!(
unsafe { &(*(::std::ptr::null::<Packed>())).c as *const _ as usize },
4usize,
concat!("Offset of field: ", stringify!(Packed), "::", stringify!(c))
);
assert_eq!(
unsafe { &(*(::std::ptr::null::<Packed>())).d as *const _ as usize },
6usize,
concat!("Offset of field: ", stringify!(Packed), "::", stringify!(d))
);
}
+8
View File
@@ -0,0 +1,8 @@
#pragma pack(push, 2)
struct Packed {
char a;
short b;
char c;
int d;
};
#pragma pack(pop)