让GCC链接器警告多个函数定义

4

考虑一下我的小型示例C库:

#include <external_library.h>

void some_function(void)
{
    external_library_call();
    // Do other stuff...
}

它计划使some_function()成为公共可调用的函数。然而,该库无法正常工作,因为它需要使用一个名为some_function()的函数的外部库,这个函数恰好有相同的原型。然而,GCC的链接器并不关心some_function符号有多少源头。它似乎随机选择一个,并且外部库可能会使用我的some_function()而不是它自己的函数。这很疯狂。我指的不是库不能正常工作的事实。这个库肯定不能正常工作。问题在于有两个源头来定义'some_function'符号,但是链接器对此没有任何处理。你知道,这并不太让我困扰,因为我已经习惯了GCC和C语言默认情况下的病态鲁莽行为。然而,一定有一种方法可以让链接器在出现相同符号的两个源头时警告我。我已经尝试过-Wall -Wextra -Wshadow,但是没有产生任何警告。请注意,-fvisibility=hidden在这里没有帮助,因为两个库都想要导出some_function()。我知道你可以说我不应该在没有唯一前缀的情况下进行函数调用。你是对的。这是一个错误。但我并不关心这个错误。这个错误是可以被链接器捕捉到的,所以应该被捕捉。没有理由链接器不应该捕捉到这个错误。另外,你使用的库可能会导出一些奇怪和意外的符号,而你并不一定控制别人的库导出什么。此外,在程序员停滞并着火之前,前缀只能做得如此独特。
2个回答

1

添加:

-fvisibility=hidden

到您的构建标志。请注意一些注意事项,有些头文件可能不会预期到这一点。在这些情况下,在包含它们之前需要使用一个 pragma:

#pragma GCC visibility push(hidden)
#include <problematic_header>
#pragma GCC visibility pop

这样做除了避免符号冲突之外,还有其他一些好处。请参见:

http://gcc.gnu.org/wiki/Visibility

如果您感兴趣的话。


如果我理解正确,我还必须在 main() 前面添加 attribute((visibility("default"))) 以导出它。是这样吗? - enigmaticPhysicist
@enigmaticPhysicist 我不这么认为。GCC似乎会特殊处理main()函数并始终导出它。 - Nikos C.
虽然这非常有帮助,我肯定会使用它,但它并没有解决原始问题。链接器/knew/存在两个相同符号的不同实例。为什么它没有停止并提供错误?为什么它选择了一个而不是另一个?标准是什么?如果我需要导出一个符号,并且我想确保它不与我正在使用的任何其他符号发生冲突怎么办?在这种情况下,除非我能让链接器在错误条件下停止,否则我仍然认为这个问题没有得到解决。 - enigmaticPhysicist
@enigmaticPhysicist 在你篡改并接管问题之前,这已经回答了这个问题。现在它问的是另外一个问题。你应该发表一个新的问题。 - Nikos C.
它目前正在询问我一直想问的问题。你一开始误解了我,因为我第一次写得不清楚。我认为我会知道我的意思,因为我首先提出了这个问题。这不是劫持。 - enigmaticPhysicist
@enigmaticPhysicist 嗯,因为这个问题已经几年了,所以几乎没有人会看到你编辑后的问题。对于应用程序使用情况,答案是正确的,但对于库使用情况则不然。因此,如果您想有很好的机会得到新问题的答案,您应该将此问题恢复到原始状态,并为您的问题创建一个新问题。 - Nikos C.

0

-Wall -Wextra -Wshadow这样的选项是编译器标志,会影响源代码的分析;显然,您需要一个链接器选项。

但是,由于shutdown()在保留给应用程序使用的命名空间中(而且您甚至没有显示已经包含声明系统shutdown()的头文件),因此编译器或链接器无法做太多事情。作为应用程序员,您有权创建一个名为shutdown()的函数并调用它。您可以查看类似LSB(Linux标准库)兼容性套件之类的东西,以检查您是否定义了实现已定义的函数。

我曾经追踪过的最难的错误之一涉及我们代码中的一个名为_bind()的函数,它碰巧与具有不同接口的系统函数_bind()具有相同的名称。当系统库的某些部分调用我们的函数时,一切都变得混乱不堪。这在一定程度上是我们使用保留给系统的名称的错误(不要以_开头的变量或函数名称),不幸的是只有一个平台存在名称冲突。对我们的_bind()进行系统前缀解决了这个问题。

你可以查看C和POSIX标准中保留名称的列表。但是,你会发现_t后缀是保留的,但带有该后缀的类型名称也被广泛(滥)用于用户定义的类型。你还需要知道任何给定类型的定义是“来自系统头文件”(可能可以接受),还是来自用户定义的头文件(可能不可以接受),或者是“提供缺失系统功能的用户定义头文件”(边缘情况)。这变得非常棘手。

关于_bind()的错误:我认为这完全不是你的错。在某个时候,链接器必须意识到有两个_bind()的定义。没有办法链接器不知道这一点。除非有一些我不熟悉的链接器操作的琐碎细节... - enigmaticPhysicist
其实,是不是编译时的链接器从来没有检查过它正在链接的共享对象的符号表呢?这是我能想到的唯一解释。即便如此,这仍然有点鲁莽。 - enigmaticPhysicist

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