https://gcc.gnu.org/bugzilla/show_bug.cgi?id=88782
Bug ID: 88782 Summary: Crash when mixing make_shared from gcc <= 8.2 with make_shared from gcc >= 8.3 Product: gcc Version: 9.0 Status: UNCONFIRMED Severity: normal Priority: P3 Component: libstdc++ Assignee: unassigned at gcc dot gnu.org Reporter: romain.geissler at amadeus dot com Target Milestone: --- Hi, The change introduced in r266380 makes newer gcc >= 8.3 and gcc 9 sometimes incompatible with object files (archive libraries) generated with gcc <= 8.2, even when all the generated objects are using -frtti. See this example where mixing an old library build with an old gcc 8 and a new library build with a new gcc 8 result in the end in a segfault: cat > A.h <<END_OF_FILE class A { public: class Constructor1 {}; class Constructor2 {}; A(A::Constructor1) {}; A(A::Constructor2) {}; }; END_OF_FILE cat > library1.cpp <<END_OF_FILE #include "A.h" #include <memory> void f1() { std::make_shared<A>(A::Constructor1()); } END_OF_FILE cat > library2.cpp <<END_OF_FILE #include "A.h" #include <memory> void f2() { std::make_shared<A>(A::Constructor2()); } END_OF_FILE cat > main.cpp <<END_OF_FILE #include <memory> extern void f1(); extern void f2(); int main() { f1(); f2(); } END_OF_FILE ### Built like this: ### old-g++-8 -g -o library1.o -c library1.cpp ar cr library1.a library1.o new-g++-8 -g -o library2.o -c library2.cpp ar cr library2.a library2.o new-g++-8 -g -o main.o -c main.cpp new-g++-8 -o main main.o library1.a library2.a (in my case, old-g++-8 is actually named /remote/tools/Linux/2.6/1A/toolchain/x86_64-2.6.32-v4.0.40/bin/g++ and new-g++-8 is actually named /remote/tools/Linux/2.6/1A/toolchain/x86_64-2.6.32-v4.0.46/bin/g++) ### When you run it (with gdb): ### (gdb) r Starting program: /tmp/reproduce-gcc-make-shared/main Program received signal SIGSEGV, Segmentation fault. 0x00000000004011bd in std::type_info::operator== (this=0x403200 <std::_Sp_make_shared_tag::_S_ti()::__tag>, __arg=...) at /remote/tools/Linux/2.6/1A/toolchain/x86_64-2.6.32-v4.0.40/include/c++/8.2.1/typeinfo:123 123 || (__name[0] != '*' && (gdb) bt #0 0x00000000004011bd in std::type_info::operator== (this=0x403200 <std::_Sp_make_shared_tag::_S_ti()::__tag>, __arg=...) at /remote/tools/Linux/2.6/1A/toolchain/x86_64-2.6.32-v4.0.40/include/c++/8.2.1/typeinfo:123 #1 0x0000000000401d6f in std::_Sp_counted_ptr_inplace<A, std::allocator<A>, (__gnu_cxx::_Lock_policy)2>::_M_get_deleter (this=0x418c20, __ti=...) at /remote/tools/Linux/2.6/1A/toolchain/x86_64-2.6.32-v4.0.40/include/c++/8.2.1/bits/shared_ptr_base.h:569 #2 0x000000000040176e in std::__shared_count<(__gnu_cxx::_Lock_policy)2>::_M_get_deleter (this=0x7fffffffc4e8, __ti=...) at /remote/tools/Linux/2.6/1A/toolchain/x86_64-2.6.32-v4.0.40/include/c++/8.2.1/bits/shared_ptr_base.h:749 #3 0x0000000000401f8c in std::__shared_ptr<A, (__gnu_cxx::_Lock_policy)2>::__shared_ptr<std::allocator<A>, A::Constructor2> (this=0x7fffffffc4e0, __tag=..., __a=...) at /remote/tools/Linux/2.6/1A/toolchain/x86_64-2.6.32-v4.0.46/include/c++/8.2.1/bits/shared_ptr_base.h:1328 #4 0x0000000000401f1d in std::shared_ptr<A>::shared_ptr<std::allocator<A>, A::Constructor2> (this=0x7fffffffc4e0, __tag=..., __a=...) at /remote/tools/Linux/2.6/1A/toolchain/x86_64-2.6.32-v4.0.46/include/c++/8.2.1/bits/shared_ptr.h:360 #5 0x0000000000401ee0 in std::allocate_shared<A, std::allocator<A>, A::Constructor2> (__a=..., __args#0=...) at /remote/tools/Linux/2.6/1A/toolchain/x86_64-2.6.32-v4.0.46/include/c++/8.2.1/bits/shared_ptr.h:707 #6 0x0000000000401e68 in std::make_shared<A, A::Constructor2> (__args#0=...) at /remote/tools/Linux/2.6/1A/toolchain/x86_64-2.6.32-v4.0.46/include/c++/8.2.1/bits/shared_ptr.h:723 #7 0x0000000000401e00 in f2 () at library2.cpp:6 #8 0x0000000000401152 in main () at main.cpp:9 The reason for that is that the symbols and the vtable for the class std::_Sp_counted_ptr_inplace comes from the first object that defines it, which in this case is library1 built with the old gcc behavior. This class is common both when you call make_shared with constructor 1 or constructor 2, and this is where _M_get_deleter does it's check for the typeid(_Sp_make_shared_tag). On the other side, there are two different callers of _M_get_deleter. One with the old typeid(__tag) tag in the library 1 (when instantiating the call to constructor 1) and one with the new _Sp_make_shared_tag::_S_ti() tag in the library 2 (when instantiating the call to constructor 2). Because the linker picked the "wrong" old _M_get_deleter, the second call ends it in seg fault. Do we foresee a way to avoid rebuilding all libraries that were built with gcc <= 8.2 when mixing them with libraries build with gcc >= 8.3 ? I am thinking about doing something like this: --- bits/shared_ptr_base.h +++ bits/shared_ptr_base.h @@ -509,8 +509,12 @@ static const type_info& _S_ti() noexcept _GLIBCXX_VISIBILITY(default) { +#if __cpp_rtti + return typeid(_Sp_make_shared_tag); +#else alignas(type_info) static constexpr char __tag[sizeof(type_info)] = { }; return reinterpret_cast<const type_info&>(__tag); +#endif } }; @@ -567,12 +571,6 @@ // as a real type_info object. if (&__ti == &_Sp_make_shared_tag::_S_ti()) return const_cast<typename remove_cv<_Tp>::type*>(_M_ptr()); -#if __cpp_rtti - // Callers compiled with old libstdc++ headers and RTTI enabled - // might pass this instead: - else if (__ti == typeid(_Sp_make_shared_tag)) - return const_cast<typename remove_cv<_Tp>::type*>(_M_ptr()); -#endif return nullptr; } but that may be quite wrong too (I can just confirm it works on my reduced test case). Cheers, Romain