From aaaf271868dcefa6194217466d972cdf7c1d08ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Sun, 6 Sep 2026 19:48:47 +0200 Subject: [PATCH 1/4] codegen: Simplify some conditions about repr(align). --- bindgen/codegen/struct_layout.rs | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/bindgen/codegen/struct_layout.rs b/bindgen/codegen/struct_layout.rs index 3dfd076c25..6e7f9471d0 100644 --- a/bindgen/codegen/struct_layout.rs +++ b/bindgen/codegen/struct_layout.rs @@ -310,8 +310,6 @@ impl<'a> StructLayoutTracker<'a> { return None; } - let repr_align = true; - // We always pad to get to the correct size if the struct is one of // those we can't align properly. // @@ -320,8 +318,7 @@ impl<'a> StructLayoutTracker<'a> { // other fields. if padding_bytes >= layout.align || (self.last_field_was_bitfield && - padding_bytes >= self.latest_field_layout.unwrap().align) || - (!repr_align && layout.align > MAX_GUARANTEED_ALIGN) + padding_bytes >= self.latest_field_layout.unwrap().align) { let layout = if self.is_packed { Layout::new(padding_bytes, 1) @@ -343,13 +340,11 @@ impl<'a> StructLayoutTracker<'a> { } pub(crate) fn requires_explicit_align(&self, layout: Layout) -> bool { - let repr_align = true; - // Always force explicit repr(align) for stuff more than 16-byte aligned // to work-around https://github.com/rust-lang/rust/issues/54341. // // Worst-case this just generates redundant alignment attributes. - if repr_align && self.max_field_align >= 16 { + if self.max_field_align >= 16 { return true; } @@ -357,9 +352,7 @@ impl<'a> StructLayoutTracker<'a> { return false; } - // We can only generate up-to a 8-bytes of alignment unless we support - // repr(align). - repr_align || layout.align <= MAX_GUARANTEED_ALIGN + true } fn padding_bytes(&self, layout: Layout) -> usize { From 845258659da34491b41bc483a8213fa63f80fa86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Sun, 6 Sep 2026 19:32:33 +0200 Subject: [PATCH 2/4] codegen: Don't force padding for over-aligned fields. It's not necessary now that we support repr(align) properly. There's probably some edge cases where we still get the padding wrong tho. Fixes #3406. --- .../tests/expectations/tests/issue-3406.rs | 27 +++++++++++++++++++ .../tests/layout_large_align_field.rs | 9 ------- bindgen-tests/tests/headers/issue-3406.h | 8 ++++++ bindgen/codegen/struct_layout.rs | 5 ++-- 4 files changed, 37 insertions(+), 12 deletions(-) create mode 100644 bindgen-tests/tests/expectations/tests/issue-3406.rs create mode 100644 bindgen-tests/tests/headers/issue-3406.h diff --git a/bindgen-tests/tests/expectations/tests/issue-3406.rs b/bindgen-tests/tests/expectations/tests/issue-3406.rs new file mode 100644 index 0000000000..258a4aea1c --- /dev/null +++ b/bindgen-tests/tests/expectations/tests/issue-3406.rs @@ -0,0 +1,27 @@ +#![allow(dead_code, non_snake_case, non_camel_case_types, non_upper_case_globals)] +#[repr(C)] +#[repr(align(16))] +#[derive(Debug, Default, Copy, Clone)] +pub struct Inner { + pub byte: ::std::os::raw::c_char, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of Inner"][::std::mem::size_of::() - 16usize]; + ["Alignment of Inner"][::std::mem::align_of::() - 16usize]; + ["Offset of field: Inner::byte"][::std::mem::offset_of!(Inner, byte) - 0usize]; +}; +#[repr(C)] +#[repr(align(16))] +#[derive(Debug, Default, Copy, Clone)] +pub struct Outer { + pub before: ::std::os::raw::c_int, + pub inner: Inner, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of Outer"][::std::mem::size_of::() - 32usize]; + ["Alignment of Outer"][::std::mem::align_of::() - 16usize]; + ["Offset of field: Outer::before"][::std::mem::offset_of!(Outer, before) - 0usize]; + ["Offset of field: Outer::inner"][::std::mem::offset_of!(Outer, inner) - 16usize]; +}; diff --git a/bindgen-tests/tests/expectations/tests/layout_large_align_field.rs b/bindgen-tests/tests/expectations/tests/layout_large_align_field.rs index 0ccd40a5f8..a3a4077044 100644 --- a/bindgen-tests/tests/expectations/tests/layout_large_align_field.rs +++ b/bindgen-tests/tests/expectations/tests/layout_large_align_field.rs @@ -1,12 +1,4 @@ #![allow(dead_code, non_snake_case, non_camel_case_types, non_upper_case_globals)] -#[derive(PartialEq, Eq, Copy, Clone, Debug, Hash)] -#[repr(C, align(8))] -pub struct __BindgenOpaqueArray8(pub T); -impl Default for __BindgenOpaqueArray8<[T; N]> { - fn default() -> Self { - Self([::default(); N]) - } -} #[repr(C)] #[derive(Default)] pub struct __IncompleteArrayField(::std::marker::PhantomData, [T; 0]); @@ -294,7 +286,6 @@ pub struct rte_ip_frag_tbl { pub last: *mut ip_frag_pkt, ///< LRU list for table entries. pub lru: ip_pkt_list, - pub __bindgen_padding_0: __BindgenOpaqueArray8<[u8; 8usize]>, ///< statistics counters. pub stat: ip_frag_tbl_stat, ///< hash table. diff --git a/bindgen-tests/tests/headers/issue-3406.h b/bindgen-tests/tests/headers/issue-3406.h new file mode 100644 index 0000000000..aa3f0c1585 --- /dev/null +++ b/bindgen-tests/tests/headers/issue-3406.h @@ -0,0 +1,8 @@ +struct __attribute__((aligned(16))) Inner { + char byte; +}; + +struct Outer { + int before; + struct Inner inner; +}; diff --git a/bindgen/codegen/struct_layout.rs b/bindgen/codegen/struct_layout.rs index 6e7f9471d0..a077ebc4b9 100644 --- a/bindgen/codegen/struct_layout.rs +++ b/bindgen/codegen/struct_layout.rs @@ -206,9 +206,8 @@ impl<'a> StructLayoutTracker<'a> { let force_padding = self.ctx.options().force_explicit_padding; // Otherwise the padding is useless. - let need_padding = force_padding || - padding_bytes >= field_layout.align || - field_layout.align > MAX_GUARANTEED_ALIGN; + let need_padding = + force_padding || padding_bytes >= field_layout.align; debug!( "Offset: : {} -> {}", From 7b8fec846cebcf36eda180ae4a1d1b10150c2d3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Sun, 6 Sep 2026 19:39:08 +0200 Subject: [PATCH 3/4] codegen: Don't force padding to be aligned. Since we know precisely the amount of bytes we need. --- bindgen-tests/tests/expectations/tests/layout_array.rs | 8 ++++---- bindgen-tests/tests/expectations/tests/unknown_attr.rs | 10 +--------- bindgen/codegen/struct_layout.rs | 7 +------ 3 files changed, 6 insertions(+), 19 deletions(-) diff --git a/bindgen-tests/tests/expectations/tests/layout_array.rs b/bindgen-tests/tests/expectations/tests/layout_array.rs index dd34681a5d..cac7141a09 100644 --- a/bindgen-tests/tests/expectations/tests/layout_array.rs +++ b/bindgen-tests/tests/expectations/tests/layout_array.rs @@ -1,8 +1,8 @@ #![allow(dead_code, non_snake_case, non_camel_case_types, non_upper_case_globals)] #[derive(PartialEq, Eq, Copy, Clone, Debug, Hash)] -#[repr(C, align(8))] -pub struct __BindgenOpaqueArray8(pub T); -impl Default for __BindgenOpaqueArray8<[T; N]> { +#[repr(C)] +pub struct __BindgenOpaqueArray(pub T); +impl Default for __BindgenOpaqueArray<[T; N]> { fn default() -> Self { Self([::default(); N]) } @@ -133,7 +133,7 @@ pub struct rte_mempool_ops_table { pub sl: rte_spinlock_t, ///< Number of used ops structs in the table. pub num_ops: u32, - pub __bindgen_padding_0: __BindgenOpaqueArray8<[u8; 56usize]>, + pub __bindgen_padding_0: __BindgenOpaqueArray<[u8; 56usize]>, /// Storage for all possible ops structs. pub ops: [rte_mempool_ops; 16usize], } diff --git a/bindgen-tests/tests/expectations/tests/unknown_attr.rs b/bindgen-tests/tests/expectations/tests/unknown_attr.rs index d749dad977..5bfd121d1c 100644 --- a/bindgen-tests/tests/expectations/tests/unknown_attr.rs +++ b/bindgen-tests/tests/expectations/tests/unknown_attr.rs @@ -1,18 +1,10 @@ #![allow(dead_code, non_snake_case, non_camel_case_types, non_upper_case_globals)] -#[derive(PartialEq, Eq, Copy, Clone, Debug, Hash)] -#[repr(C, align(8))] -pub struct __BindgenOpaqueArray8(pub T); -impl Default for __BindgenOpaqueArray8<[T; N]> { - fn default() -> Self { - Self([::default(); N]) - } -} #[repr(C)] #[repr(align(16))] #[derive(Debug, Default, Copy, Clone)] pub struct max_align_t { pub __clang_max_align_nonce1: ::std::os::raw::c_longlong, - pub __bindgen_padding_0: __BindgenOpaqueArray8<[u8; 8usize]>, + pub __bindgen_padding_0: [u8; 8usize], pub __clang_max_align_nonce2: ::std::os::raw::c_longlong, } #[allow(clippy::unnecessary_operation, clippy::identity_op)] diff --git a/bindgen/codegen/struct_layout.rs b/bindgen/codegen/struct_layout.rs index a077ebc4b9..1aa34c5e4a 100644 --- a/bindgen/codegen/struct_layout.rs +++ b/bindgen/codegen/struct_layout.rs @@ -221,12 +221,7 @@ impl<'a> StructLayoutTracker<'a> { field_offset.unwrap_or(0) / 8, ); - let padding_align = if force_padding { - 1 - } else { - cmp::min(field_layout.align, MAX_GUARANTEED_ALIGN) - }; - + let padding_align = 1; if need_padding && padding_bytes != 0 { Some(Layout::new(padding_bytes, padding_align)) } else { From b8c297aafaad01def923d1982bb2166f81d1837f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Sun, 6 Sep 2026 20:36:35 +0200 Subject: [PATCH 4/4] codegen: Remove hack that is no longer needed now that we have repr(align) everywhere. --- .../tests/expectations/tests/layout_array.rs | 9 ------- bindgen/codegen/struct_layout.rs | 24 ++----------------- 2 files changed, 2 insertions(+), 31 deletions(-) diff --git a/bindgen-tests/tests/expectations/tests/layout_array.rs b/bindgen-tests/tests/expectations/tests/layout_array.rs index cac7141a09..9a197b0232 100644 --- a/bindgen-tests/tests/expectations/tests/layout_array.rs +++ b/bindgen-tests/tests/expectations/tests/layout_array.rs @@ -1,12 +1,4 @@ #![allow(dead_code, non_snake_case, non_camel_case_types, non_upper_case_globals)] -#[derive(PartialEq, Eq, Copy, Clone, Debug, Hash)] -#[repr(C)] -pub struct __BindgenOpaqueArray(pub T); -impl Default for __BindgenOpaqueArray<[T; N]> { - fn default() -> Self { - Self([::default(); N]) - } -} pub const RTE_CACHE_LINE_SIZE: u32 = 64; pub const RTE_MEMPOOL_OPS_NAMESIZE: u32 = 32; pub const RTE_MEMPOOL_MAX_OPS_IDX: u32 = 16; @@ -133,7 +125,6 @@ pub struct rte_mempool_ops_table { pub sl: rte_spinlock_t, ///< Number of used ops structs in the table. pub num_ops: u32, - pub __bindgen_padding_0: __BindgenOpaqueArray<[u8; 56usize]>, /// Storage for all possible ops structs. pub ops: [rte_mempool_ops; 16usize], } diff --git a/bindgen/codegen/struct_layout.rs b/bindgen/codegen/struct_layout.rs index 1aa34c5e4a..2e84304b92 100644 --- a/bindgen/codegen/struct_layout.rs +++ b/bindgen/codegen/struct_layout.rs @@ -5,7 +5,7 @@ use super::helpers; use crate::ir::comp::CompInfo; use crate::ir::context::BindgenContext; use crate::ir::layout::Layout; -use crate::ir::ty::{Type, TypeKind}; +use crate::ir::ty::Type; use crate::FieldVisibilityKind; use proc_macro2::{Ident, Span}; use std::cmp; @@ -142,27 +142,7 @@ impl<'a> StructLayoutTracker<'a> { field_ty: &Type, field_offset: Option, ) -> Option { - let mut field_layout = field_ty.layout(self.ctx)?; - - if let TypeKind::Array(inner, len) = - *field_ty.canonical_type(self.ctx).kind() - { - // FIXME(emilio): As an _ultra_ hack, we correct the layout returned - // by arrays of structs that have a bigger alignment than what we - // can support. - // - // This means that the structs in the array are super-unsafe to - // access, since they won't be properly aligned, but there's not too - // much we can do about it. - if let Some(layout) = self.ctx.resolve_type(inner).layout(self.ctx) - { - if layout.align > MAX_GUARANTEED_ALIGN { - field_layout.size = - align_to(layout.size, layout.align) * len; - field_layout.align = MAX_GUARANTEED_ALIGN; - } - } - } + let field_layout = field_ty.layout(self.ctx)?; self.saw_field_with_layout(field_name, field_layout, field_offset) }