Wofür ist __gxx_personality_v0?

103

Dies ist eine gebrauchte Frage von einer OS-Entwicklungsseite, die mich jedoch neugierig machte, da ich nirgendwo eine anständige Erklärung finden konnte.

Beim Kompilieren und Verknüpfen eines freistehenden C ++ - Programms mit gcc tritt manchmal ein Linkerfehler wie der folgende auf:

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

Dies liegt anscheinend daran, dass dieses Symbol in libstdc ++ definiert ist, das in einer freistehenden Umgebung fehlt. Um das Problem zu beheben, muss dieses Symbol einfach irgendwo definiert werden:

void *__gxx_personality_v0;

Was schön ist, aber ich mag keine Dinge, die einfach magisch funktionieren ... Die Frage ist also, was ist der Zweck dieses Symbols?

Bruce Johnston
quelle

Antworten:

92

Es wird in den Stapel-Unwiding-Tabellen verwendet, die Sie beispielsweise in der Assembly-Ausgabe meiner Antwort auf eine andere Frage sehen können . Wie in dieser Antwort erwähnt, wird seine Verwendung durch das Itanium C ++ ABI definiert , wo es als Persönlichkeitsroutine bezeichnet wird .

Der Grund, warum es "funktioniert", indem es als globaler NULL-Void-Zeiger definiert wird, liegt wahrscheinlich darin, dass nichts eine Ausnahme auslöst. Wenn etwas versucht, eine Ausnahme auszulösen, werden Sie feststellen, dass sie sich schlecht verhält.

Wenn nichts Ausnahmen verwendet, können Sie diese natürlich mit deaktivieren -fno-exceptions(und wenn nichts RTTI verwendet, können Sie auch hinzufügen -fno-rtti). Wenn Sie sie verwenden, müssen Sie (wie andere bereits erwähnte Antworten) g++stattdessen mit verknüpfen gcc, was -lstdc++für Sie hinzugefügt wird .

CesarB
quelle
2
Danke für den Tipp über -fno-exceptions. Ich CPPFLAGS += -fno-exceptionshabe meinem Makefile hinzugefügt , und das hat den Fehler behoben.
Alan Kinnaman
12

Es ist Teil der Ausnahmebehandlung. Der gcc EH-Mechanismus ermöglicht das Mischen verschiedener EH-Modelle, und eine Persönlichkeitsroutine wird aufgerufen, um zu bestimmen, ob eine Ausnahme übereinstimmt, welche Finalisierung aufgerufen werden soll usw. Diese spezifische Persönlichkeitsroutine ist für die Behandlung von C ++ - Ausnahmen vorgesehen (im Gegensatz beispielsweise zu gcj / Java) Ausnahmebehandlung).

Martin v. Löwis
quelle
11

Die Ausnahmebehandlung ist in freistehenden Implementierungen enthalten.

Der Grund dafür ist, dass Sie möglicherweise gccIhren Code kompilieren. Wenn Sie mit der Option kompilieren, werden -###Sie feststellen, dass die Linker-Option fehlt, -lstdc++wenn der Linker-Prozess aufgerufen wird. Das Kompilieren mit g++enthält diese Bibliothek und damit die darin definierten Symbole.

Johannes Schaub - litb
quelle
Ich habe immer gedacht, dass das Kompilieren mit g ++ nur erforderlich ist, wenn Sie dem Compiler ausdrücklich mitteilen möchten, dass der Code C ++ ist (z. B. fehlende Erweiterung). Nun scheint es, dass beim Kompilieren von C ++ - Code mit gcc die Einbeziehung von Come-Bibliotheken fehlt. Gibt es neben dem Fehlen einiger Bibliotheken noch andere "Nebenwirkungen" beim Kompilieren meiner file.cppmit gccstatt g++?
Lazer
1
@eSkay Soweit ich weiß, ist die Verknüpfung von libstdc++der einzige Unterschied zwischen den beiden.
Johannes Schaub - litb
6

Ein kurzer Blick auf die libstd++Codebasis ergab die folgenden zwei Verwendungen von __gx_personality_v0:

In 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 *);

In 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 ...
}

(Hinweis: Es ist tatsächlich etwas komplizierter. Es gibt eine bedingte Kompilierung, die einige Details ändern kann.)

Solange Ihr Code keine Ausnahmebehandlung verwendet, hat das Definieren des Symbols void*keine Auswirkungen, aber sobald dies der Fall ist, stürzen Sie ab - __gxx_personality_v0ist eine Funktion, kein globales Objekt. Versuchen Sie es also Wenn Sie die Funktion aufrufen, springen Sie zur Adresse 0 und verursachen einen Segfault.

Adam Rosenfield
quelle
Nicht unbedingt auf 0 springen; Das Globale ist nicht initialisiert, also könnte es wirklich jeder Wert sein.
Strager
6
Strager, Globals werden mit Null initialisiert, wenn der Programmierer sie nicht initialisiert
Johannes Schaub - Litb
@litb: Dies ist nur wahr, wenn der Kernel das Nullsetzen des bss-Abschnitts implementiert :-P. Aber ja, sie sollten aus Gründen der Vernunft 0 initialisiert werden.
Evan Teran
9
@Evan Teran: Nein, eine konforme C-Implementierung initialisiert Globals immer auf 0. Siehe §5.1.2 und §6.7.8 Absatz 10 des C99-Standards.
Adam Rosenfield
6

Ich hatte diesen Fehler einmal und fand den Ursprung heraus:

Ich habe einen gcc-Compiler verwendet und meine Datei wurde aufgerufen, CLIENT.Cobwohl ich ein C-Programm und kein C ++ - Programm ausgeführt habe.

gcc erkennt die .CErweiterung als C ++ - Programm und die .cErweiterung als C-Programm (achten Sie auf das kleine c und das große C).

Also habe ich mein Dateiprogramm umbenannt CLIENT.cund es hat funktioniert.

jlguenego
quelle
2

Die obigen Antworten sind richtig: Sie werden bei der Ausnahmebehandlung verwendet. Das Handbuch für GCC Version 6 enthält weitere Informationen (die im Handbuch für Version 7 nicht mehr enthalten sind). Der Fehler kann auftreten, wenn eine externe Funktion verknüpft wird, die - GCC unbekannt - Java-Ausnahmen auslöst.

Sagitta
quelle