D issues are now tracked on GitHub. This Bugzilla instance remains as a read-only archive.
Issue 15610 - extern(C++) multiple inheritance - calling with wrong 'this' ptr
Summary: extern(C++) multiple inheritance - calling with wrong 'this' ptr
Status: RESOLVED FIXED
Alias: None
Product: D
Classification: Unclassified
Component: dmd (show other issues)
Version: D2
Hardware: x86_64 Windows
: P1 blocker
Assignee: No Owner
URL:
Keywords: C++
Depends on:
Blocks:
 
Reported: 2016-01-26 04:53 UTC by Manu
Modified: 2016-03-19 20:21 UTC (History)
1 user (show)

See Also:


Attachments
test case (4.75 KB, application/x-zip-compressed)
2016-01-28 12:15 UTC, Manu
Details

Note You need to log in before you can comment on or make changes to this issue.
Description Manu 2016-01-26 04:53:26 UTC
I'm seeing a case where 'this' being passed to a C++ function is wrong.
C++ multiple-inheritance calls expect that 'this' pointers are adjusted by the offset of the base class.

Something like this:

extern(C++) class Base { ~this() {} size_t i; } // Base is 16 bytes
extern(C++) interface I { void f(); }
extern(C++) class C : Base, I
{
  final override void f();
}


C is:
{
  _vtbl,
  i,
  _I_vtbl (this+16h)
}


void code(C c)
{
  // crash:
  c.f(); passes this = 'c' to f(), but C++ expects 'c + C._I_vtbl.offsetof'
}


In this example, I have a class instance 'c', and I'm calling 'f' (introduced by the interface), which is marked final, so D is able to perform a static call to the C++ function with no vtable lookup.

I have managed to get this to link, and D does call the correct C++ function, but D doesn't seem to adjust the 'this' pointer correctly for the call.

The C++ function 'f' expects that 'this' is an I pointer (not a C pointer), which is offset by sizeof(Base) from the start of C, but D just passes 'this' unaltered. The result is that within C::f(), the C++ code expects that 'C' is at 'this-16h', and everything is 16 bytes out of phase leading to a prompt crash.


TL;DR: Calling of multiple-inherited C++ interfaces requires adjustment of the 'this' pointer before making the call.
Comment 1 Walter Bright 2016-01-27 23:53:34 UTC
This was addressed by:

 https://github.com/D-Programming-Language/dmd/pull/5364

Since the code snippet you post here looks like the test case in 5364, I don't see what the different is.

> Something like this:

I cannot do anything with this, since it already looks like the test case in 5364. Clearly, something DIFFERENT is happening if it fails for you, meaning it is DIFFERENT code in some way that you have not posted.

PLEASE submit test cases with reproducible code snippets. "Something like this" is not going to work. So many times I have tried to fill in what the missing pieces might be, only to fail and eventually discover that the submitter had omitted critical information, not realizing that it was critical.
Comment 2 Walter Bright 2016-01-28 00:05:28 UTC
I did some guesswork, and figured out that if you remove the 'final', it will work successfully, so I suggest that as a workaround for the moment.
Comment 3 Manu 2016-01-28 00:19:59 UTC
Okay, I'll give that a shot.
Incidentally, 99% of my functions are final, and about 80% of my virtuals are also final.
It'd be good to make finalised virtuals work properly a priority.
Final also has a mangling problem.
Comment 5 Manu 2016-01-28 12:15:36 UTC
Created attachment 1580 [details]
test case
Comment 6 Manu 2016-01-28 12:17:37 UTC
Reduced a test case.
You'll see a crash as it tries to call the non vtable member 'BAADFOOD'.

Bonus points:
void test() is extern(C) because extern(C++) doesn't mangle the type properly; doesn't link.
Comment 7 Manu 2016-01-28 12:20:42 UTC
Also, I don't think this particular crash looks the same as the situation of my other crash; neither rax or rcx are messed up in quite the same way, but they are both messed up, so it might be related.
The other case is that the pointers are dereferenced one level too deep before the call. This case appears that that the wrong offset is used as the vtable pointer.
Comment 8 github-bugzilla 2016-01-31 01:34:57 UTC
Commits pushed to master at https://github.com/D-Programming-Language/dmd

https://github.com/D-Programming-Language/dmd/commit/f46f2197f458b4f242484b486302a383e70ceac4
fix Issue 15610 - extern(C++) multiple inheritance - calling with wrong 'this' ptr

https://github.com/D-Programming-Language/dmd/commit/84f6aaf592c6b4526a24f43d1bbdca77b4943b74
Merge pull request #5372 from WalterBright/fix15610

fix Issue 15610 - extern(C++) multiple inheritance - calling with wro…
Comment 9 github-bugzilla 2016-03-19 20:21:46 UTC
Commits pushed to stable at https://github.com/D-Programming-Language/dmd

https://github.com/D-Programming-Language/dmd/commit/f46f2197f458b4f242484b486302a383e70ceac4
fix Issue 15610 - extern(C++) multiple inheritance - calling with wrong 'this' ptr

https://github.com/D-Programming-Language/dmd/commit/84f6aaf592c6b4526a24f43d1bbdca77b4943b74
Merge pull request #5372 from WalterBright/fix15610