我注意到gtest提供了一种再次链接gtest_main
的方法,因此最终用户不需要编写自己的main
函数。这可以通过以下方式工作。 (名为hello.cpp
的小示例文件)
#include <gtest/gtest.h>
TEST(Hello, Basic) {}
可以使用以下命令进行编译:
g++ hello.cpp -lgtest -lgtest_main
,一切正常。这样做的原因是gtest_main.cc中定义了一个main
函数,从中生成了libgtest_main.a
。
现在这是东西。如果我将hello.cpp
更改为
#include <gtest/gtest.h>
TEST(Hello, Basic) {}
int main(int argc, char** argv) {
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
所有内容仍可在同一命令行中使用!现在有两个main
符号,链接器方便地选择了我在hello.cpp
中定义的一个主要功能。
这是怎么回事?
答案 0 :(得分:2)
没有魔术在继续。您观察到的是的正常默认行为 链接器。
静态库libxy.a
是以下内容的ar
archive
目标文件x.o
,y.o
,...
如果目标文件x.o
出现在程序的链接器输入中,则链接器将其链接
进入程序无条件。
如果静态库libxy.a
出现在链接器输入中,则链接器将检查
归档以查找提供具有以下特征的符号的定义的任何目标文件:
已被链接到的文件中已被引用但尚未定义
该程序。它仅从存档和链接中提取那些目标文件(如果有)
them 进入程序,就像它们被分别命名为链接器输入一样
而且根本没有提到静态库。
通常的原因是我们向静态库中的链接器提供了一组目标文件, 而不是作为单独的输入,所以 链接器将仅选择那些 需要,而不是简单地获取未解析符号引用的定义 不管是否需要,都将它们链接到程序中。
这是C 1 中的基本插图:-
main.c
extern void x(void);
int main(void)
{
x();
return 0;
}
lib_main.c
extern void y(void);
int main(void)
{
y();
return 0;
}
x.c
#include <stdio.h>
void x(void)
{
puts(__func__);
}
y.c
#include <stdio.h>
void y(void)
{
puts(__func__);
}
将所有内容编译为目标文件:
$ gcc -Wall -c main.c lib_main.c x.c y.c
制作一个包含lib_main.o
,x.o
和y.o
的静态库:
$ ar rcs libmxy.a lib_main.o x.o y.o
像这样链接程序prog
:
$ gcc -o prog main.o libmxy.a
运行方式:
$ ./prog
x
因此,main
提供的main.o
的定义已链接,而另一个
main
中libmxy.a(lib_main.o)
的定义被忽略。重复链接
带有一些诊断信息的照明系统。
$ gcc -o prog main.o libmxy.a -Wl,-trace,-trace-symbol=main,-trace-symbol=x
/usr/bin/ld: mode elf_x86_64
/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/Scrt1.o
/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/crti.o
/usr/lib/gcc/x86_64-linux-gnu/7/crtbeginS.o
main.o
(libmxy.a)x.o
libgcc_s.so.1 (/usr/lib/gcc/x86_64-linux-gnu/7/libgcc_s.so.1)
/lib/x86_64-linux-gnu/libc.so.6
(/usr/lib/x86_64-linux-gnu/libc_nonshared.a)elf-init.oS
/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
libgcc_s.so.1 (/usr/lib/gcc/x86_64-linux-gnu/7/libgcc_s.so.1)
/usr/lib/gcc/x86_64-linux-gnu/7/crtendS.o
/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/crtn.o
/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/Scrt1.o: reference to main
main.o: definition of main
main.o: reference to x
libmxy.a(x.o): definition of x
-trace
选项要求链接器向我们显示实际在哪些文件中使用了哪些文件。
链接。 -trace-symbol=name
要求链接器向我们显示其中的文件
符号name
已定义或引用。链接的大多数文件都是样板
gcc
默认添加到链接器命令行中。 我们构建的是:
main.o
(libmxy.a)x.o
链接器发现符号main
首先在样板对象中被引用
文件/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/Scrt1.o
。然后
它在对象文件main
中找到了main.o
的定义,该定义已链接
无条件的。解决了main
。链接器未在libmxy.a
中搜索
main
的另一种定义,因为它不需要一个。
在main.o
中,发现对x
的未定义引用以及下一个链接器输入
是libmxy.a
。因此,它将目标档案中的目标文件保留了一个
定义x
。它找到了libmxy.a(x.o)
并提取并链接了它。那是
完成。
我们将提供给libmxy.a
中的链接器的其他目标文件:
libmxy.a(lib_main.o)
libmxy.a(y.o)
不需要。他们可能还不存在。链接是完全 与
相同$ gcc -o prog main.o x.o
$ ./prog
x
有关libgtest_main.a
...
...事实上,这里有一个静态库,其中包含一个将链接的成员(libgtest_main.a(gtest_main.cc.o)
)
即使您的链接之前未输入任何对象文件
libgtest_main.a
:
$ g++ -o prog -lgtest_main -pthread
成功链接,prog
会运行,只是说它无关紧要。
如果-lgtest_main
是第一个链接器输入,则链接器认为
它,它不可能在已经链接的文件中发现任何未定义的引用,
由于没有,因此不需要链接其中的任何目标文件
libgtest_main.a
。但是确实如此,这种行为可能被描述为
魔术。
但是我们已经在以下诊断输出中看到了解释:
$ gcc -o prog main.o libmxy.a -Wl,-trace,-trace-symbol=main,-trace-symbol=x
它告诉我们main
中首先引用了/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/Scrt1.o
。
该样板对象文件是GCC C运行时启动代码,该代码执行程序的标准初始化
执行并通过调用main
完成。这是一个目标文件,因此将被链接
GCC会无条件地将其放置在生成的链接器命令行中的所有其他输入之前 之前。详细链接
模式(gcc -v ...
)来查看。因此,实际上,总是有一个 对象文件,首先在程序的链接中,
无论您显式链接了哪些对象文件,都引用了main
。如果你
在输入库之前,请勿自己输入定义main
的对象文件,然后
链接器将搜索库中的main
定义。 libgtest_main
利用了这一事实。
当然,只有针对googletest利用这一事实是可行的
链接googletest的程序,main
的定义为相同。