Skip to content

Bitfields should be packed into earlier fields if alignment allows so. #1377

Open
@xxks-kkk

Description

@xxks-kkk

Per the discussion with @emilio on issue #687, I create this new issue for better tracking.

Input C/C++ Header

I have the following structure definition in C

/**
 * Data used by Set Features / Get Features \ref SPDK_NVME_FEAT_ASYNC_EVENT_CONFIGURATION
 */
union spdk_nvme_feat_async_event_configuration {
	uint32_t raw;
	struct {
		union spdk_nvme_critical_warning_state crit_warn;
		uint32_t ns_attr_notice		: 1;
		uint32_t fw_activation_notice	: 1;
		uint32_t telemetry_log_notice	: 1;
		uint32_t reserved		: 21;
	} bits;
};
SPDK_STATIC_ASSERT(sizeof(union spdk_nvme_feat_async_event_configuration) == 4, "Incorrect size");

It is translated as

/// Data used by Set Features / Get Features \ref SPDK_NVME_FEAT_ASYNC_EVENT_CONFIGURATION
#[repr(C)]
#[derive(Copy, Clone)]
pub union spdk_nvme_feat_async_event_configuration { pub raw: u32, pub bits: spdk_nvme_feat_async_event_configuration__bindgen_ty_1, _bindgen_union_align: u32 }

#[repr(C)]
#[derive(Copy, Clone)]
pub struct spdk_nvme_feat_async_event_configuration__bindgen_ty_1 { pub crit_warn: spdk_nvme_critical_warning_state, pub _bitfield_1: __BindgenBitfieldUnit<[u8; 4usize], u32>, pub __bindgen_align: [u32; 0usize] }

However, during cargo test, the following test is failed

    bindgen_test_layout_spdk_nvme_feat_async_event_configuration
    bindgen_test_layout_spdk_nvme_feat_async_event_configuration__bindgen_ty_1

The corresponding test is

#[test]
fn bindgen_test_layout_spdk_nvme_feat_async_event_configuration() {
    assert_eq!(::std::mem::size_of::<spdk_nvme_feat_async_event_configuration>(), 4usize, concat!( "Size of: " , stringify ! ( spdk_nvme_feat_async_event_configuration ) ));
    assert_eq!(::std::mem::align_of::<spdk_nvme_feat_async_event_configuration>(), 4usize, concat!( "Alignment of " , stringify ! ( spdk_nvme_feat_async_event_configuration ) ));
    assert_eq!(unsafe { &(*(::std::ptr::null::<spdk_nvme_feat_async_event_configuration>())).raw as *const _ as usize }, 0usize, concat!( "Offset of field: " , stringify ! ( spdk_nvme_feat_async_event_configuration ) , "::" , stringify ! ( raw ) ));
    assert_eq!(unsafe { &(*(::std::ptr::null::<spdk_nvme_feat_async_event_configuration>())).bits as *const _ as usize }, 0usize, concat!( "Offset of field: " , stringify ! ( spdk_nvme_feat_async_event_configuration ) , "::" , stringify ! ( bits ) ));
}

and the failure happens at

thread 'bindgen_test_layout_spdk_nvme_feat_async_event_configuration' panicked at 'assertion failed: `(left == right)`
  left: `8`,
 right: `4`: Size of: spdk_nvme_feat_async_event_configuration', /home/zeyuanhu/rustfs/rust-spdk/target/debug/build/rust-spdk-3c1fe41f551c8d38/out/bindings.rs:14005:5
thread 'bindgen_test_layout_spdk_nvme_feat_async_event_configuration__bindgen_ty_1' panicked at 'assertion failed: `(left == right)`
  left: `8`,
 right: `4`: Size of: spdk_nvme_feat_async_event_configuration__bindgen_ty_1', /home/zeyuanhu/rustfs/rust-spdk/target/debug/build/rust-spdk-3c1fe41f551c8d38/out/bindings.rs:13938:5

From what I see, the union is 4 bytes on C but becomes 8 bytes in its corresponding Rust translation.

The spdk_nvme_critical_warning_state inside spdk_nvme_feat_async_event_configuration reported above is defined as

union spdk_nvme_critical_warning_state {
	uint8_t		raw;

	struct {
		uint8_t	available_spare		: 1;
		uint8_t	temperature		: 1;
		uint8_t	device_reliability	: 1;
		uint8_t	read_only		: 1;
		uint8_t	volatile_memory_backup	: 1;
		uint8_t	reserved		: 3;
	} bits;
};
SPDK_STATIC_ASSERT(sizeof(union spdk_nvme_critical_warning_state) == 1, "Incorrect size");

Bindgen Invocation

    let bindings = bindgen::Builder::default()
        .clang_arg(include_path)
        // The input header we would like to generate
        // bindings for.
        .header("src/wrapper.h")
        .blacklist_type("IPPORT_.*")   // https://github.com/rust-lang-nursery/rust-bindgen/issues/687
        .blacklist_type("max_align_t") // https://github.com/rust-lang-nursery/rust-bindgen/issues/550
        // Finish the builder and generate the bindings.
        .generate()
        // Unwrap the Result and panic on failure.
        .expect("Unable to generate bindings");

Actual Results

See above.

Expected Results

union structure expected to be 4 bytes large but got 8 bytes instead.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions