Segfault na declaração de uma variável do tipo vetor <shared_ptr <int>>

Código

Aqui está o programa que fornece o 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;
}

Claro que existeabsolutamente nada de errado no próprio programa. A causa raiz do segfault depende do ambiente em que ele foi construído e executado.

fundo

Na Amazon, usamos um sistema de compilação que cria e implanta os binários (lib ebin) em umquase maneira independente da máquina. Para o nosso caso, isso significa basicamente que ele implementa o executável (criado a partir do programa acima) no$project_dir/build/bin/ equase todas as suas dependências (ou seja, as bibliotecas compartilhadas) em$project_dir/build/lib/. Por que eu usei a frase"quase" é porque para bibliotecas compartilhadas comolibc.so, libm.so, ld-linux-x86-64.so.2 e possivelmente alguns outros, as opções executáveis do sistema (ou seja, de/lib64 ) Note que ésuposto escolherlibstdc++ de$project_dir/build/lib Apesar.

Agora eu corro da seguinte forma:

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

segmentation fault

No entanto, se eu executá-lo, sem definir oLD_LIBRARY_PATH. Corre bem.

Diagnóstico1. LDD

Aqui estãoldd informações para os dois casos (observe que eu editei a saída para mencionar ocheio versão das bibliotecasonde houver diferença)

$ 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)

e sem 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 quando segfaults
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 = tudo

Também tentei ver as informações do vinculador ativandoLD_DEBUG=all para o caso segfault. Encontrei algo suspeito, pois ele procurapthread_once símbolo e, quando não é possível encontrá-lo, fornece segfault (que é minha interpretação do seguinte trecho de saída 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]

Mas eu não vejo nenhumpthread_once para o caso em que é executado com sucesso!

Questões

Eu sei que é muito difícil depurar assim e provavelmente não dei muitas informações sobre os ambientes e tudo. Mas, ainda assim, minha pergunta é: qual poderia ser a possível causa raiz desse segfault? Como depurar ainda mais e encontrar isso? Depois de encontrar o problema, a correção seria fácil.

Compilador e Plataforma

estou a usarGCC 4.9 no RHEL5.

ExperiênciasE # 1

Se eu comentar a seguinte linha:

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

Compila e corre bem!

E # 2

Acabei de incluir o seguinte cabeçalho no meu programa:

#include <boost/filesystem.hpp>

e vinculado de acordo. Agora ele funciona sem nenhum segfault. Então parece ter uma dependência delibboost_system.so.1.53.0., alguns requisitos foram atendidos ou o problema foi contornado!

E # 3

Desde que eu vi que funcionava quando tornei o executável vinculado alibboost_system.so.1.53.0, então eu fiz as seguintes coisas passo a passo.

Ao invés de usar#include <boost/filesystem.hpp> no próprio código, eu uso o código original e o executei pré-carregandolibboost_system.so usandoLD_PRELOAD do seguinte modo:

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

e correu com sucesso!

Em seguida eu fizldd nolibboost_system.so que forneceu uma lista de bibliotecas, duas das quais foram:

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

Então, em vez de pré-carregarlibboost_systemEu pré-carregolibrt elibpthread separadamente:

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

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

Nos dois casos, ele foi executado com sucesso.

Agora, minha conclusão é que, carregandolibrt oulibpthread (ouambos ), alguns requisitos foram atendidos ou o problema foi contornado! Ainda não sei a causa raiz do problema.

Opções de compilação e vinculação

Como o sistema de compilação é complexo e existem muitas opções por padrão. Então eu tentei adicionar explicitamente-lpthread usando o CMakeset comando, então funcionou, como já vimos isso porpré-carregamento libpthread funciona!

Para ver oConstruir diferença entre esses dois casos (quando funciona equando-dá-segfault), Eu o construíverboso modo passando-v para o GCC, para ver os estágios de compilação e as opções para as quais ele realmente passacc1plus (compilador) ecollect2 (vinculador).

(Observe que os caminhos foram editados por questões de brevidade, usando caminhos de cifrão e fictícios.)

$ / 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.o -D_GNU_SOURCE -D_REENTRANT -D __USE_XOPEN2K8 -D _LARGEFILE_SOURCE -D _FILE_OFFSET_BITS = 64 -D __STDC_FORMAT_MACROS -D __STDC_LIMIT_MACROS -d NDEBUG $ /pp: -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

Independentemente de funcionar ou não, os argumentos da linha de comando paracc1plus são exatamente iguais. Nenhuma diferença. Isso não parece ser muito útil.

A diferença, no entanto, está no momento da ligação. Aqui está o que eu vejo,para o caso em que 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- atravé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 execute /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 necessária 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 -lbz2 -licui18n -licul / /: lib: lgcc_s -lgcc-lpthread -lc -lgcc_s -lgcc $ / gcc-4.9.4 / crtend.o /usr/lib/../lib64/crtn.o

Como você pode ver,-lpthread é mencionadoduas vezes! O primeiro-lpthread (que é seguido por--as-needed) éausência de para o caso em que dá segfault. Essa é asó diferença entre esses dois casos.

Saída denm -C em ambos os casos

Curiosamente, a produção denm -C em ambos os casos é idêntico (se você ignorar os valores inteiros nas primeiras colunas)

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

questionAnswers(2)

yourAnswerToTheQuestion