这个C程序如何编译和运行两个主要function?
今天,在一个定制库的同时,我发现了一个奇怪的行为。 一个静态库代码包含一个debuggingmain()
函数。 它不在#define
标志内。 所以它也存在于图书馆里。 它被用来链接到另一个包含真正main()
。
当它们都链接在一起时,链接器没有为main()
抛出多重声明错误。 我想知道这是怎么发生的。
为了简单起见,我创build了一个模拟相同行为的示例程序:
$ cat prog.c #include <stdio.h> int main() { printf("Main in prog.c\n"); } $ cat static.c #include <stdio.h> int main() { printf("Main in static.c\n"); } $ gcc -c static.c $ ar rcs libstatic.a static.o $ gcc prog.c -L. -lstatic -o 2main $ gcc -L. -lstatic -o 1main $ ./2main Main in prog.c $ ./1main Main in static.c
“2main”二进制文件如何find执行哪个main
?
但将它们编译在一起会导致多重声明错误:
$ gcc prog.c static.o static.o: In function `main': static.c:(.text+0x0): multiple definition of `main' /tmp/ccrFqgkh.o:prog.c:(.text+0x0): first defined here collect2: ld returned 1 exit status
任何人都可以解释这种行为?
引用ld(1):
链接器将仅在命令行中指定的位置search存档。 如果存档定义了一个符号,该符号在命令行上存档之前出现的某个对象中未定义,则链接器将从存档中包含相应的文件。
当连接2main时,主符号在ld达到-lstatic之前得到解决,因为ld从prog.o中获取它。
当连接1main时,在获得-lstatic的时候,你确实拥有未定义的main,所以它searchmain的存档。
这个逻辑只适用于档案(静态库),而不是常规的对象。 当你链接prog.o和static.o时,两个对象的所有符号都被无条件包含,所以你得到一个重复的定义错误。
链接一个静态库(.a)时,链接器只search存档,如果有迄今为止跟踪的任何未定义的符号。 否则,它根本没有看档案。 所以你的第二个案例,它从来没有2main
档案,因为它没有任何未定义的符号来制作翻译单元。
如果你在static.c
包含一个简单的函数:
#include <stdio.h> void fun() { printf("This is fun\n"); } int main() { printf("Main in static.c\n"); }
并从prog.c
调用它,然后链接器将被迫查看存档,以find符号的fun
,你会得到相同的多重主定义错误,因为链接器现在会find重复的符号main
。
当你直接编译目标文件(如gcc ao bo
)时,链接器在这里没有任何作用,所有的符号都包含在内,以形成一个单一的二进制文件,而且显然有重复的符号。
底线是链接器只有在缺less符号时才查看存档。 否则,就像没有链接任何库一样好。
链接器加载任何目标文件后,它search库中未定义的符号 。 如果没有,则不需要读取库。 既然main已经被定义了,即使它在每个库中都find了一个main,也没有理由加载第二个。
然而,连接器具有显着不同的行为。 例如,如果你的库包含了一个main()和foo()这两个对象文件 ,而且foo是未定义的,那么你很可能会得到一个多重定义的符号main()的错误。
现代(重复)连接器将省略无法访问的对象的全局符号 – 例如AIX。 老式连接器就像Solaris上的连接器一样,而Linux系统仍然像70年代的unix连接器那样工作,从对象模块中加载所有的符号,可以访问或不可访问。 这可能是一个可怕的膨胀的来源,以及过多的链接时间。
* nix链接器的特点在于,每次列出它们时,它们只能有效地search一次库。 这就要求程序员除了编写程序之外,还要命令行上的库链接器或make文件。 不要求有序的库列表是不现代的。 较老的操作系统通常有连接器,会反复search所有库,直到通过未能parsing符号。