GCC 优化导致运行时出现“未定义符号”错误

6

我现在有一个相当令我困惑的问题:我有一段用C++编写的软件,它链接到一个用C编写的库。我使用通常的方法包含头文件类:

extern "C" {
    #include <libheader.h>
}

只要我不使用gcc的优化,一切都正常。但一旦我开启-O1(即第一个优化级别),在运行时会出现“未定义符号”错误,该符号来自此库。但是,由于extern "C",名称已经进行了名称重整,因此应禁用它。
调用相关符号的函数是内联的,如果这很重要。所使用的编译器是gcc 4.4.3。
老实说,我甚至不知道要搜索什么,所以如果你们中的任何人能给我一些原因,我会非常感激。
感谢您的支持。

你能否移除内联(即使函数不再内联)并查看它是否开始工作? - sashoalm
1
另外,您可以将内联函数调用作为C++函数进行封装,然后查看是否可以使其开始工作,这是@satuon的建议的替代方案。 - Andrew Aylett
谢谢你的回复!我尝试了两种建议,但是这个符号仍然找不到。 - Thilo
1
在运行时???在编译语言中如何会出现“未定义符号”错误呢?你是否在使用某种动态库? - AnT stands with Russia
@AndreyT 我认为这是包含另一个共享库的共享库的常见行为。我遇到过这个问题不止一次(尽管原因不同)。 - Thilo
4个回答

4
您说您在extern "C"块中包含了libheader.h文件,但链接器正在寻找的符号已被名称混淆。这表明libheader.h也在extern "C"块之外被包含(由于libheader.h中的包含保护,extern "C"块内的包含可能是一个nop)。请查找其他可能导致libheader.h被包含的方式。GCC的-E和/或各种-M选项可能会有所帮助。或者(仅供测试)将extern "C"块移动到libheader.h内部:
// at start of libheader.h:
#ifdef __cplusplus
extern "C" {
#endif

/* existing contents of libheader.h */
// ...

// at end of libheader.h:
#ifdef __cplusplus
}
#endif

请注意,链接规范可以嵌套,因此您不必在#include位置删除现有的extern "C"块。
我不知道为什么问题只会发生在优化构建中,除非可能包含调用函数的非内联版本的.c或.cpp文件获得正确的头文件,并且只需要一个翻译单元获得错误的头文件并内联调用函数才能看到问题。

谢谢!如果那是问题的原因,这可能是一个好主意来追踪问题。不幸的是,我只有下周五才有时间研究它,届时会回报。 - Thilo

3
如果您有一个在未内联时定义但在内联时未定义的函数,则问题应该很容易定义。
您在某个地方使用了该函数,但没有包含其头文件。
检查所有调用此函数的文件,并确保已包含头文件。

1

调用内联函数的头文件是否可以包含库的头文件,而不需要使用 extern "C" 的包装方式,并且其他所有地方都使用了该包装方式呢?

你尝试过其他级别(如 -O2)吗?

你尝试过取消内联函数的优化吗?


谢谢你提供的想法。是的,正确的文件头部分确实使用了“extern"C"”包装器。-O2和-O3具有相同的效果。如上所述,去掉inline也没有帮助。 - Thilo
@Thilo 特别是,您删除了内联并将函数从头文件移动到源文件中,以便编译器无法自动内联它? - Mark B
啊...感谢您的澄清。之前忘记将函数移动到源文件中。这解决了问题。然而,将函数内联可能会显著提高速度,所以结果并不是特别优雅。 - Thilo

0

使用 extern "C" {...} 包装 C 头文件并不总是有效的;通常,头文件需要被设计成能够与两种语言一起工作才能实现这个目的。

在这种情况下,如果没有更多细节,很难确定到底发生了什么,但是在 C 和 C++ 中,inline 关键字的含义是不同的;我不会期望一个带有 inline 函数的头文件能够以这种方式工作。(顺便说一句,我不会期望你遇到的症状,但它们也不会让我特别惊讶。)

正确的处理方法是要求库的供应商提供一个专门为两种语言设计的头文件。

如果无法做到这一点,正确的处理方法是编写自己的 C 代码,其中包括头文件并包装所有需要的函数,并编写自己的头文件,该头文件旨在从两种语言中包含。这是一项艰巨的工作;最好让供应商完成他的工作,而不是让你为他完成一半的工作。


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