Incorrect construction vtable on ARM in case of diamond shaped virtual inheritance

andrew wiggin end3er@gmail.com
Fri Nov 5 13:12:00 GMT 2010


Hi all,

I have recently met a segfault in a program I had to port from x86 to
ARMv7. After some investigation I managed to narrow it down to a few
C++ lines which when compiled with -O > 0 will always produce an
incorrect assembly on ARM.

I am using GCC 4.2.1 for ARM.

The register R1 holding the pointer to the construction vtable for the
Parent-in-Child is actually pointing to the wrong Parent-in-Child
vtable:
Let's assume we have a virtual base class A, two derived class B and C
virtually inheriting from A and a final class D derived from B and C.
When instantiating D, the CTOR for B is called with R1 pointing to the
construction vtable for C-in-D instead of the one for B-in-D, which
thus make the program crash since it tries to access an uninitialized
element of the vtable for C when trying to access elements in B. (I
hope it is clear enough).

When compiling with the -fno-toplevel-reorder, the issue disappear
since the CTORs are called in the correct order and with their
corresponding construction vtable. Compiling with -fno-unit-at-a-time
also fixes the problem, but it is deprecated according to GCC
documentation. Disabling inlining also works obviously.
It seems to be related to bug:
http://gcc.gnu.org/bugzilla/show_bug.cgi?id=41354 however the
-fno-tree-sink doesn't resolve the issue, so I am assuming that it is
not a duplicate.

Since the code seems legit from an ISO C++ point of view, I assume
that GCC is doing something wrong when inlining some CTORs. Can you
guys confirm me that ?

Here is a sample code:
class A { public: ~A() {} virtual int e() = 0; };
class B : virtual public A { public: ~B() {} virtual int f() = 0; };
class C : virtual public A { public: ~C(); C(int a); protected: int
_c; virtual int e(); };
class D : public C, virtual public B { public: ~D(); D(int a); virtual
int f(); };
C::C(int a) { _c = a; }
C::~C() {}
int C::e() { return _c; }
D::D(int a):C(a) { }
D::~D() {}
int D::f() { return _c+1; }
int main() { D* d = new D(1); }

Here is GDB analysis:
Program received signal SIGSEGV, Segmentation fault.
0x00008530 in C::C ()
(gdb) disas
Dump of assembler code for function C::C(int):
0x00008524 <C::C(int)+0>:       push    {lr}            ; (str lr, [sp, #-4]!)
0x00008528 <C::C(int)+4>:       ldr     r3, [r1]
0x0000852c <C::C(int)+8>:       str     r3, [r0]
0x00008530 <C::C(int)+12>:      ldr     lr, [r3, #-16]
0x00008534 <C::C(int)+16>:      ldr     r3, [r1, #4]
0x00008538 <C::C(int)+20>:      str     r3, [r0, lr]
0x0000853c <C::C(int)+24>:      str     r2, [r0, #4]
0x00008540 <C::C(int)+28>:      pop     {pc}            ; (ldr pc, [sp], #4)
End of assembler dump.
(gdb) disas $r1
Dump of assembler code for function construction vtable for B-in-D:

Regards,
-- 
ender



More information about the Gcc-help mailing list