Skip to content

Wrong memory layout when handle virtual inheritance #465

Open
@flier

Description

@flier

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,
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions