0

I have code with inheritance that looks like this:

        B
       / \
      /   \
     /     \
    BI      D
(template) /
      \   /
       \ /
        DI
    (template)

[B]ase and [D]erived are interfaces that both contain a static method create(), which returns an instance of the respective implementation, BaseImpl or DerivedImpl. The implementations are templates, and the factory methods choose how to instantiate them at runtime. The code below is a working example of my program's logic.

The problem is, in my real code, Base methods get a faulty this pointer and inevitably crash. I have examined the assembly code right before the call to one of the methods, which takes no arguments, in both real and example code, and found that it differs:

Working (example) code

call   <std::unique_ptr<Base, std::default_delete<Base> >::operator->() const>
mov    (%eax),%edx
add    $0x8,%edx
mov    (%edx),%edx
mov    %eax,%ecx
call   *%edx

The above translates to a base->method() call, where base is a std::unique_ptr<Base>. For those that don't know, the calling convention of class methods can be a __thiscall, which it in this case is. That means that this isn't passed on the stack, but is stored in ecx. The code above simply stores this into ecx, and then locates method(), stores its address into edx, and calls it. Everything works as it should.

Faulty (real) code

call   <std::unique_ptr<Base, std::default_delete<Base> >::operator->() const>
mov    (%eax),%edx
add    $0x18,%edx
mov    (%edx),%ebx
lea    -0x24(%ebp),%edx
mov    %eax,(%esp)
movl   $0x9,-0x9c(%ebp)
mov    %edx,%ecx
call   *%ebx

This is the exact same base->method() call. The address of method() is stored into ebx this time. this, however, is loaded from an arbitrary place off the stack!? The program, as expected, crashes. Anyone knows why would the compiler generate assembly like that? I'm still trying to figure it out.

Code

The example code is below. The real code crashes at a point analogous to the example's call to base->check() from invoker->invoke().

Code removed to conserve space. To view it, check the edit history.

hauzer
  • 258
  • 3
  • 12
  • possible duplicate of [C++ pointer multi-inheritance fun](http://stackoverflow.com/questions/2157104/c-pointer-multi-inheritance-fun) – n. m. could be an AI Sep 29 '13 at 03:41
  • 1
    The `this` pointer doesn't have to stay the same inside different derived classes. If you need the base address of the outermost object you can use `dynamic_cast` iirc. – Alan Stokes Sep 29 '13 at 08:36
  • @AlanStokes Yes, you're absolutely right. The code I've posted doesn't reproduce the error I have in my real code. The address of `this` pointer in the real code is way out of bounds of the program's virtual memory, eg. `0x429bce8` vs `0x7a55ec`. I'll try to figure out what's happening. – hauzer Sep 29 '13 at 18:21
  • @n.m. It's not a duplicate. – hauzer Sep 30 '13 at 02:45
  • It's a completely different question now. – n. m. could be an AI Sep 30 '13 at 02:59
  • @n.m. The question is about the same problem, even though it's slightly reformulated now. I've just changed the information as I researched deeper into the problem. But it wasn't even close to being a duplicate of [C++ pointer multi-inheritance fun](http://stackoverflow.com/q/2157104/2006222) to begin with. – hauzer Sep 30 '13 at 03:33
  • I call it "totally rewritten" rather than "slightly reformulated", but whatever works. – n. m. could be an AI Sep 30 '13 at 04:27

1 Answers1

0

It's a bug. (55173, 55367, 55453) The bug is specific to gcc-i686-mingw versions 4.7.0 to 4.7.2.

From 55173:

When a virtual call invoked on an object which uses both virtual and non-virtual inheritance, the virtual thunk leaves the target function with an invalid "this" pointer.

It's basically what I've described in the question. The register that should hold this was used in non-related operations.

The only solution is to downgrade below 4.7.0 or upgrade to 4.7.3 or above.

hauzer
  • 258
  • 3
  • 12