[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