rust-lang / rust-lang/rust-bindgen
Missing virtual destructors in C++ vtables on Linux x86_64
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 5.3k
- Forks
- 829
- Avg merge
- 1d 1h
- Merged PRs (30d)
- 15
Description
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.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Clone the linked reproduction and run cargo run; compare src/main.cpp and src/main.rs, then inspect the generated vtable with the shown gdb commands. Trace bindgen's C++ vtable generation from the reproduction and verify behavior on Linux x86_64. Done means virtual destructors are represented safely or the generated binding clearly reports that this case is unsupported without misdispatching fn3().
Written by the indexing model from the issue text.
Assessment
- Tech stack
- cpp, rust
- Domain
- compilers, tooling
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100