Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extraneous padding in struct with bitfield and flexible array member #1589

Closed
lopopolo opened this issue Jul 3, 2019 · 5 comments · Fixed by #1592
Closed

Extraneous padding in struct with bitfield and flexible array member #1589

lopopolo opened this issue Jul 3, 2019 · 5 comments · Fixed by #1592

Comments

@lopopolo
Copy link
Contributor

lopopolo commented Jul 3, 2019

Input C/C++ Header

struct {
  char a : 1;
  void *b[]
};

Bindgen Invocation

    let bindings = bindgen::Builder::default()
        .header("./fail.h")
        .generate()
        .unwrap();
    bindings
        .write_to_file("./src/fail.rs")
        .unwrap();

Actual Results

$ RUST_BACKTRACE=1 cargo test bindgen_test_layout__bindgen_ty_1
    Finished dev [unoptimized + debuginfo] target(s) in 0.20s
     Running /Users/lopopolo/dev/repos/ferrocarril/target/debug/deps/mruby_sys-2b55e6d363978cf4

running 1 test
test fail::bindgen_test_layout__bindgen_ty_1 ... FAILED

failures:

---- fail::bindgen_test_layout__bindgen_ty_1 stdout ----
thread 'fail::bindgen_test_layout__bindgen_ty_1' panicked at 'assertion failed: `(left == right)`
  left: `16`,
 right: `8`: Size of: _bindgen_ty_1', mruby-sys/src/fail.rs:130:5
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
stack backtrace:
   0: std::sys::unix::backtrace::tracing::imp::unwind_backtrace
             at src/libstd/sys/unix/backtrace/tracing/gcc_s.rs:39
   1: std::sys_common::backtrace::_print
             at src/libstd/sys_common/backtrace.rs:71
   2: std::panicking::default_hook::{{closure}}
             at src/libstd/sys_common/backtrace.rs:59
             at src/libstd/panicking.rs:197
   3: std::panicking::default_hook
             at src/libstd/panicking.rs:208
   4: <std::panicking::begin_panic::PanicPayload<A> as core::panic::BoxMeUp>::get
             at src/libstd/panicking.rs:474
   5: std::panicking::continue_panic_fmt
             at src/libstd/panicking.rs:381
   6: std::panicking::try::do_call
             at src/libstd/panicking.rs:336
   7: mruby_sys::fail::bindgen_test_layout__bindgen_ty_1
             at mruby-sys/src/fail.rs:130
   8: mruby_sys::fail::bindgen_test_layout__bindgen_ty_1::{{closure}}
             at mruby-sys/src/fail.rs:129
   9: core::ops::function::FnOnce::call_once
             at /rustc/3c235d5600393dfe6c36eeed34042efad8d4f26e/src/libcore/ops/function.rs:231
  10: <alloc::boxed::Box<F> as core::ops::function::FnOnce<A>>::call_once
             at /rustc/3c235d5600393dfe6c36eeed34042efad8d4f26e/src/liballoc/boxed.rs:702
  11: panic_unwind::dwarf::eh::read_encoded_pointer
             at src/libpanic_unwind/lib.rs:87
  12: test::run_test::run_test_inner::{{closure}}
             at /rustc/3c235d5600393dfe6c36eeed34042efad8d4f26e/src/libstd/panicking.rs:272
             at /rustc/3c235d5600393dfe6c36eeed34042efad8d4f26e/src/libstd/panic.rs:388
             at src/libtest/lib.rs:1468


failures:
    fail::bindgen_test_layout__bindgen_ty_1

test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 73 filtered out

error: test failed, to rerun pass '--lib'

and/or

/* automatically generated by rust-bindgen */

#[repr(C)]
#[derive(Copy, Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct __BindgenBitfieldUnit<Storage, Align> {
    storage: Storage,
    align: [Align; 0],
}
impl<Storage, Align> __BindgenBitfieldUnit<Storage, Align> {
    #[inline]
    pub const fn new(storage: Storage) -> Self {
        Self { storage, align: [] }
    }
}
impl<Storage, Align> __BindgenBitfieldUnit<Storage, Align>
where
    Storage: AsRef<[u8]> + AsMut<[u8]>,
{
    #[inline]
    pub fn get_bit(&self, index: usize) -> bool {
        debug_assert!(index / 8 < self.storage.as_ref().len());
        let byte_index = index / 8;
        let byte = self.storage.as_ref()[byte_index];
        let bit_index = if cfg!(target_endian = "big") {
            7 - (index % 8)
        } else {
            index % 8
        };
        let mask = 1 << bit_index;
        byte & mask == mask
    }
    #[inline]
    pub fn set_bit(&mut self, index: usize, val: bool) {
        debug_assert!(index / 8 < self.storage.as_ref().len());
        let byte_index = index / 8;
        let byte = &mut self.storage.as_mut()[byte_index];
        let bit_index = if cfg!(target_endian = "big") {
            7 - (index % 8)
        } else {
            index % 8
        };
        let mask = 1 << bit_index;
        if val {
            *byte |= mask;
        } else {
            *byte &= !mask;
        }
    }
    #[inline]
    pub fn get(&self, bit_offset: usize, bit_width: u8) -> u64 {
        debug_assert!(bit_width <= 64);
        debug_assert!(bit_offset / 8 < self.storage.as_ref().len());
        debug_assert!((bit_offset + (bit_width as usize)) / 8 <= self.storage.as_ref().len());
        let mut val = 0;
        for i in 0..(bit_width as usize) {
            if self.get_bit(i + bit_offset) {
                let index = if cfg!(target_endian = "big") {
                    bit_width as usize - 1 - i
                } else {
                    i
                };
                val |= 1 << index;
            }
        }
        val
    }
    #[inline]
    pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) {
        debug_assert!(bit_width <= 64);
        debug_assert!(bit_offset / 8 < self.storage.as_ref().len());
        debug_assert!((bit_offset + (bit_width as usize)) / 8 <= self.storage.as_ref().len());
        for i in 0..(bit_width as usize) {
            let mask = 1 << i;
            let val_bit_is_set = val & mask == mask;
            let index = if cfg!(target_endian = "big") {
                bit_width as usize - 1 - i
            } else {
                i
            };
            self.set_bit(index + bit_offset, val_bit_is_set);
        }
    }
}
#[repr(C)]
#[derive(Default)]
pub struct __IncompleteArrayField<T>(::std::marker::PhantomData<T>, [T; 0]);
impl<T> __IncompleteArrayField<T> {
    #[inline]
    pub const fn new() -> Self {
        __IncompleteArrayField(::std::marker::PhantomData, [])
    }
    #[inline]
    pub unsafe fn as_ptr(&self) -> *const T {
        ::std::mem::transmute(self)
    }
    #[inline]
    pub unsafe fn as_mut_ptr(&mut self) -> *mut T {
        ::std::mem::transmute(self)
    }
    #[inline]
    pub unsafe fn as_slice(&self, len: usize) -> &[T] {
        ::std::slice::from_raw_parts(self.as_ptr(), len)
    }
    #[inline]
    pub unsafe fn as_mut_slice(&mut self, len: usize) -> &mut [T] {
        ::std::slice::from_raw_parts_mut(self.as_mut_ptr(), len)
    }
}
impl<T> ::std::fmt::Debug for __IncompleteArrayField<T> {
    fn fmt(&self, fmt: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
        fmt.write_str("__IncompleteArrayField")
    }
}
impl<T> ::std::clone::Clone for __IncompleteArrayField<T> {
    #[inline]
    fn clone(&self) -> Self {
        Self::new()
    }
}
#[repr(C)]
#[repr(align(8))]
#[derive(Debug)]
pub struct _bindgen_ty_1 {
    pub _bitfield_1: __BindgenBitfieldUnit<[u8; 1usize], u8>,
    pub b: __IncompleteArrayField<*mut ::std::os::raw::c_void>,
    pub __bindgen_padding_0: [u8; 7usize],
}
#[test]
fn bindgen_test_layout__bindgen_ty_1() {
    assert_eq!(
        ::std::mem::size_of::<_bindgen_ty_1>(),
        8usize,
        concat!("Size of: ", stringify!(_bindgen_ty_1))
    );
    assert_eq!(
        ::std::mem::align_of::<_bindgen_ty_1>(),
        8usize,
        concat!("Alignment of ", stringify!(_bindgen_ty_1))
    );
}
impl _bindgen_ty_1 {
    #[inline]
    pub fn a(&self) -> ::std::os::raw::c_char {
        unsafe { ::std::mem::transmute(self._bitfield_1.get(0usize, 1u8) as u8) }
    }
    #[inline]
    pub fn set_a(&mut self, val: ::std::os::raw::c_char) {
        unsafe {
            let val: u8 = ::std::mem::transmute(val);
            self._bitfield_1.set(0usize, 1u8, val as u64)
        }
    }
    #[inline]
    pub fn new_bitfield_1(a: ::std::os::raw::c_char) -> __BindgenBitfieldUnit<[u8; 1usize], u8> {
        let mut __bindgen_bitfield_unit: __BindgenBitfieldUnit<[u8; 1usize], u8> =
            Default::default();
        __bindgen_bitfield_unit.set(0usize, 1u8, {
            let a: u8 = unsafe { ::std::mem::transmute(a) };
            a as u64
        });
        __bindgen_bitfield_unit
    }
}

Expected Results

Expect layout test to pass.

@lopopolo lopopolo changed the title Failed layout test with bitfield and array Extraneous padding in struct with bitfield and flexible array member Jul 3, 2019
@emilio
Copy link
Contributor

emilio commented Jul 10, 2019

Thanks for the report, this is a bug in bindgen. StructLayoutTracker::pad_field should pad to the closest alignment, but probably we're not finding the right struct layout for the incomplete array.

@emilio
Copy link
Contributor

emilio commented Jul 10, 2019

Seems we're failing to find the layout of the incomplete array, should be fixable.

@emilio emilio self-assigned this Jul 10, 2019
emilio added a commit to emilio/rust-bindgen that referenced this issue Jul 10, 2019
So as to not pad stuff incorrectly, or needlessly add extra alignment, for
example.

Fixes rust-lang#1589
@emilio
Copy link
Contributor

emilio commented Jul 10, 2019

Fix at #1592.

@emilio
Copy link
Contributor

emilio commented Jul 10, 2019

(And sorry for the lag triaging this, was on vacation when it was filed, and apparently lost track of this mail :))

@lopopolo
Copy link
Contributor Author

Thanks for the fix and release of 0.51.0 @emilio. Happy weekend.

lopopolo added a commit to artichoke/artichoke that referenced this issue Jul 28, 2019
This pulls in the fix for rust-lang/rust-bindgen#1589 which unblocks
enabling layout tests for mruby-sys.
lopopolo added a commit to artichoke/artichoke that referenced this issue Jul 28, 2019
This pulls in the fix for rust-lang/rust-bindgen#1589 which unblocks
enabling layout tests for mruby-sys.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants