Para que serve __gxx_personality_v0?


103

Esta é uma pergunta de segunda mão de um site de desenvolvimento de sistema operacional, mas me deixou curioso, pois não consegui encontrar uma explicação decente em lugar nenhum.

Ao compilar e vincular um programa C ++ independente usando gcc, às vezes ocorre um erro de vinculador como este:

out/kernel.o:(.eh_frame+0x11): undefined reference to `__gxx_personality_v0'

Aparentemente, isso ocorre porque esse símbolo é definido em libstdc ++, que está ausente em um ambiente independente. Para resolver o problema, basta definir este símbolo em algum lugar:

void *__gxx_personality_v0;

O que é legal, mas eu não gosto de coisas que simplesmente funcionam magicamente ... Então a questão é: qual é o propósito deste símbolo?

Respostas:


93

Ele é usado nas tabelas de desdobramento da pilha, que você pode ver, por exemplo, na saída da montagem de minha resposta a outra pergunta . Conforme mencionado nessa resposta, seu uso é definido pelo Itanium C ++ ABI , onde é denominado Rotina de Personalidade .

A razão pela qual "funciona" definindo-o como um ponteiro void NULL global é provavelmente porque nada está lançando uma exceção. Quando algo tentar lançar uma exceção, você verá que ele se comportou mal.

Claro, se nada estiver usando exceções, você pode desativá-las com -fno-exceptions(e se nada estiver usando RTTI, você também pode adicionar -fno-rtti). Se você os estiver usando, deverá (como outras respostas já mencionadas) vincular com em g++vez de gcc, o que adicionará -lstdc++para você.


2
Obrigado pela dica sobre -fno-exceptions. Eu adicionei CPPFLAGS += -fno-exceptionsao meu makefile, e isso resolveu o erro.
Alan Kinnaman

12

Faz parte do tratamento de exceções. O mecanismo gcc EH permite misturar vários modelos EH, e uma rotina de personalidade é chamada para determinar se uma exceção corresponde, qual finalização invocar, etc. Esta rotina de personalidade específica é para tratamento de exceção C ++ (em oposição a, digamos, gcj / Java manipulação de exceção).


11

O tratamento de exceções está incluído em implementações independentes.

A razão disso é que você possivelmente usa gccpara compilar seu código. Se você compilar com a opção -###, notará que falta a opção do vinculador -lstdc++ao invocar o processo do vinculador. Compilar com g++incluirá essa biblioteca e, portanto, os símbolos definidos nela.


Sempre pensei que compilar com g ++ só fosse necessário quando você quisesse especificamente dizer ao compilador que o código era C ++ (por exemplo - extensão ausente). Agora, parece que a compilação do código C ++ com gcc perde a inclusão de bibliotecas come. Além de perder algumas bibliotecas, existem alguns outros "efeitos colaterais" de compilar file.cppcom em gccvez de g++?
Lazer de

1
@eSkay, tanto quanto eu sei, a ligação de libstdc++é a única diferença entre os dois.
Johannes Schaub - litb

6

Um rápido grep da libstd++base de código revelou os seguintes dois usos de __gx_personality_v0:

Em libsupc ++ / unwind-cxx.h

// GNU C++ personality routine, Version 0.                                      
extern "C" _Unwind_Reason_Code __gxx_personality_v0
     (int, _Unwind_Action, _Unwind_Exception_Class,
      struct _Unwind_Exception *, struct _Unwind_Context *);

Em libsupc ++ / eh_personality.cc

#define PERSONALITY_FUNCTION    __gxx_personality_v0
extern "C" _Unwind_Reason_Code
PERSONALITY_FUNCTION (int version,
                      _Unwind_Action actions,
                      _Unwind_Exception_Class exception_class,
                      struct _Unwind_Exception *ue_header,
                      struct _Unwind_Context *context)
{
  // ... code to handle exceptions and stuff ...
}

(Nota: na verdade é um pouco mais complicado do que isso; há algumas compilações condicionais que podem alterar alguns detalhes).

Então, contanto que seu código não esteja realmente usando tratamento de exceção, definir o símbolo como void*não afetará nada, mas assim que isso acontecer, você irá travar - __gxx_personality_v0é uma função, não um objeto global, então tentar chamar a função vai pular para o endereço 0 e causar um segfault.


Não necessariamente pula para 0; o global não foi inicializado, então poderia ter qualquer valor, na verdade.
strager

6
strager, os globais são inicializados com zero se o programador não os inicializar
Johannes Schaub - litb

@litb: isso só é verdade se o kernel implementa zerar a seção bss :-P. Mas sim, eles devem ser 0 inicializados por uma questão de sanidade.
Evan Teran

9
@Evan Teran: Não, uma implementação C em conformidade sempre inicializará os globais para 0. Consulte §5.1.2 e §6.7.8 parágrafo 10 do padrão C99.
Adam Rosenfield,

6

Tive este erro uma vez e descobri a origem:

Eu estava usando um compilador gcc e meu arquivo foi chamado CLIENT.Capesar de eu estar fazendo um programa C e não um programa C ++.

gcc reconhece a .Cextensão como um programa C ++ e a .cextensão como um programa C (cuidado com o C minúsculo e o C grande).

Então, renomeei meu CLIENT.cprograma de arquivos e funcionou.


2

As respostas acima estão corretas: ele é usado no tratamento de exceções. O manual do GCC versão 6 contém mais informações (que não estão mais presentes no manual da versão 7). O erro pode surgir ao vincular uma função externa que - desconhecida do GCC - lança exceções Java.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.