Input C/C++ Header
struct phase8_align16_record {
unsigned long long left;
unsigned long long right;
} __attribute__((aligned(16)));
struct phase8_packet {
unsigned char prefix;
struct phase8_align16_record values[];
};
Bindgen Invocation
$ bindgen input.h \
--output bindings.rs \
--allowlist-type '^phase8_.*' \
--no-layout-tests \
--no-doc-comments \
--formatter none \
--rust-target 1.75 \
-- -x c -std=gnu11
Actual Results
Current main generates this layout, shortened to the relevant fields:
#[repr(C, align(8))]
pub struct __BindgenOpaqueArray8<T>(pub T);
#[repr(C)]
#[repr(align(16))]
pub struct phase8_packet {
pub prefix: u8,
pub __bindgen_padding_0: __BindgenOpaqueArray8<[u8; 15usize]>,
pub values: __IncompleteArrayField<phase8_align16_record>,
}
The C ABI puts values at offset 16. The generated Rust type puts it at
offset 32:
C sizeof(packet without FAM): 16
C offsetof(values): 16
Rust size_of::<packet>(): 32
Rust offset of values: 32
The padding calculation asks for 15 bytes with alignment 8. The generated
wrapper is an align-8 Rust type containing [u8; 15], so its actual size is
rounded up to 16. It also starts at offset 8 after Rust inserts alignment
before the wrapper. The next align-16 field therefore starts at 32.
This affects the generated helpers. With a C allocation and one array element:
C writes, Rust as_slice reads:
C offset 16, Rust access 32, digest mismatch
Rust as_mut_slice writes, C reads:
C offset 16, Rust access 32, digest mismatch
back canary overwritten
Expected Results
The generated representation must keep values at offset 16. A plain
[u8; 15] padding field works for this case. Splitting implicit and explicit
padding would also be fine, as long as the actual Rust field placement agrees
with the C ABI.
Environment
bindgen current main: 9d26c6eddeff9192ddedb563192abe3128fc5aae
bindgen release: 0.72.1
clang: 15.0.7
rustc: 1.75.0
target: x86_64-unknown-linux-gnu
This is a current-main regression. Bindgen 0.72.1 puts values at offset 16,
and both runtime directions match the C reference. I tested lengths 1, 2, and
7 at O0 and O2. Current main failed every helper-based comparison; 0.72.1 and
the raw-offset controls passed.
Additional notes
This appears to have started with #3280, which fixed #3279 by replacing
primitive opaque padding fields with explicitly aligned wrappers. #3279 was
about insufficient alignment on x86. This case is a different failure: a
padding byte count that is not a multiple of the wrapper alignment increases
the wrapper's physical size and shifts a following flexible-array member.
Input C/C++ Header
Bindgen Invocation
Actual Results
Current main generates this layout, shortened to the relevant fields:
The C ABI puts
valuesat offset 16. The generated Rust type puts it atoffset 32:
The padding calculation asks for 15 bytes with alignment 8. The generated
wrapper is an align-8 Rust type containing
[u8; 15], so its actual size isrounded up to 16. It also starts at offset 8 after Rust inserts alignment
before the wrapper. The next align-16 field therefore starts at 32.
This affects the generated helpers. With a C allocation and one array element:
Expected Results
The generated representation must keep
valuesat offset 16. A plain[u8; 15]padding field works for this case. Splitting implicit and explicitpadding would also be fine, as long as the actual Rust field placement agrees
with the C ABI.
Environment
This is a current-main regression. Bindgen 0.72.1 puts
valuesat offset 16,and both runtime directions match the C reference. I tested lengths 1, 2, and
7 at O0 and O2. Current main failed every helper-based comparison; 0.72.1 and
the raw-offset controls passed.
Additional notes
This appears to have started with #3280, which fixed #3279 by replacing
primitive opaque padding fields with explicitly aligned wrappers. #3279 was
about insufficient alignment on x86. This case is a different failure: a
padding byte count that is not a multiple of the wrapper alignment increases
the wrapper's physical size and shifts a following flexible-array member.