diff options
author | Flier Lu <flier.lu@gmail.com> | 2017-02-02 01:02:39 +0800 |
---|---|---|
committer | Flier Lu <flier.lu@nexusguard.com> | 2017-02-07 10:55:50 +0800 |
commit | 7fd707004b03a8110c6d82e82165f9e1a8ad6985 (patch) | |
tree | 94b170597b30a470edde2c6999a6e2a52e3db9c2 /src/codegen | |
parent | 7fa654c699b61250787e53a300357f0c58c55b18 (diff) |
add padding bytes to align strcture
Diffstat (limited to 'src/codegen')
-rw-r--r-- | src/codegen/mod.rs | 119 | ||||
-rw-r--r-- | src/codegen/struct_layout.rs | 193 |
2 files changed, 291 insertions, 21 deletions
diff --git a/src/codegen/mod.rs b/src/codegen/mod.rs index 3b0882af..f4da014e 100644 --- a/src/codegen/mod.rs +++ b/src/codegen/mod.rs @@ -1,6 +1,8 @@ mod helpers; +mod struct_layout; use self::helpers::{BlobTyBuilder, attributes}; +use self::struct_layout::StructLayoutTracker; use aster; use ir::annotations::FieldAccessorKind; @@ -21,6 +23,7 @@ use ir::var::Var; use std::borrow::Cow; use std::cell::Cell; +use std::cmp; use std::collections::{HashSet, VecDeque}; use std::collections::hash_map::{Entry, HashMap}; use std::fmt::Write; @@ -723,9 +726,9 @@ impl<'a> Bitfield<'a> { fn codegen_fields(self, ctx: &BindgenContext, fields: &mut Vec<ast::StructField>, - methods: &mut Vec<ast::ImplItem>) { + methods: &mut Vec<ast::ImplItem>) + -> Layout { use aster::struct_field::StructFieldBuilder; - use std::cmp; let mut total_width = self.fields .iter() .fold(0u32, |acc, f| acc + f.bitfield().unwrap()); @@ -736,10 +739,9 @@ impl<'a> Bitfield<'a> { debug_assert_eq!(total_width % 8, 0); let total_width_in_bytes = total_width as usize / 8; - let bitfield_type = - BlobTyBuilder::new(Layout::new(total_width_in_bytes, - total_width_in_bytes)) - .build(); + let bitfield_layout = Layout::new(total_width_in_bytes, + total_width_in_bytes); + let bitfield_type = BlobTyBuilder::new(bitfield_layout).build(); let field_name = format!("_bitfield_{}", self.index); let field_ident = ctx.ext_cx().ident_of(&field_name); let field = StructFieldBuilder::named(&field_name) @@ -805,6 +807,8 @@ impl<'a> Bitfield<'a> { methods.extend(items.into_iter()); offset += width; } + + bitfield_layout } } @@ -940,6 +944,7 @@ impl CodeGenerator for CompInfo { // Also, we need to generate the vtable in such a way it "inherits" from // the parent too. let mut fields = vec![]; + let mut struct_layout = StructLayoutTracker::new(ctx, self); if self.needs_explicit_vtable(ctx) { let vtable = Vtable::new(item.id(), self.methods(), self.base_members()); @@ -951,6 +956,8 @@ impl CodeGenerator for CompInfo { .pub_() .build_ty(vtable_type); + struct_layout.saw_vtable(); + fields.push(vtable_field); } @@ -985,6 +992,8 @@ impl CodeGenerator for CompInfo { format!("_base_{}", i) }; + struct_layout.saw_base(base_ty); + let field = StructFieldBuilder::named(field_name) .pub_() .build_ty(inner); @@ -1039,8 +1048,12 @@ impl CodeGenerator for CompInfo { let bitfield_fields = mem::replace(&mut current_bitfield_fields, vec![]); bitfield_count += 1; - Bitfield::new(bitfield_count, bitfield_fields) + let bitfield_layout = Bitfield::new(bitfield_count, + bitfield_fields) .codegen_fields(ctx, &mut fields, &mut methods); + + struct_layout.saw_bitfield(bitfield_layout); + current_bitfield_width = None; current_bitfield_layout = None; } @@ -1100,6 +1113,11 @@ impl CodeGenerator for CompInfo { } }; + if let Some(padding_field) = + struct_layout.pad_field(&field_name, field_ty, field.offset()) { + fields.push(padding_field); + } + let is_private = field.annotations() .private_fields() .unwrap_or(fields_should_be_private); @@ -1192,8 +1210,11 @@ impl CodeGenerator for CompInfo { let bitfield_fields = mem::replace(&mut current_bitfield_fields, vec![]); bitfield_count += 1; - Bitfield::new(bitfield_count, bitfield_fields) + let bitfield_layout = Bitfield::new(bitfield_count, + bitfield_fields) .codegen_fields(ctx, &mut fields, &mut methods); + + struct_layout.saw_bitfield(bitfield_layout); } debug_assert!(current_bitfield_fields.is_empty()); @@ -1203,6 +1224,9 @@ impl CodeGenerator for CompInfo { let field = StructFieldBuilder::named("bindgen_union_field") .pub_() .build_ty(ty); + + struct_layout.saw_union(layout); + fields.push(field); } @@ -1227,6 +1251,16 @@ impl CodeGenerator for CompInfo { warn!("Opaque type without layout! Expect dragons!"); } } + } else if !is_union && !self.is_unsized(ctx) { + if let Some(padding_field) = + layout.and_then(|layout| struct_layout.pad_struct(layout)) { + fields.push(padding_field); + } + + if let Some(align_field) = + layout.and_then(|layout| struct_layout.align_struct(layout)) { + fields.push(align_field); + } } // C requires every struct to be addressable, so what C compilers do is @@ -1296,7 +1330,7 @@ impl CodeGenerator for CompInfo { canonical_name); } - if applicable_template_args.is_empty() && !self.found_unknown_attr() { + if applicable_template_args.is_empty() { for var in self.inner_vars() { ctx.resolve_item(*var) .codegen(ctx, result, whitelisted_items, &()); @@ -1313,11 +1347,57 @@ impl CodeGenerator for CompInfo { ::$prefix::mem::align_of::<$ident>()); let size = layout.size; let align = layout.align; + + let check_struct_align = if align > mem::size_of::<*mut ()>() { + // FIXME when [RFC 1358](https://github.com/rust-lang/rust/issues/33626) ready + None + } else { + quote_item!(ctx.ext_cx(), + assert_eq!($align_of_expr, $align); + ) + }; + + // FIXME when [issue #465](https://github.com/servo/rust-bindgen/issues/465) ready + let too_many_base_vtables = self.base_members() + .iter() + .filter(|base| ctx.resolve_type(base.ty).has_vtable(ctx)) + .count() > + 1; + + let should_skip_field_offset_checks = item.is_opaque(ctx) || + too_many_base_vtables; + + let check_field_offset = if should_skip_field_offset_checks { + None + } else { + let type_name = ctx.rust_ident(&canonical_name); + + let asserts = self.fields() + .iter() + .filter(|field| field.bitfield().is_none()) + .flat_map(|field| { + field.name().and_then(|name| { + field.offset().and_then(|offset| { + let field_offset = offset / 8; + let field_name = ctx.rust_ident(name); + + quote_item!(ctx.ext_cx(), + assert_eq!(unsafe { &(*(0 as *const $type_name)).$field_name as *const _ as usize }, $field_offset); + ) + }) + }) + }).collect::<Vec<P<ast::Item>>>(); + + Some(asserts) + }; + let item = quote_item!(ctx.ext_cx(), #[test] fn $fn_name() { assert_eq!($size_of_expr, $size); - assert_eq!($align_of_expr, $align); + + $check_struct_align + $check_field_offset }) .unwrap(); result.push(item); @@ -2278,22 +2358,20 @@ impl CodeGenerator for ObjCInterface { // Collect the actual used argument names let arg_names: Vec<_> = fn_args.iter() - .map(|ref arg| { - match arg.pat.node { - ast::PatKind::Ident(_, ref spanning, _) => { - spanning.node.name.as_str().to_string() - } - _ => { - panic!("odd argument!"); - } + .map(|ref arg| match arg.pat.node { + ast::PatKind::Ident(_, ref spanning, _) => { + spanning.node.name.as_str().to_string() + } + _ => { + panic!("odd argument!"); } }) .collect(); let methods_and_args = ctx.rust_ident(&method.format_method_call(&arg_names)); - let body = - quote_stmt!(ctx.ext_cx(), msg_send![self, $methods_and_args]) + let body = quote_stmt!(ctx.ext_cx(), + msg_send![self, $methods_and_args]) .unwrap(); let block = ast::Block { stmts: vec![body], @@ -2729,5 +2807,4 @@ mod utils { } }).collect::<Vec<_>>() } - } diff --git a/src/codegen/struct_layout.rs b/src/codegen/struct_layout.rs new file mode 100644 index 00000000..3006a31a --- /dev/null +++ b/src/codegen/struct_layout.rs @@ -0,0 +1,193 @@ +//! Helpers for code generation that need struct layout + +use super::helpers::BlobTyBuilder; + +use aster::struct_field::StructFieldBuilder; + +use ir::comp::CompInfo; +use ir::context::BindgenContext; +use ir::layout::Layout; +use ir::ty::Type; +use std::cmp; +use std::mem; + +use syntax::ast; + +/// Trace the layout of struct. +pub struct StructLayoutTracker<'a, 'ctx: 'a> { + ctx: &'a BindgenContext<'ctx>, + comp: &'a CompInfo, + latest_offset: usize, + padding_count: usize, + latest_field_layout: Option<Layout>, + max_field_align: usize, +} + +impl<'a, 'ctx> StructLayoutTracker<'a, 'ctx> { + pub fn new(ctx: &'a BindgenContext<'ctx>, comp: &'a CompInfo) -> Self { + StructLayoutTracker { + ctx: ctx, + comp: comp, + latest_offset: 0, + padding_count: 0, + latest_field_layout: None, + max_field_align: 0, + } + } + + pub fn saw_vtable(&mut self) { + let ptr_size = mem::size_of::<*mut ()>(); + self.latest_offset += ptr_size; + self.latest_field_layout = Some(Layout::new(ptr_size, ptr_size)); + self.max_field_align = ptr_size; + } + + pub fn saw_base(&mut self, base_ty: &Type) { + self.align_to_latest_field(); + + if let Some(layout) = base_ty.layout(self.ctx) { + 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 fn saw_bitfield(&mut self, layout: Layout) { + self.align_to_latest_field(); + + 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 fn saw_union(&mut self, layout: Layout) { + self.align_to_latest_field(); + + 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 fn pad_field(&mut self, + field_name: &str, + field_ty: &Type, + field_offset: Option<usize>) + -> Option<ast::StructField> { + field_ty.layout(self.ctx).and_then(|field_layout| { + self.align_to_latest_field(); + + let padding_layout = if self.comp.packed() { + None + } else { + let calculated_layout = field_ty.as_comp() + .and_then(|comp| comp.calc_layout(self.ctx)) + .unwrap_or(field_layout); + + let align = cmp::min(calculated_layout.align, mem::size_of::<*mut ()>()); + + let (padding_bytes, need_padding) = match field_offset { + Some(offset) if offset / 8 > self.latest_offset => { + (offset / 8 - self.latest_offset, true) + } + _ => { + (self.padding_bytes(field_layout), (self.latest_offset % field_layout.align) != 0) + } + }; + + self.latest_offset += padding_bytes; + + debug!("align field {} to {}/{} with {} padding bytes {:?}, calculated {:?}", + field_name, + self.latest_offset, + field_offset.unwrap_or(0) / 8, + padding_bytes, + field_layout, + calculated_layout); + + if need_padding && + (padding_bytes > calculated_layout.align || + field_layout.align > mem::size_of::<*mut ()>()) { + Some(Layout::new(padding_bytes, align)) + } else { + None + } + }; + + self.latest_offset += field_ty.calc_size(self.ctx).unwrap_or(field_layout.size); + + self.latest_field_layout = Some(field_layout); + self.max_field_align = cmp::max(self.max_field_align, field_layout.align); + + padding_layout.map(|layout| self.padding_field(layout)) + }) + } + + pub fn pad_struct(&mut self, layout: Layout) -> Option<ast::StructField> { + if layout.size < self.latest_offset { + warn!("calculate struct layout incorrect, too more {} bytes", + self.latest_offset - layout.size); + + None + } else { + let padding_bytes = layout.size - self.latest_offset; + let struct_align = cmp::min(layout.align, + mem::size_of::<*mut ()>()); + + if padding_bytes > struct_align || + (layout.align > mem::size_of::<*mut ()>() && padding_bytes > 0) { + let padding_align = if self.comp.packed() { + 1 + } else { + cmp::min(1 << padding_bytes.trailing_zeros(), + mem::size_of::<*mut ()>()) + }; + + Some(self.padding_field(Layout::new(padding_bytes, padding_align))) + } else { + None + } + } + } + + pub fn align_struct(&self, layout: Layout) -> Option<ast::StructField> { + if self.max_field_align < layout.align && + layout.align <= mem::size_of::<*mut ()>() { + let ty = BlobTyBuilder::new(Layout::new(0, layout.align)).build(); + + Some(StructFieldBuilder::named("__bindgen_align") + .pub_() + .build_ty(ty)) + } else { + None + } + } + + fn padding_bytes(&self, layout: Layout) -> usize { + if self.latest_offset % layout.align == 0 { + 0 + } else { + layout.align - (self.latest_offset % layout.align) + } + } + + fn padding_field(&mut self, layout: Layout) -> ast::StructField { + let ty = BlobTyBuilder::new(layout).build(); + let padding_count = self.padding_count; + + self.padding_count += 1; + + let padding_field_name = format!("__bindgen_padding_{}", padding_count); + + self.max_field_align = cmp::max(self.max_field_align, layout.align); + + StructFieldBuilder::named(padding_field_name).pub_().build_ty(ty) + } + + fn align_to_latest_field(&mut self) { + if self.comp.packed() { + // skip to align field when packed + } else if let Some(layout) = self.latest_field_layout { + self.latest_offset += self.padding_bytes(layout); + } + } +} |