¿Para qué sirve __gxx_personality_v0?

103

Esta es una pregunta de segunda mano de un sitio de desarrollo de SO, pero me dio curiosidad ya que no pude encontrar una explicación decente en ninguna parte.

Al compilar y vincular un programa C ++ independiente usando gcc, a veces ocurre un error de vinculador como este:

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

Aparentemente, esto se debe a que este símbolo está definido en libstdc ++, que falta en un entorno independiente. Solucionar el problema simplemente requiere definir este símbolo en alguna parte:

void *__gxx_personality_v0;

Lo cual es bueno, pero no me gustan las cosas que simplemente funcionan mágicamente ... Entonces la pregunta es, ¿cuál es el propósito de este símbolo?

Bruce Johnston
fuente

Respuestas:

93

Se usa en las tablas de pila que no se deshacen, que puede ver, por ejemplo, en el resultado del ensamblaje de mi respuesta a otra pregunta . Como se mencionó en esa respuesta, su uso está definido por Itanium C ++ ABI , donde se llama Rutina de personalidad .

La razón por la que "funciona" definiéndolo como un puntero nulo global NULL es probablemente porque nada lanza una excepción. Cuando algo intenta lanzar una excepción, verá que se comporta mal.

Por supuesto, si nada usa excepciones, puede deshabilitarlas con -fno-exceptions(y si nada usa RTTI, también puede agregar -fno-rtti). Si los está usando, debe (como ya se indicaron otras respuestas) vincular con en g++lugar de gcc, que se agregará -lstdc++por usted.

CesarB
fuente
2
Gracias por el dato -fno-exceptions. Agregué CPPFLAGS += -fno-exceptionsa mi archivo MAKE y eso resolvió el error.
Alan Kinnaman
12

Es parte del manejo de excepciones. El mecanismo gcc EH permite mezclar varios modelos EH, y se invoca una rutina de personalidad para determinar si una excepción coincide, qué finalización invocar, etc. Esta rutina de personalidad específica es para el manejo de excepciones de C ++ (a diferencia de, digamos, gcj / Java manejo de excepciones).

Martin contra Löwis
fuente
11

El manejo de excepciones se incluye en las implementaciones independientes.

La razón de esto es que posiblemente lo use gccpara compilar su código. Si compila con la opción -###, notará que falta la opción del vinculador -lstdc++cuando invoca el proceso del vinculador. Compilar con g++incluirá esa biblioteca y, por lo tanto, los símbolos definidos en ella.

Johannes Schaub - litb
fuente
Siempre pensé que compilar con g ++ solo era necesario cuando específicamente quería decirle al compilador que el código era C ++ (por ejemplo, falta la extensión). Ahora parece que compilar código C ++ con gcc no incluye la inclusión de las bibliotecas come. Aparte de que faltan algunas bibliotecas, ¿existen otros "efectos secundarios" de compilar my file.cppcon en gcclugar de g++?
Lazer
1
@eSkay que yo sepa, la vinculación de libstdc++es la única diferencia entre los dos.
Johannes Schaub - litb
6

Un grep rápido de la libstd++base del código reveló los siguientes dos usos de __gx_personality_v0:

En libsupc ++ / desenrollar-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 *);

En 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: en realidad es un poco más complicado que eso; hay una compilación condicional que puede cambiar algunos detalles).

Entonces, siempre que su código no esté usando realmente el manejo de excepciones, definir el símbolo como void*no afectará nada, pero tan pronto como lo haga, se bloqueará: __gxx_personality_v0es una función, no un objeto global, así que intentar llamar a la función va a saltar a la dirección 0 y provocar una falla de segmento.

Adam Rosenfield
fuente
No necesariamente saltar a 0; lo global no está inicializado, por lo que en realidad podría tener cualquier valor.
extraño
6
más extraño, los globales se inicializan en cero si el programador no los inicializa
Johannes Schaub - litb
@litb: esto solo es cierto si el kernel implementa la puesta a cero de la sección bss :-P. Pero sí, deberían ser 0 inicializados por el bien de la cordura.
Evan Teran
9
@Evan Teran: No, una implementación de C conforme siempre inicializará los globales a 0. Consulte §5.1.2 y §6.7.8 párrafo 10 del estándar C99.
Adam Rosenfield
6

Tuve este error una vez y descubrí el origen:

Estaba usando un compilador gcc y mi archivo fue llamado a CLIENT.Cpesar de que estaba haciendo un programa en C y no en un programa C ++.

gcc reconoce la .Cextensión como programa C ++ y la .cextensión como programa C (tenga cuidado con la c pequeña y la C grande).

Así que cambié el nombre de mi CLIENT.cprograma de archivos y funcionó.

jlguenego
fuente
2

Las respuestas anteriores son correctas: se usa en el manejo de excepciones. El manual de la versión 6 de GCC tiene más información (que ya no está presente en el manual de la versión 7). El error puede surgir al vincular una función externa que, desconocida para GCC, arroja excepciones de Java.

sagitta
fuente