0

I have a crash in application:

__cxxabiv1::__cxa_pure_virtual ()

I can understand
What is the meaning of a "pure virtual" call in a stack trace?

And according "1 Answer" below i can exercise a little test program:

  1 #include <iostream>
  2 
  3 class Base
  4 {
  5 public:
  6     Base()
  7     {
  8         std::cout << "Base c'tor" << std::endl;
  9     }
 10 
 11     virtual ~Base()
 12     {
 13         std::cout << "Base d'tor" << std::endl;
 14     }
 15 
 16 };
 17 
 18 class Derived : public Base
 19 {
 20 public:
 21     Derived()
 22     : Base()
 23     {
 24         std::cout << "Derived c'tor" << std::endl;
 25     }
 26 
 27     ~Derived()
 28     {
 29         std::cout << "Derived d'tor" << std::endl;
 30     }
 31 };
 32 
 33 int
 34 main(
 35     int,
 36     char**)
 37 {
 38     {
 39         Derived d;
 40     }
 41     return 0;
 42 }

compile with:

g++ -g3 -O0 -o test test.cc

create gdb batch script:

break 8
command
p this
x/10xg this
x/10xg (long)*this
cont
end
break 13
command
p this
x/10xg this
x/10xg (long)*this
cont
end
break 24
command
p this
x/10xg this
x/10xg (long)*this
cont
end
break 29
command
p this
x/10xg this
x/10xg (long)*this
cont
end
run

and run:

frank@frank-PC:~$ gdb ./test < gdb.bat |c++filt 

GNU gdb (Ubuntu 7.11.1-0ubuntu1~16.5) 7.11.1
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./test...done.
(gdb) Breakpoint 1 at 0x400b72: file test.cc, line 8.
(gdb) >>>>>(gdb) Breakpoint 2 at 0x400baa: file test.cc, line 13.
(gdb) >>>>>(gdb) Breakpoint 3 at 0x400c29: file test.cc, line 24.
(gdb) >>>>>(gdb) Breakpoint 4 at 0x400c81: file test.cc, line 29.
(gdb) >>>>>(gdb) Starting program: /home/frank/test 

Breakpoint 1, Base::Base (this=0x7fffffffdab0) at test.cc:8
8           std::cout << "Base c'tor" << std::endl;
$1 = (Base * const) 0x7fffffffdab0
0x7fffffffdab0: 0x0000000000400df8  0x597870ad9bc42900
0x7fffffffdac0: 0x0000000000400d10  0x00007ffff7495830
0x7fffffffdad0: 0x0000000000000000  0x00007fffffffdba8
0x7fffffffdae0: 0x00000001ffffdbb8  0x0000000000400ab6
0x7fffffffdaf0: 0x0000000000000000  0x9e98039144430dd4
0x400df8 <vtable for Base+16>:  0x0000000000400b92  0x0000000000400bde
0x400e08 <typeinfo for Derived>:    0x0000000000602200  0x0000000000400e20
0x400e18 <typeinfo for Derived+16>: 0x0000000000400e30  0x6465766972654437
0x400e28 <typeinfo name for Derived+8>: 0x0000000000000000  0x0000000000602090
0x400e38 <typeinfo for Base+8>: 0x0000000000400e40  0x0000006573614234
Base c'tor

Breakpoint 3, Derived::Derived (this=0x7fffffffdab0) at test.cc:24
24          std::cout << "Derived c'tor" << std::endl;
$2 = (Derived * const) 0x7fffffffdab0
0x7fffffffdab0: 0x0000000000400dd8  0x597870ad9bc42900
0x7fffffffdac0: 0x0000000000400d10  0x00007ffff7495830
0x7fffffffdad0: 0x0000000000000000  0x00007fffffffdba8
0x7fffffffdae0: 0x00000001ffffdbb8  0x0000000000400ab6
0x7fffffffdaf0: 0x0000000000000000  0x9e98039144430dd4
0x400dd8 <vtable for Derived+16>:   0x0000000000400c68  0x0000000000400ce2
0x400de8 <vtable for Base>: 0x0000000000000000  0x0000000000400e30
0x400df8 <vtable for Base+16>:  0x0000000000400b92  0x0000000000400bde
0x400e08 <typeinfo for Derived>:    0x0000000000602200  0x0000000000400e20
0x400e18 <typeinfo for Derived+16>: 0x0000000000400e30  0x6465766972654437
Derived c'tor

Breakpoint 4, Derived::~Derived (this=0x7fffffffdab0, __in_chrg=<optimized out>) at test.cc:29
29          std::cout << "Derived d'tor" << std::endl;
$3 = (Derived * const) 0x7fffffffdab0
0x7fffffffdab0: 0x0000000000400dd8  0x597870ad9bc42900
0x7fffffffdac0: 0x0000000000400d10  0x00007ffff7495830
0x7fffffffdad0: 0x0000000000000000  0x00007fffffffdba8
0x7fffffffdae0: 0x00000001ffffdbb8  0x0000000000400ab6
0x7fffffffdaf0: 0x0000000000000000  0x9e98039144430dd4
0x400dd8 <vtable for Derived+16>:   0x0000000000400c68  0x0000000000400ce2
0x400de8 <vtable for Base>: 0x0000000000000000  0x0000000000400e30
0x400df8 <vtable for Base+16>:  0x0000000000400b92  0x0000000000400bde
0x400e08 <typeinfo for Derived>:    0x0000000000602200  0x0000000000400e20
0x400e18 <typeinfo for Derived+16>: 0x0000000000400e30  0x6465766972654437
Derived d'tor

Breakpoint 2, Base::~Base (this=0x7fffffffdab0, __in_chrg=<optimized out>) at test.cc:13
13          std::cout << "Base d'tor" << std::endl;
$4 = (Base * const) 0x7fffffffdab0
0x7fffffffdab0: 0x0000000000400df8  0x597870ad9bc42900
0x7fffffffdac0: 0x0000000000400d10  0x00007ffff7495830
0x7fffffffdad0: 0x0000000000000000  0x00007fffffffdba8
0x7fffffffdae0: 0x00000001ffffdbb8  0x0000000000400ab6
0x7fffffffdaf0: 0x0000000000000000  0x9e98039144430dd4
0x400df8 <vtable for Base+16>:  0x0000000000400b92  0x0000000000400bde
0x400e08 <typeinfo for Derived>:    0x0000000000602200  0x0000000000400e20
0x400e18 <typeinfo for Derived+16>: 0x0000000000400e30  0x6465766972654437
0x400e28 <typeinfo name for Derived+8>: 0x0000000000000000  0x0000000000602090
0x400e38 <typeinfo for Base+8>: 0x0000000000400e40  0x0000006573614234
Base d'tor
[Inferior 1 (process 4962) exited normally]
(gdb) (gdb) quit

But the stacktrace for my real application (not the little demo above) hits:

__cxxabiv1::__cxa_pure_virtual ()

while the last d'tor i see on the call stack is the derived class.
So is this an indicator, that memory object was already free'd and re-used again for some other object instance?

Frank Bergemann
  • 325
  • 2
  • 14
  • Do you have a small, reproducible test case that exhibits the problem? It's very hard to offer much help otherwise. This is just a stub to catch a fatal error that should never happen, and it's very useful. Perhaps you tried to call a sliced pointer? Check casting, etc – gavinb Mar 08 '18 at 11:02

1 Answers1

1

How can that be? Is such done by the compiler for intermediate work?

Yes. Typically, a constructor first sets the vtable pointer to its own class's vtable. Then, when the derived class's constructor runs, it overwrites the vtable pointer with its own.

This achieves exactly the behavior that the C++ standard requires, that calls to virtual functions during the execution of constructors (and destructors; they reverse these assignments) treat the objects as having the dynamic type of the constructor, not the actual complete object. And in the case of pure virtual functions, the behavior is undefined; compilers typically insert this diagnostic stub in the vtable.

Sebastian Redl
  • 69,373
  • 8
  • 123
  • 157
  • Does it do the inverse for destructor? – Frank Bergemann Mar 08 '18 at 13:18
  • No, actually both constructors and destructors set the vtable at their start (in typical implementations). The difference is in the order base versions are called. Constructors call base constructors first and then run their own code, destructors run their own code first and then call base destructors. – Sebastian Redl Mar 08 '18 at 13:20