Для чего нужен __gxx_personality_v0?

103

Это вопрос из вторых рук с сайта разработки ОС, но он вызвал у меня любопытство, так как я нигде не мог найти достойного объяснения.

При компиляции и компоновке автономной программы на C ++ с использованием gcc иногда возникает такая ошибка компоновщика:

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

Очевидно, это связано с тем, что этот символ определен в libstdc ++, который отсутствует в отдельно стоящей среде. Для решения проблемы просто необходимо где-нибудь определить этот символ:

void *__gxx_personality_v0;

Что приятно, но мне не нравятся вещи, которые просто волшебно работают ... Итак, вопрос в том, какова цель этого символа?

Брюс Джонстон
источник

Ответы:

93

Он используется в таблицах разворачивающихся стеков, которые вы можете увидеть, например, в выводе сборки моего ответа на другой вопрос . Как упоминалось в этом ответе, его использование определяется интерфейсом Itanium C ++ ABI , где оно называется индивидуальной программой .

Причина, по которой он «работает», определяя его как глобальный указатель NULL void, вероятно, заключается в том, что ничто не генерирует исключение. Когда что-то пытается вызвать исключение, вы увидите, что оно ведет себя неправильно.

Конечно, если ничто не использует исключения, вы можете отключить их с помощью -fno-exceptions(и если ничто не использует RTTI, вы также можете добавить -fno-rtti). Если вы их используете, вам нужно (как уже отмечалось в других ответах) ссылаться g++вместо gcc, что добавит -lstdc++для вас.

CesarB
источник
2
Спасибо за подсказку -fno-exceptions. Я добавил CPPFLAGS += -fno-exceptionsв свой make-файл, и это устранило ошибку.
Алан Киннаман
12

Это часть обработки исключений. Механизм gcc EH позволяет смешивать различные модели EH, и вызывается подпрограмма индивидуальности, чтобы определить, соответствует ли исключение, какую финализацию вызывать и т. Д. Эта конкретная подпрограмма индивидуальности предназначена для обработки исключений C ++ (в отличие, скажем, от gcj / Java Обработка исключений).

Мартин против Лёвиса
источник
11

Обработка исключений включена в отдельные реализации.

Причина этого в том, что вы, возможно, используете его gccдля компиляции кода. Если вы компилируете с этой опцией, -###вы заметите, что ей не хватает опции компоновщика, -lstdc++когда он вызывает процесс компоновщика. Компиляция с помощью g++включает эту библиотеку и, следовательно, определенные в ней символы.

Йоханнес Шауб - litb
источник
Я всегда думал, что компиляция с g ++ нужна только тогда, когда вы специально хотите сообщить компилятору, что это код C ++ (например, с отсутствующим расширением). Теперь кажется, что при компиляции кода C ++ с помощью gcc не включаются входящие библиотеки. Помимо отсутствия некоторых библиотек, есть ли другие «побочные эффекты» компиляции my file.cppwith gccвместо g++?
Lazer
1
@eSkay, насколько мне известно, libstdc++единственное различие между ними - это связывание .
Йоханнес Шауб - лит
6

Быстрый просмотр libstd++базы кода с помощью команды grep показал следующие два использования __gx_personality_v0:

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

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

(Примечание: на самом деле это немного сложнее; есть некоторая условная компиляция, которая может изменить некоторые детали).

Итак, пока ваш код на самом деле не использует обработку исключений, определение символа как void*ни на что не влияет, но как только это произойдет, вы столкнетесь с ошибкой - __gxx_personality_v0это функция, а не какой-то глобальный объект, поэтому попробуйте для вызова функции произойдет переход к адресу 0 и вызовет segfault.

Адам Розенфилд
источник
Необязательно переходить к 0; глобальный объект не инициализирован, поэтому на самом деле это может быть любое значение.
Strager
6
strager, глобальные переменные инициализируются нулем, если программист не инициализирует их
Йоханнес Шауб - litb
@litb: это верно, только если ядро ​​реализует обнуление секции bss :-P. Но да, для здравомыслия они должны быть инициализированы 0.
Эван Теран,
9
@Evan Teran: Нет, соответствующая реализация C всегда инициализирует глобальные переменные значением 0. См. §5.1.2 и §6.7.8 параграф 10 стандарта C99.
Адам Розенфилд,
6

Однажды у меня была эта ошибка, и я выяснил ее происхождение:

Я использовал компилятор gcc, и мой файл был вызван, CLIENT.Cнесмотря на то, что я делал программу C, а не программу C ++.

gcc распознает .Cрасширение как программу C ++, а .cрасширение как программу C (будьте осторожны с маленьким c и большим C).

Я переименовал свою файловую CLIENT.cпрограмму, и она сработала.

jlguenego
источник
2

Приведенные выше ответы верны: он используется при обработке исключений. В руководстве для GCC версии 6 содержится дополнительная информация (которой больше нет в руководстве версии 7). Ошибка может возникнуть при связывании внешней функции, которая - неизвестно GCC - выдает исключения Java.

сагитта
источник