无声符号碰撞?

Silent symbol collision?

之前在 Raspberry Stackexchange 上发表过,因题外话而关闭,所以我来了。

下面的程序可以用 gcc main.c -lpigpio -lpthread -Wall -Wextra 干净地编译。我正在使用 gcc 8.3.0(交叉编译器或普通编译器不会改变任何东西)。

// main.c
#include <stdio.h>
#include <pigpio.h>

void listen() {
        printf("hi from listen\n");
}

int main() {
        gpioInitialise();
        printf("hi from main\n");
        gpioTerminate();
        return 0;
}

没有警告,但程序愉快地打印

hi from listen
hi from main

原因似乎是 gpioInitialise() 试图从 <sys/sockets.h> here 调用 listen(2),而是从 main.c 调用函数。当然,这是行不通的,会导致 pigpio 库出现错误消息:

xxxx-xx-xx xx:xx:xx pthSocketThread: setsockopt() fail, closing socket -1
xxxx-xx-xx xx:xx:xx pthSocketThread: accept failed (Bad file descriptor)

问题:为什么 gcc 链接一个具有完全不同签名的函数而不抛出任何错误?我知道 static,但我想理解这一点以从中学习。肯定不能像 listen(2) 不声明为静态那么简单?

链接器允许您插入这样的符号以用于调试、分析等目的。不幸的是,在 link 阶段,输入信息通常不可用,因此 linker 无法执行任何健全性检查来验证预期签名和实际签名是否匹配。有人可能会争辩说 -g 这是可能的,但没有人足够关心实现它(尽管这可能是一个很好的家庭项目)。

Static 会对您的情况有所帮助,因为它会阻止 listen 被导出和 libc 的 listen 被覆盖。

作为旁注,在 ELF 系统 (Linux) 上情况更糟,因为它们允许运行时符号插入(即库在程序启动时竞争谁赢得符号名称)。这通常用 static-fvisiblity=hidden 解决(不幸的是很多库 do not use them)。