The bug shows up as a pure virtual call, but the real error is use-after-free caused by apparent miscompilation of shared_ptr<>.
I've done deep repro on this, and what I find is that when creating a new thread, libstdc++ std:thread creates an impl object containing the virtual _M_run() function to run the thread's code. An odd detail is that this impl object contains a shared_ptr<> to itself; it's a shared_ptr<base> inside the base type, and the created impl object is always a type derived from this base. This approach, while weird, ensures that the impl object stays alive until the new thread starts running and uses it. Obviously since the impl object contains the virtual _M_run() that actually runs the thread's code, this is quite important.
But this goes wrong because Clang destroys the impl in the parent thread, before the child thread can use it.
The apparently miscompiled code is in <thread>; I quote here from libstdc++ 4.7.2:
Clang destroys the impl with a backtrace that starts at shared_ptr::_M_destroy() ends at line 135 above. Remember, since the impl contains a shared_ptr to *itself*, the reference count should never fall below one until the thread exits, so this destruction is a mistake.
Running under valgrind confirms that there is a use-after-free here.
I don't know why Clang would do this.
Why does this show up as a pure virtual call? Simple: In the process of orderly destruction, the impl's vtable is reset to its base class vtable before the object is finally freed. That base vtable contains a pure virtual function, which is subsequently called. (Even though the memory was freed, the vtable pointer seems to remain unchanged.) I suppose this symptom would not occur if e.g. the base impl class had a pure virtual constructor.
Here's the backtrace, showing premature destruction. We can be sure that this destruction is premature because it's supposed to happen in the child thread, not in the parent.
(gdb) where
#0 std::_Sp_counted_ptr_inplace<std::thread::_Impl<std::_Bind_simple<void (*())()> >, std::allocator<std::thread::_Impl<std::_Bind_simple<void (*())()> > >, (__gnu_cxx::_Lock_policy)1>::_M_destroy() (this=0x804e008)
at /usr/bin/../lib/gcc/i486-linux-gnu/4.7/../../../../include/c++/4.7/bits/shared_ptr_base.h:418
#1 0x08048f4f in std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)1>::_M_release (this=0x804e008)
at /usr/bin/../lib/gcc/i486-linux-gnu/4.7/../../../../include/c++/4.7/bits/shared_ptr_base.h:164
#2 0x08048ecb in std::__shared_count<1>::~__shared_count (this=0xbffff464)
at /usr/bin/../lib/gcc/i486-linux-gnu/4.7/../../../../include/c++/4.7/bits/shared_ptr_base.h:558
#3 0x08048e97 in std::__shared_count<1>::~__shared_count (this=0xbffff464)
at /usr/bin/../lib/gcc/i486-linux-gnu/4.7/../../../../include/c++/4.7/bits/shared_ptr_base.h:556
#4 0x08048fac in std::__shared_ptr<std::thread::_Impl_base, 1>::~__shared_ptr (this=0xbffff460)
at /usr/bin/../lib/gcc/i486-linux-gnu/4.7/../../../../include/c++/4.7/bits/shared_ptr_base.h:813
#5 0x08048f87 in std::shared_ptr<std::thread::_Impl_base>::~shared_ptr (this=0xbffff460)
at /usr/bin/../lib/gcc/i486-linux-gnu/4.7/../../../../include/c++/4.7/bits/shared_ptr.h:93
#6 0x08048e07 in std::shared_ptr<std::thread::_Impl_base>::~shared_ptr (this=0xbffff460)
at /usr/bin/../lib/gcc/i486-linux-gnu/4.7/../../../../include/c++/4.7/bits/shared_ptr.h:93
#7 0x08048cd8 in std::thread::thread<void (&)(), >(void (&)(void)) (this=0xbffff4c0, __f=@0x8048a60: {void (void)} 0x8048a60 <f()>)
at /usr/bin/../lib/gcc/i486-linux-gnu/4.7/../../../../include/c++/4.7/thread:133
#8 0x08048c14 in std::thread::thread<void (&)(), >(void (&)(void)) (this=0xbffff4c0, __f=@0x8048a60: {void (void)} 0x8048a60 <f()>)
at /usr/bin/../lib/gcc/i486-linux-gnu/4.7/../../../../include/c++/4.7/thread:135
#9 0x08048a8c in main () at test.cc:9
The bug shows up as a pure virtual call, but the real error is use-after-free caused by apparent miscompilation of shared_ptr<>.
I've done deep repro on this, and what I find is that when creating a new thread, libstdc++ std:thread creates an impl object containing the virtual _M_run() function to run the thread's code. An odd detail is that this impl object contains a shared_ptr<> to itself; it's a shared_ptr<base> inside the base type, and the created impl object is always a type derived from this base. This approach, while weird, ensures that the impl object stays alive until the new thread starts running and uses it. Obviously since the impl object contains the virtual _M_run() that actually runs the thread's code, this is quite important.
But this goes wrong because Clang destroys the impl in the parent thread, before the child thread can use it.
The apparently miscompiled code is in <thread>; I quote here from libstdc++ 4.7.2:
129 template<typename _Callable, typename... _Args> thread( _M_make_ routine( std::__ bind_simple( _Callable> (__f), _Args>( __args) ...)));
130 explicit
131 thread(_Callable&& __f, _Args&&... __args)
132 {
133 _M_start_
134 std::forward<
135 std::forward<
136 }
Clang destroys the impl with a backtrace that starts at shared_ ptr::_M_ destroy( ) ends at line 135 above. Remember, since the impl contains a shared_ptr to *itself*, the reference count should never fall below one until the thread exits, so this destruction is a mistake.
Running under valgrind confirms that there is a use-after-free here.
I don't know why Clang would do this.
Why does this show up as a pure virtual call? Simple: In the process of orderly destruction, the impl's vtable is reset to its base class vtable before the object is finally freed. That base vtable contains a pure virtual function, which is subsequently called. (Even though the memory was freed, the vtable pointer seems to remain unchanged.) I suppose this symptom would not occur if e.g. the base impl class had a pure virtual constructor.
Here's the backtrace, showing premature destruction. We can be sure that this destruction is premature because it's supposed to happen in the child thread, not in the parent.
(gdb) where counted_ ptr_inplace< std::thread: :_Impl< std::_Bind_ simple< void (*())()> >, std::allocator< std::thread: :_Impl< std::_Bind_ simple< void (*())()> > >, (__gnu_ cxx::_Lock_ policy) 1>::_M_ destroy( ) (this=0x804e008) ../lib/ gcc/i486- linux-gnu/ 4.7/../ ../../. ./include/ c++/4.7/ bits/shared_ ptr_base. h:418 counted_ base<(_ _gnu_cxx: :_Lock_ policy) 1>::_M_ release (this=0x804e008) ../lib/ gcc/i486- linux-gnu/ 4.7/../ ../../. ./include/ c++/4.7/ bits/shared_ ptr_base. h:164 shared_ count<1> ::~__shared_ count (this=0xbffff464) ../lib/ gcc/i486- linux-gnu/ 4.7/../ ../../. ./include/ c++/4.7/ bits/shared_ ptr_base. h:558 shared_ count<1> ::~__shared_ count (this=0xbffff464) ../lib/ gcc/i486- linux-gnu/ 4.7/../ ../../. ./include/ c++/4.7/ bits/shared_ ptr_base. h:556 shared_ ptr<std: :thread: :_Impl_ base, 1>::~__shared_ptr (this=0xbffff460) ../lib/ gcc/i486- linux-gnu/ 4.7/../ ../../. ./include/ c++/4.7/ bits/shared_ ptr_base. h:813 ptr<std: :thread: :_Impl_ base>:: ~shared_ ptr (this=0xbffff460) ../lib/ gcc/i486- linux-gnu/ 4.7/../ ../../. ./include/ c++/4.7/ bits/shared_ ptr.h:93 ptr<std: :thread: :_Impl_ base>:: ~shared_ ptr (this=0xbffff460) ../lib/ gcc/i486- linux-gnu/ 4.7/../ ../../. ./include/ c++/4.7/ bits/shared_ ptr.h:93 :thread< void (&)(), >(void (&)(void)) (this=0xbffff4c0, __f=@0x8048a60: {void (void)} 0x8048a60 <f()>) ../lib/ gcc/i486- linux-gnu/ 4.7/../ ../../. ./include/ c++/4.7/ thread: 133 :thread< void (&)(), >(void (&)(void)) (this=0xbffff4c0, __f=@0x8048a60: {void (void)} 0x8048a60 <f()>) ../lib/ gcc/i486- linux-gnu/ 4.7/../ ../../. ./include/ c++/4.7/ thread: 135
#0 std::_Sp_
at /usr/bin/
#1 0x08048f4f in std::_Sp_
at /usr/bin/
#2 0x08048ecb in std::__
at /usr/bin/
#3 0x08048e97 in std::__
at /usr/bin/
#4 0x08048fac in std::__
at /usr/bin/
#5 0x08048f87 in std::shared_
at /usr/bin/
#6 0x08048e07 in std::shared_
at /usr/bin/
#7 0x08048cd8 in std::thread:
at /usr/bin/
#8 0x08048c14 in std::thread:
at /usr/bin/
#9 0x08048a8c in main () at test.cc:9