链接两个符号。一个在存档文件中定义
linking with two symbols. one defined in an archive file
我注意到 gtest 提供了一种再次 link 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
符号,linker 方便地选择了我在 hello.cpp
中定义的一个主要功能。
这里发生了什么魔法?
没有魔法发生。您观察到的是正常的默认行为
linker.
静态库 libxy.a
是 ar
archive
对象文件 x.o
、y.o
、...
如果目标文件 x.o
出现在程序的 linker 输入中,linker links 它
进入程序无条件.
如果静态库 libxy.a
出现在 linker 输入中,linker 检查
archive 以查找为具有以下符号的符号提供定义的任何目标文件
已在已 link 编辑到的文件中引用但尚未定义
该程序。它只从存档和 links 中提取那些目标文件(如果有的话)
他们进入程序就像他们被单独命名为linker输入一样
根本没有提到静态库。
我们在静态库中向 linker 提供一组目标文件的通常原因,
这样 linker 将select
需要获取未解析符号引用的定义,而不是简单地
link无论是否需要,都将它们全部添加到程序中。
这是 C1 中的基本说明:-
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
Link 一个程序 prog
像这样:
$ gcc -o prog main.o libmxy.a
它运行就像:
$ ./prog
x
所以 main.o
提供的 main
的定义被 linked 和另一个
libmxy.a(lib_main.o)
中 main
的定义被忽略。重复 link 年龄
通过一些诊断可以阐明更多信息。
$ 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
选项要求 linker 向我们展示实际使用了哪些文件
link年龄。 -trace-symbol=name
要求 linker 向我们展示其中的文件
已定义或引用符号 name
。大多数文件 linked 都是样板文件
gcc
默认添加到 linker 命令行。 我们建造的是:
main.o
(libmxy.a)x.o
linker 找到了样板对象中首次引用的符号 main
文件 /usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/Scrt1.o
。然后
它在目标文件 main.o
中找到了 main
的定义,该文件被 linked
无条件地。这解决了 main
。 linker 没有搜索 libmxy.a
main
的另一个定义,因为它不需要。
在 main.o
它找到了对 x
和下一个 linker 输入的未定义引用
是 libmxy.a
。所以它在那个档案中搜索目标文件,寻找一个
定义 x
。它找到 libmxy.a(x.o)
并提取并 link 编辑它。然后是
完成。
我们提供给libmxy.a
中的linker的其他目标文件:
libmxy.a(lib_main.o)
libmxy.a(y.o)
不需要。它们还不如不存在。 link年龄正好
等同于:
$ gcc -o prog main.o x.o
$ ./prog
x
libgtest_main.a
有什么比较有趣的...
... 是事实,这里您有一个静态库,其中包含一个成员 (libgtest_main.a(gtest_main.cc.o)
), 将被 linked
进入你的程序,即使你的 linkage 之前没有输入 any 目标文件
libgtest_main.a
:
$ g++ -o prog -lgtest_main -pthread
links成功了,和prog
会运行只是说没关系。
如果 -lgtest_main
是第一个 linker 输入,那么当 linker 考虑
它,它不可能在已经 linked 的文件中发现任何未定义的引用,
因为有 none,因此不需要 link 内的任何目标文件
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
结束。这是一个 目标文件 ,因此它将被 linked
无条件地,GCC 将其放置在 之前 生成的 linker 命令行中的所有其他输入。 Link 详细
模式 (gcc -v ...
) 来查看。所以实际上总是一个目标文件,首先在程序的linkage,
引用 main
,无论您明确 link 是什么目标文件。如果你
在输入库之前不要自己输入定义 main
的目标文件,然后
linker 将 在库中搜索 main
的定义。 libgtest_main
利用了这一事实。
当然,只有在 googletest 中利用这个事实才是实际的,因为对于所有正常
link googletest 的程序,main
的定义相同。
[1] 选择 C 而不是 C++ 没有区别,除了在 C 中我们
不必为名称修改而烦恼。
我注意到 gtest 提供了一种再次 link 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
符号,linker 方便地选择了我在 hello.cpp
中定义的一个主要功能。
这里发生了什么魔法?
没有魔法发生。您观察到的是正常的默认行为 linker.
静态库 libxy.a
是 ar
archive
对象文件 x.o
、y.o
、...
如果目标文件 x.o
出现在程序的 linker 输入中,linker links 它
进入程序无条件.
如果静态库 libxy.a
出现在 linker 输入中,linker 检查
archive 以查找为具有以下符号的符号提供定义的任何目标文件
已在已 link 编辑到的文件中引用但尚未定义
该程序。它只从存档和 links 中提取那些目标文件(如果有的话)
他们进入程序就像他们被单独命名为linker输入一样
根本没有提到静态库。
我们在静态库中向 linker 提供一组目标文件的通常原因, 这样 linker 将select 需要获取未解析符号引用的定义,而不是简单地 link无论是否需要,都将它们全部添加到程序中。
这是 C1 中的基本说明:-
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
Link 一个程序 prog
像这样:
$ gcc -o prog main.o libmxy.a
它运行就像:
$ ./prog
x
所以 main.o
提供的 main
的定义被 linked 和另一个
libmxy.a(lib_main.o)
中 main
的定义被忽略。重复 link 年龄
通过一些诊断可以阐明更多信息。
$ 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
选项要求 linker 向我们展示实际使用了哪些文件
link年龄。 -trace-symbol=name
要求 linker 向我们展示其中的文件
已定义或引用符号 name
。大多数文件 linked 都是样板文件
gcc
默认添加到 linker 命令行。 我们建造的是:
main.o
(libmxy.a)x.o
linker 找到了样板对象中首次引用的符号 main
文件 /usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/Scrt1.o
。然后
它在目标文件 main.o
中找到了 main
的定义,该文件被 linked
无条件地。这解决了 main
。 linker 没有搜索 libmxy.a
main
的另一个定义,因为它不需要。
在 main.o
它找到了对 x
和下一个 linker 输入的未定义引用
是 libmxy.a
。所以它在那个档案中搜索目标文件,寻找一个
定义 x
。它找到 libmxy.a(x.o)
并提取并 link 编辑它。然后是
完成。
我们提供给libmxy.a
中的linker的其他目标文件:
libmxy.a(lib_main.o)
libmxy.a(y.o)
不需要。它们还不如不存在。 link年龄正好 等同于:
$ gcc -o prog main.o x.o
$ ./prog
x
libgtest_main.a
有什么比较有趣的...
... 是事实,这里您有一个静态库,其中包含一个成员 (libgtest_main.a(gtest_main.cc.o)
), 将被 linked
进入你的程序,即使你的 linkage 之前没有输入 any 目标文件
libgtest_main.a
:
$ g++ -o prog -lgtest_main -pthread
links成功了,和prog
会运行只是说没关系。
如果 -lgtest_main
是第一个 linker 输入,那么当 linker 考虑
它,它不可能在已经 linked 的文件中发现任何未定义的引用,
因为有 none,因此不需要 link 内的任何目标文件
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
结束。这是一个 目标文件 ,因此它将被 linked
无条件地,GCC 将其放置在 之前 生成的 linker 命令行中的所有其他输入。 Link 详细
模式 (gcc -v ...
) 来查看。所以实际上总是一个目标文件,首先在程序的linkage,
引用 main
,无论您明确 link 是什么目标文件。如果你
在输入库之前不要自己输入定义 main
的目标文件,然后
linker 将 在库中搜索 main
的定义。 libgtest_main
利用了这一事实。
当然,只有在 googletest 中利用这个事实才是实际的,因为对于所有正常
link googletest 的程序,main
的定义相同。
[1] 选择 C 而不是 C++ 没有区别,除了在 C 中我们 不必为名称修改而烦恼。