//! Helpers for code generation that need struct layout 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::FieldVisibilityKind; use proc_macro2::{Ident, Span}; use std::cmp; const MAX_GUARANTEED_ALIGN: usize = 8; /// Trace the layout of struct. #[derive(Debug)] pub(crate) struct StructLayoutTracker<'a> { name: &'a str, ctx: &'a BindgenContext, comp: &'a CompInfo, is_packed: bool, known_type_layout: Option, is_rust_union: bool, can_copy_union_fields: bool, latest_offset: usize, padding_count: usize, latest_field_layout: Option, max_field_align: usize, last_field_was_bitfield: bool, visibility: FieldVisibilityKind, last_field_was_flexible_array: bool, } /// Returns a size aligned to a given value. pub(crate) fn align_to(size: usize, align: usize) -> usize { if align == 0 { return size; } let rem = size % align; if rem == 0 { return size; } size + align - rem } /// Returns the lower power of two byte count that can hold at most n bits. pub(crate) fn bytes_from_bits_pow2(mut n: usize) -> usize { if n == 0 { return 0; } if n <= 8 { return 1; } if !n.is_power_of_two() { n = n.next_power_of_two(); } n / 8 } #[test] fn test_align_to() { assert_eq!(align_to(1, 1), 1); assert_eq!(align_to(1, 2), 2); assert_eq!(align_to(1, 4), 4); assert_eq!(align_to(5, 1), 5); assert_eq!(align_to(17, 4), 20); } #[test] fn test_bytes_from_bits_pow2() { assert_eq!(bytes_from_bits_pow2(0), 0); for i in 1..9 { assert_eq!(bytes_from_bits_pow2(i), 1); } for i in 9..17 { assert_eq!(bytes_from_bits_pow2(i), 2); } for i in 17..33 { assert_eq!(bytes_from_bits_pow2(i), 4); } } impl<'a> StructLayoutTracker<'a> { pub(crate) fn new( ctx: &'a BindgenContext, comp: &'a CompInfo, ty: &'a Type, name: &'a str, visibility: FieldVisibilityKind, is_packed: bool, ) -> Self { let known_type_layout = ty.layout(ctx); let (is_rust_union, can_copy_union_fields) = comp.is_rust_union(ctx, known_type_layout.as_ref(), name); StructLayoutTracker { name, ctx, comp, visibility, is_packed, known_type_layout, is_rust_union, can_copy_union_fields, latest_offset: 0, padding_count: 0, latest_field_layout: None, max_field_align: 0, last_field_was_bitfield: false, last_field_was_flexible_array: false, } } pub(crate) fn can_copy_union_fields(&self) -> bool { self.can_copy_union_fields } pub(crate) fn is_rust_union(&self) -> bool { self.is_rust_union } pub(crate) fn saw_flexible_array(&mut self) { self.last_field_was_flexible_array = true; } pub(crate) fn saw_vtable(&mut self) { debug!("saw vtable for {}", self.name); let ptr_size = self.ctx.target_pointer_size(); self.latest_offset += ptr_size; self.latest_field_layout = Some(Layout::new(ptr_size, ptr_size)); self.max_field_align = ptr_size; } pub(crate) fn saw_base(&mut self, base_ty: &Type) { debug!("saw base for {}", self.name); if let Some(layout) = base_ty.layout(self.ctx) { self.align_to_latest_field(layout); self.latest_offset += self.padding_bytes(layout) + layout.size; self.latest_field_layout = Some(layout); self.max_field_align = cmp::max(self.max_field_align, layout.align); } } pub(crate) fn saw_bitfield_unit(&mut self, layout: Layout) { debug!("saw bitfield unit for {}: {:?}", self.name, layout); self.align_to_latest_field(layout); self.latest_offset += layout.size; debug!( "Offset: : {} -> {}", self.latest_offset - layout.size, self.latest_offset ); self.latest_field_layout = Some(layout); self.last_field_was_bitfield = true; self.max_field_align = cmp::max(self.max_field_align, layout.align); } /// Returns a padding field if necessary for a given new field _before_ /// adding that field. pub(crate) fn saw_field( &mut self, field_name: &str, 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; } } } self.saw_field_with_layout(field_name, field_layout, field_offset) } pub(crate) fn saw_field_with_layout( &mut self, field_name: &str, field_layout: Layout, field_offset: Option, ) -> Option { let will_merge_with_bitfield = self.align_to_latest_field(field_layout); let is_union = self.comp.is_union(); 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 || is_union { 0 } else if !self.is_packed { self.padding_bytes(field_layout) } else if let Some(mut l) = self.known_type_layout { if field_layout.align < l.align { l.align = field_layout.align; } self.padding_bytes(l) } else { 0 } } }; self.latest_offset += padding_bytes; let padding_layout = if self.is_packed || is_union { None } else { 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; debug!( "Offset: : {} -> {}", self.latest_offset - padding_bytes, self.latest_offset ); debug!( "align field {} to {}/{} with {} padding bytes {:?}", field_name, self.latest_offset, field_offset.unwrap_or(0) / 8, padding_bytes, field_layout ); let padding_align = if force_padding { 1 } else { cmp::min(field_layout.align, MAX_GUARANTEED_ALIGN) }; if need_padding && padding_bytes != 0 { Some(Layout::new(padding_bytes, padding_align)) } else { None } }; self.latest_offset += field_layout.size; self.latest_field_layout = Some(field_layout); self.max_field_align = cmp::max(self.max_field_align, field_layout.align); self.last_field_was_bitfield = false; debug!( "Offset: {}: {} -> {}", field_name, self.latest_offset - field_layout.size, self.latest_offset ); padding_layout.map(|layout| self.padding_field(layout)) } pub(crate) fn add_tail_padding( &mut self, comp_name: &str, comp_layout: Layout, ) -> Option { // Only emit an padding field at the end of a struct if the // user configures explicit padding. if !self.ctx.options().force_explicit_padding { return None; } // Padding doesn't make sense for rust unions. if self.is_rust_union { return None; } // Also doesn't make sense for structs with flexible array members if self.last_field_was_flexible_array { return None; } if self.latest_offset == comp_layout.size { // This struct does not contain tail padding. return None; } trace!( "need a tail padding field for {}: offset {} -> size {}", comp_name, self.latest_offset, comp_layout.size ); let size = comp_layout.size - self.latest_offset; Some(self.padding_field(Layout::new(size, 0))) } pub(crate) fn pad_struct( &mut self, layout: Layout, ) -> Option { debug!( "pad_struct:\n\tself = {:#?}\n\tlayout = {:#?}", self, layout ); if layout.size < self.latest_offset { warn!( "Calculated wrong layout for {}, too more {} bytes", self.name, self.latest_offset - layout.size ); return None; } let padding_bytes = layout.size - self.latest_offset; if padding_bytes == 0 { return None; } let repr_align = self.ctx.options().rust_features().repr_align; // We always pad to get to the correct size if the struct is one of // those we can't align properly. // // Note that if the last field we saw was a bitfield, we may need to pad // regardless, because bitfields don't respect alignment as strictly as // 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) { let layout = if self.is_packed { Layout::new(padding_bytes, 1) } else if self.last_field_was_bitfield || layout.align > MAX_GUARANTEED_ALIGN { // We've already given up on alignment here. Layout::for_size(self.ctx, padding_bytes) } else { Layout::new(padding_bytes, layout.align) }; debug!("pad bytes to struct {}, {:?}", self.name, layout); Some(self.padding_field(layout)) } else { None } } pub(crate) fn requires_explicit_align(&self, layout: Layout) -> bool { let repr_align = self.ctx.options().rust_features().repr_align; // 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 { return true; } if self.max_field_align >= layout.align { 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 } fn padding_bytes(&self, layout: Layout) -> usize { align_to(self.latest_offset, layout.align) - self.latest_offset } fn padding_field(&mut self, layout: Layout) -> proc_macro2::TokenStream { let ty = helpers::blob(self.ctx, layout); let padding_count = self.padding_count; self.padding_count += 1; let padding_field_name = Ident::new( &format!("__bindgen_padding_{}", padding_count), Span::call_site(), ); self.max_field_align = cmp::max(self.max_field_align, layout.align); let vis = super::access_specifier(self.visibility); quote! { #vis #padding_field_name : #ty , } } /// Returns whether the new field is known to merge with a bitfield. /// /// This is just to avoid doing the same check also in pad_field. fn align_to_latest_field(&mut self, new_field_layout: Layout) -> bool { if self.is_packed { // Skip to align fields when packed. return false; } let layout = match self.latest_field_layout { Some(l) => l, None => return false, }; // If it was, we may or may not need to align, depending on what the // current field alignment and the bitfield size and alignment are. debug!( "align_to_bitfield? {}: {:?} {:?}", self.last_field_was_bitfield, layout, new_field_layout ); // Avoid divide-by-zero errors if align is 0. let align = cmp::max(1, layout.align); if self.last_field_was_bitfield && new_field_layout.align <= layout.size % align && new_field_layout.size <= layout.size % align { // The new field will be coalesced into some of the remaining bits. // // FIXME(emilio): I think this may not catch everything? debug!("Will merge with bitfield"); return true; } // Else, just align the obvious way. self.latest_offset += self.padding_bytes(layout); false } }