rust-lang / rust-lang/rust-bindgen

Wrong memory layout when handle virtual inheritance

Open
#465 4 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug
Dominant language
Rust
Stars
5.3k
Forks
829
Avg merge
1d 1h
Merged PRs (30d)
15

Description

Consider the test case in virtual_inheritance.hpp, C++ will generate memory layout as

&d = 0x7fff566a88b0
offset_of(A::foo)=32
offset_of(B::foo=32
offset_of(C::foo=32
offset_of(B::bar)=24
offset_of(C::baz)=8
offset_of(D::bazz)=28
a = 0x7fff566a88d0
offset_of(A::foo)=0
b = 0x7fff566a88c0
offset_of(A::foo)=16
offset_of(C::baz)=8
c = 0x7fff566a88b0
offset_of(A::foo)=32
offset_of(C::baz)=8
#include <stdio.h>
#include <stdint.h>
#include <stddef.h>

class A {
public:
  int foo;
};

class B: public virtual A {
public:
  int bar;
};

class C: public virtual A {
public:
  int baz;
};

class D: public C, public B {
public:
  int bazz;
};

int main() {
    D d;

int main() {
    D d;

    printf("&d = %p\n", &d);

    printf("offset_of(A::foo)=%lu\n", (uintptr_t)&d.foo - (uintptr_t)&d);
    printf("offset_of(B::foo=%lu\n", (uintptr_t)&((B *)&d)->foo - (uintptr_t)&d);
    printf("offset_of(C::foo=%lu\n", (uintptr_t)&((C *)&d)->foo - (uintptr_t)&d);
    printf("offset_of(B::bar)=%lu\n", (uintptr_t)&d.bar - (uintptr_t)&d);
    printf("offset_of(C::baz)=%lu\n", (uintptr_t)&d.baz - (uintptr_t)&d);
    printf("offset_of(D::bazz)=%lu\n", (uintptr_t)&d.bazz - (uintptr_t)&d);

    A *a = &d;

    printf("a = %p\n", a);

    printf("offset_of(A::foo)=%lu\n", (uintptr_t)&a->foo - (uintptr_t)a);

    B *b = &d;

    printf("b = %p\n", b);

    printf("offset_of(A::foo)=%lu\n", (uintptr_t)&b->foo - (uintptr_t)b);
    printf("offset_of(C::baz)=%lu\n", (uintptr_t)&b->bar - (uintptr_t)b);

    C *c = &d;

    printf("c = %p\n", c);

    printf("offset_of(A::foo)=%lu\n", (uintptr_t)&c->foo - (uintptr_t)c);
    printf("offset_of(C::baz)=%lu\n", (uintptr_t)&c->baz - (uintptr_t)c);
}

bindgen will generate memory layout as

#[repr(C)]
#[derive(Debug, Copy)]
pub struct B {
    pub vtable_: *const B__bindgen_vtable,
    pub bar: ::std::os::raw::c_int,
}

#[repr(C)]
#[derive(Debug, Copy)]
pub struct C {
    pub vtable_: *const C__bindgen_vtable,
    pub baz: ::std::os::raw::c_int,
}

#[repr(C)]
#[derive(Debug, Copy)]
pub struct D {
    pub _base: C,
    pub _base_1: B,
    pub bazz: ::std::os::raw::c_int,
}

First, both C and B will be aligned to 16 bytes, it cause D::bazz be aligned to 32 bytes rather than 28 bytes.
Second, A::foo should be generated and following D::bazz.

#[repr(C)]
#[derive(Debug, Copy)]
pub struct D {
    pub vtable_c: *const C__bindgen_vtable,
    pub baz: ::std::os::raw::c_int,
    pub vtable_b: *const B__bindgen_vtable,
    pub bar: ::std::os::raw::c_int,
    pub bazz: ::std::os::raw::c_int,
    pub foo: ::std::os::raw::c_int,
}

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the test case in virtual_inheritance.hpp and compare bindgen’s generated B, C, and D layouts with the C++ offsets shown in the issue. Trace how virtual inheritance, alignment, base fields, and A::foo placement are represented; done means the generated bindings match the demonstrated memory layout.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp, rust
Domain
devtools
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.