dlclose () não chama o destruidor de objetos globais
Pergunta
plugin1.cpp:
#include <iostream>
static class TestStatic {
public:
TestStatic() {
std::cout << "TestStatic create" << std::endl;
}
~TestStatic() {
std::cout << "TestStatic destroy" << std::endl;
}
} test_static;
host.cpp
#include <dlfcn.h>
#include <iostream>
int main(int argc,char *argv[]) {
void* handle = dlopen("./plugin1.so",RTLD_NOW | RTLD_LOCAL );
dlclose(handle);
return 0;
}
Construa e corra:
>g++ -c plugin1.cpp -o plugin1.o -fPIC
>g++ -shared plugin.o -o plugin1.so
>g++ host.cpp -o host -ldl
>./host
>TestStatic create
>Segmentation fault
Por que teststatic :: ~ testStatic chamado em 'exit ()', mas não em 'dlclose ()'?
Solução
O padrão C ++ exige que os destruidores sejam chamados a objetos globais quando um programa sai na ordem oposta de construção. A maioria das implementações lidou com isso chamando a rotina ATEXIT da biblioteca C para registrar os destruidores. Isso é problemático porque o padrão de 1999 C exige apenas que a implementação suportasse 32 funções registradas, embora a maioria das implementações suporta muito mais. Mais importante, ele não lida com a habilidade na maioria das implementações de remover os DSOs de uma imagem de programa em execução, ligando para o DLCLOSE antes do término do programa.
Esse problema é abordado em versões posteriores do GCC, incluindo biblioteca padrão e linker C/C ++. Basicamente, os destruidores de C ++ devem ser registrados usando __cxa_atexit
função em vez de atexit
(3).
Para os detalhes técnicos completos sobre __cxa_atexit
, Vejo Especificação Itanium C ++ ABI.
Não está claro na sua pergunta qual versão do GCC, Linker e Standard C Library você está usando. Além disso, o código que você forneceu não atende Posix padrão como não há RTDL_NOW
ou RTDL_LOCAL
macros definidos. Eles são RTLD_NOW
e RTLD_LOCAL
(Vejo Dlopen).
Se sua biblioteca padrão C não suportar __cxa_atexit
, você pode precisar desativá -lo especificando -fno-use-cxa-atexit
Bandeira do GCC:
-Fuse-CXA-ATEXIT
Registre os destruidores para objetos com duração de armazenamento estático com a função __cxA_ ATEXIT em vez da função ATEXIT. Esta opção é necessária para o manuseio totalmente compatível com os destruidores estáticos, mas só funcionará se sua biblioteca C suportar __cxa_atexit.
Mas isso pode resultar em um problema em que os destruidores são chamados em ordem diferente ou não. Portanto, a melhor solução em caso de quebra __cxa_atexit
O suporte ou nenhum suporte não é usar objetos estáticos com destruidores em suas bibliotecas compartilhadas.