Segfault al declarar una variable de tipo vector <shared_ptr <int>>

Código

Aquí está el programa que da el segfault.

#include <iostream>
#include <vector>
#include <memory>

int main() 
{
    std::cout << "Hello World" << std::endl;

    std::vector<std::shared_ptr<int>> y {};  

    std::cout << "Hello World" << std::endl;
}

Por supuesto, hayabsolutamente nada malo en el propio programa. La causa raíz del segfault depende del entorno en el que se creó y ejecutó.

Antecedentes

Nosotros, en Amazon, utilizamos un sistema de compilación que construye e implementa los binarios (lib ybin) en uncasi Máquina de forma independiente. Para nuestro caso, eso básicamente significa que implementa el ejecutable (construido a partir del programa anterior) en$project_dir/build/bin/ ycasi todas sus dependencias (es decir, las bibliotecas compartidas) en$project_dir/build/lib/. ¿Por qué usé la frase?"casi" es porque para bibliotecas compartidas taleslibc.so, libm.so, ld-linux-x86-64.so.2 y posiblemente algunos otros, el ejecutable elige del sistema (es decir, de/lib64 ) Tenga en cuenta que essupuesto para recogerlibstdc++ de$project_dir/build/lib aunque.

Ahora lo ejecuto de la siguiente manera:

$ LD_LIBRARY_PATH=$project_dir/build/lib ./build/bin/run

segmentation fault

Sin embargo, si lo ejecuto, sin configurar elLD_LIBRARY_PATH. Funciona bien

Diagnósticos1. ldd

Aquí estánldd información para ambos casos (tenga en cuenta que he editado la salida para mencionar elcompleto versión de las bibliotecasdonde hay diferencia)

$ LD_LIBRARY_PATH=$project_dir/build/lib ldd ./build/bin/run

linux-vdso.so.1 =>  (0x00007ffce19ca000)
libstdc++.so.6 => $project_dir/build/lib/libstdc++.so.6.0.20 
libgcc_s.so.1 =>  $project_dir/build/lib/libgcc_s.so.1 
libc.so.6 => /lib64/libc.so.6 
libm.so.6 => /lib64/libm.so.6 
/lib64/ld-linux-x86-64.so.2 (0x0000562ec51bc000)

y sin LD_LIBRARY_PATH:

$ ldd ./build/bin/run

linux-vdso.so.1 =>  (0x00007fffcedde000)
libstdc++.so.6 => /usr/lib64/libstdc++.so.6.0.16 
libgcc_s.so.1 => /lib64/libgcc_s-4.4.6-20110824.so.1
libc.so.6 => /lib64/libc.so.6 
libm.so.6 => /lib64/libm.so.6 
/lib64/ld-linux-x86-64.so.2 (0x0000560caff38000)
2. gdb cuando falla
Program received signal SIGSEGV, Segmentation fault.
0x00007ffff7dea45c in _dl_fixup () from /lib64/ld-linux-x86-64.so.2
Missing separate debuginfos, use: debuginfo-install glibc-2.12-1.209.62.al12.x86_64
(gdb) bt
#0  0x00007ffff7dea45c in _dl_fixup () from /lib64/ld-linux-x86-64.so.2
#1  0x00007ffff7df0c55 in _dl_runtime_resolve () from /lib64/ld-linux-x86-64.so.2
#2  0x00007ffff7b1dc41 in std::locale::_S_initialize() () from $project_dir/build/lib/libstdc++.so.6
#3  0x00007ffff7b1dc85 in std::locale::locale() () from $project_dir/build/lib/libstdc++.so.6
#4  0x00007ffff7b1a574 in std::ios_base::Init::Init() () from $project_dir/build/lib/libstdc++.so.6
#5  0x0000000000400fde in _GLOBAL__sub_I_main () at $project_dir/build/gcc-4.9.4/include/c++/4.9.4/iostream:74
#6  0x00000000004012ed in __libc_csu_init ()
#7  0x00007ffff7518cb0 in __libc_start_main () from /lib64/libc.so.6
#8  0x0000000000401021 in _start ()
(gdb)
3. LD_DEBUG = todos

También intenté ver la información del enlazador habilitandoLD_DEBUG=all para el caso segfault. Encontré algo sospechoso, ya que buscapthread_once símbolo, y cuando no puede encontrar esto, da segfault (esa es mi interpretación del siguiente fragmento de salida BTW):

initialize program: $project_dir/build/bin/run

symbol=_ZNSt8ios_base4InitC1Ev;  lookup in file=$project_dir/build/bin/run [0]
symbol=_ZNSt8ios_base4InitC1Ev;  lookup in file=$project_dir/build/lib/libstdc++.so.6 [0]
binding file $project_dir/build/bin/run [0] to $project_dir/build/lib/libstdc++.so.6 [0]: normal symbol `_ZNSt8ios_base4InitC1Ev' [GLIBCXX_3.4]
symbol=_ZNSt6localeC1Ev;  lookup in file=$project_dir/build/bin/run [0]
symbol=_ZNSt6localeC1Ev;  lookup in file=$project_dir/build/lib/libstdc++.so.6 [0]
binding file $project_dir/build/lib/libstdc++.so.6 [0] to $project_dir/build/lib/libstdc++.so.6 [0]: normal symbol `_ZNSt6localeC1Ev' [GLIBCXX_3.4]
symbol=pthread_once;  lookup in file=$project_dir/build/bin/run [0]
symbol=pthread_once;  lookup in file=$project_dir/build/lib/libstdc++.so.6 [0]
symbol=pthread_once;  lookup in file=$project_dir/build/lib/libgcc_s.so.1 [0]
symbol=pthread_once;  lookup in file=/lib64/libc.so.6 [0]
symbol=pthread_once;  lookup in file=/lib64/libm.so.6 [0]
symbol=pthread_once;  lookup in file=/lib64/ld-linux-x86-64.so.2 [0]

Pero no veo ningunapthread_once para el caso cuando se ejecuta con éxito!

Preguntas

Sé que es muy difícil de depurar así y probablemente no he dado mucha información sobre los entornos y todo. Pero aún así, mi pregunta es: ¿cuál podría ser la posible causa de este segfault? ¿Cómo depurar más y encontrar eso? Una vez que encuentre el problema, la solución sería fácil.

Compilador y Plataforma

Estoy usandoCCG 4.9 en RHEL5.

Los experimentosE # 1

Si comento la siguiente línea:

std::vector<std::shared_ptr<int>> y {}; 

Se compila y funciona bien!

E # 2

Acabo de incluir el siguiente encabezado en mi programa:

#include <boost/filesystem.hpp>

y vinculado en consecuencia. Ahora funciona sin ningún defecto de seguridad. Entonces parece tener una dependencia delibboost_system.so.1.53.0., se cumplen algunos requisitos o se evita el problema.

E # 3

Desde que lo vi funcionar cuando hice el ejecutable para vincularlolibboost_system.so.1.53.0, así que hice lo siguiente paso a paso.

En lugar de usar#include <boost/filesystem.hpp> en el código mismo, uso el código original y lo ejecuté precargandolibboost_system.so utilizandoLD_PRELOAD como sigue:

$ LD_PRELOAD=$project_dir/build/lib/libboost_system.so $project_dir/build/bin/run

y funcionó con éxito!

Lo siguiente que hiceldd sobre ellibboost_system.so que dio una lista de libs, dos de las cuales fueron:

  /lib64/librt.so.1
  /lib64/libpthread.so.0

Entonces, en lugar de precargarlibboost_systemPrecargolibrt ylibpthread por separado:

$ LD_PRELOAD=/lib64/librt.so.1 $project_dir/build/bin/run

$ LD_PRELOAD=/lib64/libpthread.so.0 $project_dir/build/bin/run

En ambos casos, se ejecutó con éxito.

Ahora mi conclusión es que al cargar cualquieralibrt olibpthread (oambos ), se cumplen algunos requisitos o se evita el problema. Sin embargo, todavía no conozco la causa raíz del problema.

Opciones de compilación y vinculación

Dado que el sistema de compilación es complejo y hay muchas opciones que están allí por defecto. Así que traté de agregar explícitamente-lpthread usando CMake'sset comando, entonces funcionó, como ya lo hemos visto porprecarga libpthread ¡funciona!

Para ver elconstruir diferencia entre estos dos casos (cuando funciona ycuando-da-segfault), Lo construí enverboso modo pasando-v a GCC, para ver las etapas de compilación y las opciones a las que realmente pasacc1plus (compilador) ycollect2 (enlazador).

(Tenga en cuenta que las rutas se han editado por brevedad, utilizando signos de dólar y rutas ficticias.)

$ / gcc-4.9.4 / cc1plus -quiet -v -I / a / include -I / b / include -iprefix $ / gcc-4.9.4 / -MMD main.cpp.d -MF main.cpp.od - MT main.cpp. -march = core2 -auxbase-strip main.cpp.o -g -O3 -Wall -Wextra -std = gnu ++ 1y -version -fdiagnostics-color = auto -ftemplate-depth = 128 -fno-operator-names -o /tmp/ccxfkRyd.s

Independientemente de si funciona o no, los argumentos de la línea de comandos paracc1plus son exactamente lo mismo No hay diferencia en absoluto. Eso no parece ser muy útil.

La diferencia, sin embargo, está en el momento de vinculación. Esto es lo que veopara el caso cuando funciona:

$ / gcc-4.9.4 / collect2 -plugin $ / gcc-4.9.4 / liblto_plugin.so
-plugin-opt = $ / gcc-4.9.4 / lto-wrapper -plugin-opt = -fresolution = / tmp / cchl8RtI.res -plugin-opt = -pass-through = -lgcc_s -plugin-opt = -pass- a través = -lgcc -plugin-opt = -pass-through = -lpthread -plugin-opt = -pass-through = -lc -plugin-opt = -pass-through = -lgcc_s -plugin-opt = -pass-through = -lgcc --eh-frame-hdr -m elf_x86_64 -export-dynamic -dynamic-linker /lib64/ld-linux-x86-64.so.2 -o run /usr/lib/../lib64/crt1.o /usr/lib/../lib64/crti.o $ / gcc-4.9.4 / crtbegin.o -L / a / lib -L / b / lib -L / c / lib-lpthread --como necesaria main.cpp.o -lboost_timer -lboost_wave -lboost_chrono -lboost_filesystem -lboost_graph -lboost_locale -lboost_thread -lboost_wserialization -lboost_atomic -lboost_context -lboost_date_time -lboost_iostreams -lboost_math_c99 -lboost_math_c99f -lboost_math_c99l -lboost_math_tr1 -lboost_math_tr1f -lboost_math_tr1l -lboost_mpi -lboost_prg_exec_monitor -lboost_program_options -lboost_random -lboost_regex -lboost_serialization -lboost_signals -lboost_system -lboost_unit_test_framework -lboost_exception -lboost_test_exec_monitor -lbz / /: lib / c: / /: -l: / /: -l: / /: -l / b / c / b / c / b / b / b / b / b / b / b / b / b / b / b / b / b / b / b / b / b / b / b / b / b / b / b / b / b / b / b / b / b / b / b / b / b / b / b / b-b / b / b / b / b / b / b $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ -lboost_l / -lc / -lc / / -lc-l / b / 2 / b / 2 / b / 2 lgcc_s -lgcc-lpthread -lc -lgcc_s -lgcc $ / gcc-4.9.4 / crtend.o /usr/lib/../lib64/crtn.o

Como puedes ver,-lpthread es mencionadodos veces! El primero-lpthread (que es seguido por--as-needed) esdesaparecido para el caso cuando da segfault. Eso es elsolamente diferencia entre estos dos casos.

Salida denm -C en ambos casos

Curiosamente, la salida denm -C en ambos casos es idéntico (si ignora los valores enteros en las primeras columnas)

0000000000402580 d _DYNAMIC
0000000000402798 d _GLOBAL_OFFSET_TABLE_
0000000000401000 t _GLOBAL__sub_I_main
0000000000401358 R _IO_stdin_used
                 w _ITM_deregisterTMCloneTable
                 w _ITM_registerTMCloneTable
                 w _Jv_RegisterClasses
                 U _Unwind_Resume
0000000000401150 W std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>::_M_destroy()
0000000000401170 W std::vector<std::shared_ptr<int>, std::allocator<std::shared_ptr<int> > >::~vector()
0000000000401170 W std::vector<std::shared_ptr<int>, std::allocator<std::shared_ptr<int> > >::~vector()
0000000000401250 W std::vector<std::unique_ptr<int, std::default_delete<int> >, std::allocator<std::unique_ptr<int, std::default_delete<int> > > >::~vector()
0000000000401250 W std::vector<std::unique_ptr<int, std::default_delete<int> >, std::allocator<std::unique_ptr<int, std::default_delete<int> > > >::~vector()
                 U std::ios_base::Init::Init()
                 U std::ios_base::Init::~Init()
0000000000402880 B std::cout
                 U std::basic_ostream<char, std::char_traits<char> >& std::endl<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&)
0000000000402841 b std::__ioinit
                 U std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)
                 U operator delete(void*)
                 U operator new(unsigned long)
0000000000401510 r __FRAME_END__
0000000000402818 d __JCR_END__
0000000000402818 d __JCR_LIST__
0000000000402820 d __TMC_END__
0000000000402820 d __TMC_LIST__
0000000000402838 A __bss_start
                 U __cxa_atexit
0000000000402808 D __data_start
0000000000401100 t __do_global_dtors_aux
0000000000402820 t __do_global_dtors_aux_fini_array_entry
0000000000402810 d __dso_handle
0000000000402828 t __frame_dummy_init_array_entry
                 w __gmon_start__
                 U __gxx_personality_v0
0000000000402838 t __init_array_end
0000000000402828 t __init_array_start
00000000004012b0 T __libc_csu_fini
00000000004012c0 T __libc_csu_init
                 U __libc_start_main
                 w __pthread_key_create
0000000000402838 A _edata
0000000000402990 A _end
000000000040134c T _fini
0000000000400e68 T _init
0000000000401028 T _start
0000000000401054 t call_gmon_start
0000000000402840 b completed.6661
0000000000402808 W data_start
0000000000401080 t deregister_tm_clones
0000000000401120 t frame_dummy
0000000000400f40 T main
00000000004010c0 t register_tm_clones

Respuestas a la pregunta(2)

Su respuesta a la pregunta