diff --git a/src/codegen/mod.rs b/src/codegen/mod.rs index 6f8a451a2a..515ebf1733 100644 --- a/src/codegen/mod.rs +++ b/src/codegen/mod.rs @@ -1797,6 +1797,14 @@ impl CodeGenerator for CompInfo { (), ); } + // Check whether an explicit padding field is needed + // at the end. + if let Some(comp_layout) = layout { + fields.extend( + struct_layout + .add_tail_padding(&canonical_name, comp_layout), + ); + } } if is_opaque { diff --git a/src/codegen/struct_layout.rs b/src/codegen/struct_layout.rs index 2e4b973591..1c6b977b99 100644 --- a/src/codegen/struct_layout.rs +++ b/src/codegen/struct_layout.rs @@ -217,8 +217,11 @@ impl<'a> StructLayoutTracker<'a> { 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 = padding_bytes >= field_layout.align || + let need_padding = force_padding || + padding_bytes >= field_layout.align || field_layout.align > MAX_GUARANTEED_ALIGN; debug!( @@ -236,11 +239,14 @@ impl<'a> StructLayoutTracker<'a> { 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, - cmp::min(field_layout.align, MAX_GUARANTEED_ALIGN), - )) + Some(Layout::new(padding_bytes, padding_align)) } else { None } @@ -262,6 +268,32 @@ impl<'a> StructLayoutTracker<'a> { padding_layout.map(|layout| self.padding_field(layout)) } + pub 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; + } + + 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 fn pad_struct( &mut self, layout: Layout, diff --git a/src/lib.rs b/src/lib.rs index 1ac053b7f8..1c51c8ae42 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -562,6 +562,10 @@ impl Builder { output_vector.push("--c-naming".into()); } + if self.options.force_explicit_padding { + output_vector.push("--explicit-padding".into()); + } + // Add clang arguments output_vector.push("--".into()); @@ -1419,6 +1423,17 @@ impl Builder { self } + /// If true, always emit explicit padding fields. + /// + /// If a struct needs to be serialized in its native format (padding bytes + /// and all), for example writing it to a file or sending it on the network, + /// then this should be enabled, as anything reading the padding bytes of + /// a struct may lead to Undefined Behavior. + pub fn explicit_padding(mut self, doit: bool) -> Self { + self.options.force_explicit_padding = doit; + self + } + /// Generate the Rust bindings using the options built up thus far. pub fn generate(mut self) -> Result { // Add any extra arguments from the environment to the clang command line. @@ -1937,6 +1952,9 @@ struct BindgenOptions { /// Generate types with C style naming. c_naming: bool, + + /// Always output explicit padding fields + force_explicit_padding: bool, } /// TODO(emilio): This is sort of a lie (see the error message that results from @@ -2079,6 +2097,7 @@ impl Default for BindgenOptions { respect_cxx_access_specs: false, translate_enum_integer_types: false, c_naming: false, + force_explicit_padding: false, } } } diff --git a/src/options.rs b/src/options.rs index cbdc945ebb..eeeb5aae8c 100644 --- a/src/options.rs +++ b/src/options.rs @@ -516,6 +516,9 @@ where Arg::with_name("c-naming") .long("c-naming") .help("Generate types with C style naming."), + Arg::with_name("explicit-padding") + .long("explicit-padding") + .help("Always output explicit padding fields."), ]) // .args() .get_matches_from(args); @@ -960,6 +963,10 @@ where builder = builder.c_naming(true); } + if matches.is_present("explicit-padding") { + builder = builder.explicit_padding(true); + } + let verbose = matches.is_present("verbose"); Ok((builder, output, verbose)) diff --git a/tests/expectations/tests/explicit-padding.rs b/tests/expectations/tests/explicit-padding.rs new file mode 100644 index 0000000000..e395e5d245 --- /dev/null +++ b/tests/expectations/tests/explicit-padding.rs @@ -0,0 +1,65 @@ +#![allow( + dead_code, + non_snake_case, + non_camel_case_types, + non_upper_case_globals +)] + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct pad_me { + pub first: u8, + pub __bindgen_padding_0: [u8; 3usize], + pub second: u32, + pub third: u16, + pub __bindgen_padding_1: [u8; 2usize], +} +#[test] +fn bindgen_test_layout_pad_me() { + assert_eq!( + ::std::mem::size_of::(), + 12usize, + concat!("Size of: ", stringify!(pad_me)) + ); + assert_eq!( + ::std::mem::align_of::(), + 4usize, + concat!("Alignment of ", stringify!(pad_me)) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).first as *const _ as usize + }, + 0usize, + concat!( + "Offset of field: ", + stringify!(pad_me), + "::", + stringify!(first) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).second as *const _ as usize + }, + 4usize, + concat!( + "Offset of field: ", + stringify!(pad_me), + "::", + stringify!(second) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).third as *const _ as usize + }, + 8usize, + concat!( + "Offset of field: ", + stringify!(pad_me), + "::", + stringify!(third) + ) + ); +} diff --git a/tests/headers/explicit-padding.h b/tests/headers/explicit-padding.h new file mode 100644 index 0000000000..d228961df2 --- /dev/null +++ b/tests/headers/explicit-padding.h @@ -0,0 +1,11 @@ +// bindgen-flags: --explicit-padding + +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; + +struct pad_me { + uint8_t first; + uint32_t second; + uint16_t third; +};