Bug 15579 – extern(C++) interfaces/multiple-inheritance

Status
RESOLVED
Resolution
FIXED
Severity
normal
Priority
P1
Component
dmd
Product
D
Version
D2
Platform
x86_64
OS
Windows
Creation time
2016-01-18T15:52:07Z
Last change time
2023-01-29T21:43:15Z
Keywords
C++
Assigned to
No Owner
Creator
Manu
See also
https://issues.dlang.org/show_bug.cgi?id=23660

Comments

Comment #0 by turkeyman — 2016-01-18T15:52:07Z
I'm noticing some differences in struct layout between C++ and D. I'm doing tests by making some classes, putting members in between the locations I expect vtable pointers to be placed, and then comparing the offsets of the members. This is what I'm seeing: In C++: class Base { virtual ~Base() {} size_t x; }; class Interface { virtual Method() = 0; }; class Derived : public Base, public Interface { size_t y; }; In C++, Derived is: { void *__Base_vtable; size_t x; void *__Interface_vtable; size_t y; } This is as I expect. ------------------- D: extern(C++) class Base { ~this() {} size_t x; } extern(C++) interface Interface { abstract Method(); } extern(C++) class Derived : Base, Interface { size_t y; } Derived appears to be: { void *__Base_vtable; size_t x; size_t y; } So, D seems to think 'y' is where Interface_vtable should be... I'm not sure where D thinks the Interface_vtable pointer actually is. What's interesting though, is that I can call 'Method' introduced by Interface, D does try and call something... which means it must get a vtable pointer somewhere, but it crashes on a bad function pointer, so something goes wrong. Digging into the bad virtual call, I get this: (Note, my test case is not exactly what's above; in this test, Interface has a few functions and I'm calling the 4th one in the vtable) 00007FF72DB7381B mov rbx,qword ptr [this] // rbx is 'this' 00007FF72DB7381F mov rcx,qword ptr [rbx+10h] // rcx is [this+10h], which is actually the proper location for Interface_vtable, contrary to what I found above Surprisingly, this looks good. rcx is Interface_vtable, and it found it at the location I expect, which means my test above was lying to me about the location of 'y'. But here it gets a little weird... 00007FF72DB73823 mov rcx,qword ptr [rcx] // what's this? ie, rcx = rcx[0] rcx is now the first function pointer in the vtable; NOT the function I'm calling, which is actually [rcx+18h]! 00007FF72DB73826 sub rsp,20h // nothing of interest 00007FF72DB7382A mov rax,qword ptr [rcx] // dereferencing the function pointer! rax is now the first 8 bytes of program code of the first function in the vtable 00007FF72DB7382D call qword ptr [rax+18h] // calling rax+18h If rax were rcx from the second operation above, 18h is the proper vtable offset of the function I'm trying to call, and this would be correct, but there are 2 bonus dereferences there that shouldn't be. I don't know how interfaces are implemented in D, is this showing some relationship to normal D interface calls? This is certainly not proper extern(C++) behaviour. The offset of y seems confused, and the extra 2 dereferences shouldn't be there. Tests are with DMD-Win64 compared against MSC2015-Win64.
Comment #1 by deadalnix — 2016-01-19T19:14:15Z
Comment #2 by bugzilla — 2016-01-21T19:46:09Z
Compilable D version of the code: extern (C++) { class Base { ~this() {} size_t x = 4; } interface Interface { int Method(); } class Derived : Base, Interface { size_t y = 5; int Method() { return 3; } } } The layout for Win64 Base is: 4Base6__initZ: void* &vtbl size_t x; The layout for Win64 Derived is: 7Derived6__initZ: void* &vtbl size_t x; size_t y; void* &vtbl These can be figured out by running obj2asm on the object file and looking at it.
Comment #3 by bugzilla — 2016-01-22T07:53:46Z
Comment #4 by bugzilla — 2016-01-22T07:55:44Z
Comment #5 by github-bugzilla — 2016-01-22T23:21:05Z
Commits pushed to master at https://github.com/D-Programming-Language/dmd https://github.com/D-Programming-Language/dmd/commit/316f2e900dc16ce1351a305c58bad287b2855ad5 fix Issue 15579 - extern(C++) interfaces/multiple-inheritance https://github.com/D-Programming-Language/dmd/commit/c6ac10aed0f7dc76112e576204e3a1e8ea307dbd Merge pull request #5361 from WalterBright/fix15579 fix Issue 15579 - extern(C++) interfaces/multiple-inheritance
Comment #6 by bugzilla — 2016-01-22T23:36:26Z
Reopened because the fix for Win64 is incomplete.
Comment #7 by turkeyman — 2016-01-23T04:47:03Z
I was just doing some work with the latest nightly (should have this patch in it). I got a compile error that's new since the last nightly I was using: 3>Building bin\Debug_x64\dplug.dll... 3>libdep.lib(component_344_d5.obj) : fatal error LNK1179: invalid or corrupt file: duplicate COMDAT '??1Component@ep@@QEAA@XZ' The project structure is: cpplib.lib --+ dlib.lib ----| | dplug.dll So, cpplib has the stuff I'm extern-ing to, dlib is a binding to cpplib with some adaptation support code, and dplug is a dll which links both libs. dlib is mostly extern(C++)'s, and a bunch of extra (mostly extern(D)) code for adaptation. I presume that symbol exists in cpplib, but the error suggests that D is also emitting the same symbol somewhere? Possibly uninteresting, but I have a lot of code like this: extern(C++) class CppClass { void cppMethod(ref const(CustomString) &s); void cppMethod2(); extern(D): void dAdapterMethod(const(char)[] s) { auto temp = CustomString(s.ptr, s.length); cppMethod(temp); } } This is a simple example, but there's a lot of this going on... so the extern(C++) classes are kitted out with a bunch of extra functions on the D side. I only say this, because I wonder if the fact that I'm not strictly binding, but also extending may lead to symbols being emitted...
Comment #8 by turkeyman — 2016-01-23T04:50:21Z
/s/compile error/link error/
Comment #9 by bugzilla — 2016-01-24T09:20:20Z
Comment #10 by bugzilla — 2016-01-24T10:39:58Z
(In reply to Manu from comment #7) > 3>Building bin\Debug_x64\dplug.dll... > 3>libdep.lib(component_344_d5.obj) : fatal error LNK1179: invalid or corrupt > file: duplicate COMDAT '??1Component@ep@@QEAA@XZ' > [...] > I presume that symbol exists in cpplib, but the error suggests that D is > also emitting the same symbol somewhere? Note the filename in the error message: libdep.lib(component_344_d5.obj) You can also use 'grep' to find all instances and references to '??1Component@ep@@QEAA@XZ'
Comment #11 by github-bugzilla — 2016-01-26T13:51:49Z
Commits pushed to master at https://github.com/D-Programming-Language/dmd https://github.com/D-Programming-Language/dmd/commit/073ac6b496971015fc8cce08abea394093d75982 2nd try fix Issue 15579 - extern(C++) interfaces/multiple-inheritance https://github.com/D-Programming-Language/dmd/commit/b4ac93569071d17ea08fdce017f20c02dc6b727c Merge pull request #5364 from WalterBright/fix15579-2 2nd try fix Issue 15579 - extern(C++) interfaces/multiple-inheritance
Comment #12 by github-bugzilla — 2016-03-19T20:21:44Z
Comment #13 by github-bugzilla — 2017-08-02T08:07:31Z
Commit pushed to dmd-cxx at https://github.com/dlang/dmd https://github.com/dlang/dmd/commit/1a34a0f00eaf5fb8adf4e55d599d83a3d2657649 Issue 15579 - extern(C++) interfaces/multiple-inheritance