Skip to content

Missing virtual destructors in C++ vtables on Linux x86_64 #3193

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
DevilishSpirits opened this issue Apr 13, 2025 · 0 comments
Open

Missing virtual destructors in C++ vtables on Linux x86_64 #3193

DevilishSpirits opened this issue Apr 13, 2025 · 0 comments

Comments

@DevilishSpirits
Copy link

Bug

I am using a large C++ library which makes heavy use of virtual functions and virtual destructor. I am using bindgen's vtable generation which miss virtual destructors on Linux x86_64.

I made a simple repository that exhibit this bug : https://github.com/DevilishSpirits/rust-bindgen-vtable-april25, clone and cargo run it. fn3() should be printed if bindgen correctly generated the vtable.

The src/main.cpp file define a class with pure virtual functions fn1(), fn2() and fn3(), then this class is derived with implementations that echo fnx() on screen. The level of indirection in the C++ is because I reproduced the style of the library I am using.

Both src/main.cpp and src/main.rs contain an example that create the object then call fn3() and destroy the object. While the C++ code echo fn3(), the Rust code echo fn1(). I inspected the vtables using gdb which show that there are 2 virtual destructor prepended in the vtable :

(gdb) info vtbl this
vtable for 'BugIllustrationImpl' @ 0x5555555a8eb0 (subobject @ 0x5555555bfa20):
[0]: 0x55555555b890 <BugIllustrationImpl::~BugIllustrationImpl()>
[1]: 0x55555555b8be <BugIllustrationImpl::~BugIllustrationImpl()>
[2]: 0x55555555b8ea <BugIllustrationImpl::fn1()>
[3]: 0x55555555b908 <BugIllustrationImpl::fn2()>

Rust does not include these virtual destructors and is off by 2 pointers and hence call fn1() instead of fn3() :

(gdb) p *bug_vtable
$1 = aa::BugIllustration__bindgen_vtable {
	BugIllustration_fn1: 0x55555555b890 <BugIllustrationImpl::~BugIllustrationImpl()>,
	BugIllustration_fn2: 0x55555555b8be <BugIllustrationImpl::~BugIllustrationImpl()>,
	BugIllustration_fn3: 0x55555555b8ea <BugIllustrationImpl::fn1()>
}

Expected behavior

I expect bindgen to include pointers to the virtual destructor in the vtable or an opaque padding to allow read-only usage of the vtable.

Expected behavior 2

If that is too difficult to implement, at least bindgen should detect that virtual destructor are in use and generate a fake vtable such as the one below :

/// Virtual destructors detected: vtable not generated
#[repr(C, align(8))] // ← shouldn't bindgen explicitely align the struct???
pub struct BugIllustration__bindgen_vtable {
	/// Virtual destructors detected: vtable not generated
	///
	/// Bindgen currently does not support vtable generation of C++ class with 
	/// virtual destructors. See https://example.com/some/bug/or/link
	pub __virtual_destructors_detected: std::convert::Infallible,
}

This clearly tell the user that bindgen attempted to generate the vtable but that it ran into a limitation.
The user can also use the vtable reference and pass it around for it own needs.
Then if bindgen get support for these vtable, existing code is unlikely to break.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant