"Yongwei Wu" 的分析很有道理,这个问题很可能是弱符号 __attribute__((weak))
所引发。其实就算不是弱符号,在不同的可执行文件里也可以存在重复的符号,而
链接器不会报错。在同一个可执行文件(比如 bin 文件或者 .so 共享库)里不能
有重复的符号,否则链接的时候会报错;但如果重复的符号在不同的可执行文件里,
比如一个在 bin 里,另一个在 .so 里,那么就按照链接顺序找到所需要的符号,
重复的定义被忽略掉,不报错。
附上一个简单的示例来演示这种微妙的问题。
$ cat common.h
void fun1();
void fun2();
void fun3();
$ cat main.c
#include <unistd.h>
#include "common.h"
int main()
{
fun1();
fun2();
sleep(3);
return 0;
}
$ cat fun1.c
#include "common.h"
void fun1()
{
}
$ cat fun2.c
#include "common.h"
void fun2()
{
}
$ cat fun3.c
#include <stdio.h>
#include "common.h"
void fun3()
{
}
unsigned int sleep (unsigned int seconds)
{
printf("Sleep by myself!\n");
}
main 里调用了 sleep, 本来那是 glibc 里的函数。但在 fun3.c 里将它改成
自己的函数。那么编译链接:
$ gcc -c main.c -o main.o
$ gcc -c fun1.c -o fun1.o
$ gcc -c fun2.c -o fun2.o
$ gcc -c fun3.c -o fun3.o
$ gcc main.o fun1.o fun2.o fun3.o -o example # 不会报错
$ ./example
Sleep by myself!
$
如果将最后的链接改成:
$ gcc main.o fun1.o fun2.o -o example # 也不会报错
$ ./example # 简单地 sleep 三秒钟
$
很明显,当链接上 fun3.o 的时候,就找到了“自己的” sleep; 如果不链接
上,就到之后的 .so 文件(即隐含链接的 glibc)里去找,刚好也能找到。两种
情况都能运行,但逻辑明显不一样。