Do czego służy __gxx_personality_v0?

103

To pytanie z drugiej ręki z witryny programistycznej systemu operacyjnego, ale zaciekawiło mnie, ponieważ nigdzie nie mogłem znaleźć przyzwoitego wyjaśnienia.

Podczas kompilowania i linkowania wolnostojącego programu w C ++ przy użyciu gcc, czasami pojawia się taki błąd konsolidatora:

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

Jest to najwyraźniej spowodowane tym, że ten symbol jest zdefiniowany w libstdc ++, którego brakuje w wolnostojącym środowisku. Naprawienie problemu wymaga po prostu gdzieś zdefiniowania tego symbolu:

void *__gxx_personality_v0;

Co jest miłe, ale nie lubię rzeczy, które po prostu działają magicznie ... Więc pytanie brzmi, jaki jest cel tego symbolu?

Bruce Johnston
źródło

Odpowiedzi:

93

Jest używany w stosie rozwijania tabel, co można zobaczyć na przykład w wyniku montażu mojej odpowiedzi na inne pytanie . Jak wspomniano w tej odpowiedzi, jego użycie jest zdefiniowane przez Itanium C ++ ABI , gdzie nazywa się to procedurą osobowości .

Powodem, dla którego „działa” przez zdefiniowanie go jako globalnego wskaźnika void o wartości NULL, jest prawdopodobnie to, że nic nie zgłasza wyjątku. Kiedy coś próbuje zgłosić wyjątek, zobaczysz, że źle się zachowuje.

Oczywiście, jeśli nic nie używa wyjątków, możesz je wyłączyć za pomocą -fno-exceptions(a jeśli nic nie używa RTTI, możesz również dodać -fno-rtti). Jeśli ich używasz, musisz (jak zauważyły ​​już inne odpowiedzi) połączyć z g++zamiast gcc, co doda -lstdc++za Ciebie.

CesarB
źródło
2
Dzięki za wskazówkę -fno-exceptions. Dodałem CPPFLAGS += -fno-exceptionsdo mojego pliku makefile i to rozwiązało błąd.
Alan Kinnaman
12

To część obsługi wyjątków. Mechanizm gcc EH pozwala mieszać różne modele EH, a procedura osobowości jest wywoływana w celu określenia, czy wyjątek pasuje, jaką finalizację wywołać itp. Ta specyficzna procedura osobowości służy do obsługi wyjątków w C ++ (w przeciwieństwie do, powiedzmy, gcj / Java Obsługa wyjątków).

Martin przeciwko Löwis
źródło
11

Obsługa wyjątków jest zawarta w wolnostojących implementacjach.

Powodem tego jest to, że prawdopodobnie używasz gccdo kompilacji kodu. Jeśli kompilujesz z opcją -###, zauważysz, że brakuje opcji konsolidatora, -lstdc++gdy wywołuje ona proces konsolidatora. Kompilowanie z g++będzie zawierało tę bibliotekę, a tym samym symbole w niej zdefiniowane.

Johannes Schaub - litb
źródło
Zawsze myślałem, że kompilacja z g ++ jest potrzebna tylko wtedy, gdy specjalnie chciałeś powiedzieć kompilatorowi, że kod jest C ++ (na przykład - brak rozszerzenia). Teraz wydaje się, że kompilacja kodu C ++ za pomocą gcc pomija dołączenie bibliotek come. Oprócz braku niektórych bibliotek, czy są jakieś inne „skutki uboczne” kompilacji za file.cpppomocą gcczamiast g++?
Lazer
1
@eSkay, o ile wiem, libstdc++jedyną różnicą między nimi jest linkowanie .
Johannes Schaub - litb
6

Szybkie przeglądanie libstd++bazy kodu ujawniło następujące dwa zastosowania __gx_personality_v0:

W libsupc ++ / relax-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 *);

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

(Uwaga: w rzeczywistości jest to trochę bardziej skomplikowane; istnieje pewna kompilacja warunkowa, która może zmienić niektóre szczegóły).

Tak więc, o ile twój kod nie korzysta z obsługi wyjątków, zdefiniowanie symbolu jako void*nie wpłynie na nic, ale gdy tylko to zrobi, nastąpi awaria - __gxx_personality_v0to funkcja, a nie jakiś obiekt globalny, więc spróbuj wywołanie funkcji powoduje przejście do adresu 0 i spowodowanie segfault.

Adam Rosenfield
źródło
Niekoniecznie przeskoczyć do 0; globalny jest niezainicjalizowany, więc może to być naprawdę dowolna wartość.
strager
6
strager, wartości globalne są inicjalizowane przez zero, jeśli programista ich nie zainicjuje
Johannes Schaub - litb
@litb: jest to prawdą tylko wtedy, gdy jądro implementuje zerowanie sekcji bss :-P. Ale tak, ze względów normalnych powinny być inicjalizowane na 0.
Evan Teran
9
@Evan Teran: Nie, zgodna implementacja C zawsze inicjalizuje globals do 0. Patrz §5.1.2 i §6.7.8 paragraf 10 standardu C99.
Adam Rosenfield
6

Miałem raz ten błąd i znalazłem pochodzenie:

Używałem kompilatora GCC i mój plik został wywołany, CLIENT.Cmimo że robiłem program w C, a nie w C ++.

gcc rozpoznaje .Crozszerzenie jako program C ++, a .crozszerzenie jako program C (uważaj na małe c i duże C).

Więc zmieniłem nazwę mojego CLIENT.cprogramu plików i zadziałało.

jlguenego
źródło
2

Powyższe odpowiedzi są poprawne: jest używany w obsłudze wyjątków. Instrukcja dla GCC w wersji 6 ma więcej informacji (który nie jest już obecny jest w wersji 7 instrukcji). Błąd może wystąpić podczas łączenia funkcji zewnętrznej, która - nieznana GCC - zgłasza wyjątki Java.

sagitta
źródło