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.