Skip to content

Incorrect field offset when using #[repr(C, packed(8))] #128373

@kurtmcmillan

Description

@kurtmcmillan

I'm using bindgen to create bindings for a library. It appears that rustc is not packing the structs in the same way as clang/gcc.

struct foo
{
    uint64_t a;
    uint32_t b;
    uint32_t c;
    uint32_t d;
    uint64_t e;
} __attribute__((packed, aligned(8)));

bindgen generates the following:

#[repr(C, packed(8))]
#[derive(Debug, Copy, Clone)]
pub struct foo {
    pub a: u64,
    pub b: u32,
    pub c: u32,
    pub d: u32,
    pub e: u64,
}

I expected to see this happen:

The bindgen tests pass. The e field is at offset 20.

Instead, this happened:

The bindgen tests fail. The e field is at offset 24.

fn bindgen_test_layout_foo() {
    const UNINIT: ::std::mem::MaybeUninit<foo> = ::std::mem::MaybeUninit::uninit();
    let ptr = UNINIT.as_ptr();
    // ... snip ...
    assert_eq!(
        unsafe { ::std::ptr::addr_of!((*ptr).e) as usize - ptr as usize },
        20usize,
        concat!("Offset of field: ", stringify!(foo), "::", stringify!(e))
    );
}
cargo test
... snip ...
---- bindings::bindgen_test_layout_foo stdout ----
thread 'bindings::bindgen_test_layout_foo' panicked at /home/kumcmill/bindgen/target/debug/build/bindgen-bbd2a2691349bec7/out/bindings.rs:246:5:
assertion `left == right` failed: Offset of field: foo::e
  left: 24
 right: 20

Reproduced with a smaller example:

struct bar
{
    uint32_t f;
    uint64_t g;
} __attribute__((packed, aligned(8)));

bindgen generates the following:

#[repr(C, packed(8))]
#[derive(Debug, Copy, Clone)]
pub struct bar {
    pub f: u32,
    pub g: u64,
}

I expected to see this happen:

The bindgen tests pass. The g field is at offset 4.

Instead, this happened:

The bindgen tests fail. The g field is at offset 8.

#[test]
fn bindgen_test_layout_bar() {
    const UNINIT: ::std::mem::MaybeUninit<bar> = ::std::mem::MaybeUninit::uninit();
    let ptr = UNINIT.as_ptr();
    // ... snip ...
    assert_eq!(
        unsafe { ::std::ptr::addr_of!((*ptr).e) as usize - ptr as usize },
        4usize,
        concat!("Offset of field: ", stringify!(bar), "::", stringify!(e))
    );
}
cargo test
... snip ...
---- bindings::bindgen_test_layout_bar stdout ----
thread 'bindings::bindgen_test_layout_bar' panicked at /home/kumcmill/bindgen/target/debug/build/bindgen-bbd2a2691349bec7/out/bindings.rs:277:5:
assertion `left == right` failed: Offset of field: bar::e
  left: 8
 right: 4

With gcc and clang:

#include <stddef.h>
#include <stdint.h>
#include <stdio.h>

struct foo
{
    uint64_t a;
    uint32_t b;
    uint32_t c;
    uint32_t d;
    uint64_t e;
} __attribute__((packed, aligned(8)));

struct bar
{
    uint32_t f;
    uint64_t g;
} __attribute__((packed, aligned(8)));

int main() {
    printf("offset of foo.e: %lu\n", offsetof(struct foo, e));
    printf("offset of bar.g: %lu\n", offsetof(struct bar, g));
}

clang:

$ clang --version
clang version 10.0.0-4ubuntu1
Target: x86_64-pc-linux-gnu
Thread model: posix
InstalledDir: /usr/bin

$ clang main.c -o main && ./main
offset of foo.e: 20
offset of bar.g: 4

gcc:

$ gcc --version
gcc (Ubuntu 9.4.0-1ubuntu1~20.04.2) 9.4.0
Copyright (C) 2019 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

$ gcc main.c -o main && ./main
offset of foo.e: 20
offset of bar.g: 4

Meta

rustc --version --verbose:

$ rustc --version --verbose
rustc 1.79.0 (129f3b996 2024-06-10)
binary: rustc
commit-hash: 129f3b9964af4d4a709d1383930ade12dfe7c081
commit-date: 2024-06-10
host: x86_64-unknown-linux-gnu
release: 1.79.0

Metadata

Metadata

Assignees

No one assigned

    Labels

    C-feature-requestCategory: A feature request, i.e: not implemented / a PR.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions