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.
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.
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...
(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