[Bug libstdc++/61519] New: Seemingly incorrect vtable reference when libstdc++ built with RTTI
matthew at theiselins dot net
gcc-bugzilla@gcc.gnu.org
Mon Jun 16 07:22:00 GMT 2014
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61519
Bug ID: 61519
Summary: Seemingly incorrect vtable reference when libstdc++
built with RTTI
Product: gcc
Version: 4.8.2
Status: UNCONFIRMED
Severity: normal
Priority: P3
Component: libstdc++
Assignee: unassigned at gcc dot gnu.org
Reporter: matthew at theiselins dot net
I have run into a problem today with a GCC toolchain and libstdc++ for a custom
OS target, and the x86_64 architecture (https://github.com/pcmattman/pedigree).
The problem is "solved" by building the toolchain and libstdc++ with -fno-rtti
(as the code paths inside libstdc++ are protected by preprocessor checks for
__GXX_RTTI).
GCC 4.8.2, with one patch applied:
https://github.com/pcmattman/pedigree/blob/develop/compilers/pedigree-gcc.patch
(adding the x86_64-pedigree target).
The behaviour seen is a segfault, which seems to be caused by an incorrect
vtable offset in __dynamic_cast. This segfault happens when running global
constructors when loading a program that pulls in global objects (a simple C++
Hello World is enough).
The basic trace is as follows:
1. libstdc++-v3/src/c++98/ios_init.cc:91
new (&cout) ostream(&buf_cout_sync);
1.5 ... basic_ios constructors ...
2. libstdc++-v3/include/bits/basic_ios.tcc:131
// Cache locale data and specific facets used by iostreams.
_M_cache_locale(_M_ios_locale);
3. libstdc++-v3/include/bits/basic_ios.tcc:155
template<typename _CharT, typename _Traits>
void
basic_ios<_CharT, _Traits>::_M_cache_locale(const locale& __loc)
{
if (__builtin_expect(has_facet<__ctype_type>(__loc), true)) // <--
_M_ctype = &use_facet<__ctype_type>(__loc);
4. libstdc++-v3/include/bits/locale_classes.tcc:102
template<typename _Facet>
bool
has_facet(const locale& __loc) throw()
{
const size_t __i = _Facet::id._M_id();
const locale::facet** __facets = __loc._M_impl->_M_facets;
return (__i < __loc._M_impl->_M_facets_size
#ifdef __GXX_RTTI
&& dynamic_cast<const _Facet*>(__facets[__i])); // <--
#else
&& static_cast<const _Facet*>(__facets[__i]));
#endif
}
5. libstdc++-v3/libsupc++/dyncast.cc:59
whole_type->__do_dyncast (src2dst, __class_type_info::__contained_public,
dst_type, whole_ptr, src_type, src_ptr, result);
6: libstdc++-v3/libsupc++/class_type_info.cc:45
bool __class_type_info::
__do_upcast (const __class_type_info *dst_type,
void **obj_ptr) const
{
__upcast_result result (__vmi_class_type_info::__flags_unknown_mask);
__do_upcast (dst_type, *obj_ptr, result); // <--
if (!contained_public_p (result.part2dst))
return false;
*obj_ptr = const_cast <void *> (result.dst_ptr);
return true;
}
----
For the 'whole_type->__do_dyncast' call, the following is the case (registers
adjusted for shared object base, 'nm' and 'c++filt' output added):
vtable for whole_type RAX 0x2a7d80
00000000002a7d80 V _ZTVN10__cxxabiv121__vmi_class_type_infoE [Vtable for
__cxxabiv1::__vmi_class_type_info]
whole_type RDI 0x2a7e80
00000000002a7e80 V _ZTISt5ctypeIcE [typeinfo for std::ctype<char>]
src2dst RSI 0
__class_type_info::__contained_public RDX 0x6
dst_type RCX 0x2a7e80
00000000002a7e80 V _ZTISt5ctypeIcE [typeinfo for std::ctype<char>]
whole_ptr R8 0x2ae6a0
00000000002ae6a0 b _ZN12_GLOBAL__N_17ctype_cE [(anonymous namespace)::ctype_c]
src_type R9 0x2a7fb0
00000000002a7fb0 V _ZTINSt6locale5facetE [typeinfo for std::locale::facet]
Disassembly:
42bd4: ff 50 38 callq *0x38(%rax)
Looking at the vtable (via -fdump-class-hierarchy), it seems offset 0x38 is a
__do_upcast, not __do_dyncast.
Vtable for __cxxabiv1::__vmi_class_type_info
__cxxabiv1::__vmi_class_type_info::_ZTVN10__cxxabiv121__vmi_class_type_infoE:
11u entries
0 (int (*)(...))0
8 (int (*)(...))(& _ZTIN10__cxxabiv121__vmi_class_type_infoE)
16 (int (*)(...))__cxxabiv1::__vmi_class_type_info::~__vmi_class_type_info
24 (int (*)(...))__cxxabiv1::__vmi_class_type_info::~__vmi_class_type_info
32 (int (*)(...))std::type_info::__is_pointer_p
40 (int (*)(...))std::type_info::__is_function_p
48 (int (*)(...))__cxxabiv1::__class_type_info::__do_catch
56 (int (*)(...))__cxxabiv1::__class_type_info::__do_upcast
64 (int (*)(...))__cxxabiv1::__vmi_class_type_info::__do_upcast
72 (int (*)(...))__cxxabiv1::__vmi_class_type_info::__do_dyncast
80 (int (*)(...))__cxxabiv1::__vmi_class_type_info::__do_find_public_src
The segfault takes place when __do_upcast attempts to dereference obj_ptr,
because obj_ptr == (void **) 6.
I'm not completely sure where to begin looking on this. I have a temporary
workaround, as mentioned, but would be interested in determining what happened
here.
The ideal outcome would be to be able to build libstdc++ without -fno-rtti,
without running into this segfault in programs linked against it.
More information about the Gcc-bugs
mailing list