__gxx_personality_v0是用来做什么的?

113

这是一个来自操作系统开发网站的二手问题,但由于我无法在任何地方找到合适的解释,它引起了我的好奇心。

使用gcc编译和链接一个自由站C++程序时,有时会出现以下链接器错误:

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

这显然是因为该符号是在libstdc++中定义的,而在无操作系统环境中该库会缺失。解决问题只需要在某处定义该符号:

void *__gxx_personality_v0;

这很不错,但我不喜欢那些只是神奇地运作的东西...所以问题是,这个符号的目的是什么?


使用 msys2 msys 版本的 clang++,我尝试在 main.cpp 中定义这个符号,但它并没有解决问题 - 它导致了另一个错误:clang++: error: clang frontend command failed due to signal (use -v to see invocation) - AJM
6个回答

103

这个代码被用在堆栈展开表中,你可以在我的另一个回答的汇编输出中看到它。正如那个回答中提到的,它的使用由Itanium C++ ABI定义,其中它被称为Personality Routine

它之所以通过将其定义为全局NULL void指针来“工作”,可能是因为没有任何东西会抛出异常。当某些东西尝试抛出异常时,您将看到它的行为不当。

当然,如果没有任何东西使用异常,您可以使用-fno-exceptions禁用它们(如果没有任何东西使用RTTI,还可以添加-fno-rtti)。如果您正在使用它们,您必须(就像其他答案已经指出的那样)使用g++链接,而不是使用gcc,这将为您添加-lstdc ++


3
谢谢你提供有关“-fno-exceptions”的提示。我在我的makefile中添加了“CPPFLAGS += -fno-exceptions”,这解决了错误。 - Alan Kinnaman

13

这是异常处理的一部分。gcc的异常处理机制允许混合使用各种异常模型,并调用个性化例程来确定异常是否匹配,以及要调用哪些终止操作等。这个特定的个性化例程用于C++异常处理(而不是比如gcj/Java的异常处理)。


13

异常处理已包含在独立的实现中。

这是因为您可能使用 gcc 编译您的代码。如果您使用选项 -### 进行编译,您会注意到当它调用链接器进程时缺少链接器选项 -lstdc++。如果使用 g++ 进行编译,则会包含该库,从而包含其中定义的符号。


我一直认为只有在需要告诉编译器代码是C++时(例如-缺少扩展名)才需要使用g++进行编译。现在似乎使用gcc编译C++代码会漏掉一些库的包含。除了缺少一些库之外,如果我使用gcc而不是g++来编译我的file.cpp,是否还有其他“副作用”? - Lazer
1
据我所知,libstdc++ 的链接是两者之间唯一的区别。 - Johannes Schaub - litb

7
一个快速的grep搜索libstd++代码库,发现以下两个使用__gxx_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并导致段错误。


1
不一定跳到0;全局变量未初始化,因此它可以是任何值。 - strager
6
如果程序员没有对全局变量进行初始化,它们将被零初始化。"strager"这个词是错误的拼写,可能应该是 "stranger" 的拼写错误,但我无法确定它是否与上下文相关。 - Johannes Schaub - litb
@litb:只有内核实现清零BSS段才是真的:-P。但是,为了保持清醒,它们应该被初始化为0。 - Evan Teran
11
@Evan Teran:不,符合C标准的实现会始终将全局变量初始化为0。请参阅C99标准的§5.1.2和§6.7.8第10段。 - Adam Rosenfield
你有代码库的Github/SourceForge链接吗? - AJM

6

我曾经遇到过这个错误,后来发现了原因:

我使用的是gcc编译器,我的文件名叫作CLIENT.C,尽管我正在进行的是C程序而不是C++程序。

gcc将.C扩展名视为C++程序,而将.c扩展名视为C程序(务必区分大小写)。

因此,我将我的文件重命名为CLIENT.c,程序就可以正常运行了。


2
上面的答案是正确的:它用于异常处理。GCC 6版本的手册提供了更多信息(在版本7手册中不再存在)。当链接一个未知于GCC的抛出Java异常的外部函数时,可能会出现错误。请注意保留HTML标签。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接