假设我们有一个库libutils,它实现两个功能:function1和function2。假设function2使用库libanother(因此libutils取决于libanother)。
现在,我想构建仅使用function1的应用程序:
g++ myapplication.cpp -o my application -lutils
是否需要在链接中包含libanother:
g++ myapplication.cpp -o my application -lutils -lanother
是否有与此相关的链接器要求?答案取决于库libutils和libanother是共享库还是静态库?
答案 0 :(得分:2)
一些首要原则
可以将三种文件输入到程序的链接中:
但是实际上只有两个这些文件可以链接到程序中: 目标文件和共享库。 1
如果您输入任何 object 文件p.o
到程序的链接程序,则链接器
将其无条件链接到程序 。
如果您在链接中输入static library libx.a
,则链接器从每个存档成员的libx.a
中提取
libx.a(q.o)
需要 进行链接并将 it 链接到
该程序。如果存档成员libx.a(q.o)
定义了至少一个,则需要
在某些文件(目标文件或目标文件)中已被引用但尚未定义的符号
共享库)已经链接到程序中。
如果您输入共享库liby.so
,则故事分叉:-
如果到达ld
时链接器(--as-needed
)选项liby.so
生效,
那么如果需要与存档成员liby.so
相同,则libx.a(q.o)
将被链接到程序中。
如果--as-needed
实际上不是 ,则liby.so
是无条件链接的,例如
一个目标文件。
通常,如果您通过发行版的GCC前端之一(gcc
,g++
,gfortran
...)调用链接器,
方式,那么只要默认情况下,您将始终在默认情况下获得共享库的--as-needed
行为,或者始终不得到它
GCC工具链保持不变,因为您的发行版GCC版本将配置--as-needed
根据发行版对这样做的优缺点的评估,是否将其作为默认的链接选项。
近年来,来自Debian的发行版(Ubuntu等人)默认为--as-needed
。我上次看
Redhat发行的发行版(Fedora等)没有。 2
将任何文件file.{o|so}
链接到程序后,它将0个或多个未解析的引用引入程序。这些未解决的参考是
引用file.{o|so}
中尚未定义的file.{o|so}
中或尚未链接到程序的任何其他文件中的符号。
链接器必须在完成链接的链接时解析所有引用。 程序,否则链接失败,并出现未解决的参考错误。必须输入和链接文件,以提供以下内容的定义: 链接文件中所引用的所有符号。
根据您的情况
当你说:
我的应用程序...仅使用function1
您的意思是将链接引用function1
的某些文件 。这很简单
假设此文件是将无条件链接的某个目标文件,
说出定义main
的那个,例如
main.c
#include <stdio.h>
extern void function1(void);
void call_function1(void) {
function1();
}
int main(void)
{
puts("Hello World!");
}
我们将其编译为目标文件:
$ gcc -Wall -c main.c
请注意,尽管我们无法{@ {1}}引用main.o
以任何会调用的方式运行带有该function1
函数的程序
main
。 function1
定义了一个功能,
main.o
,即调用call_function1
;因此function1
中引用了function1
。
main.o
本身没有被引用,但call_function1
被引用。如果我们看
function1
的符号表:
main.o
定义了带有注释$ nm main.o
0000000000000000 T call_function1
U function1
U _GLOBAL_OFFSET_TABLE_
000000000000000c T main
U puts
的符号。引用了带注释的T
但未定义。因此,当我们尝试将U
链接到程序中时:
main.o
即使链接了程序,我们也需要输入定义$ gcc -o prog main.o
/usr/bin/ld: main.o: in function `call_function1':
main.c:(.text+0x5): undefined reference to `function1'
collect2: error: ld returned 1 exit status
的文件
与此function1
在一起的人可能无法调用main.o
。
您的function1
在某种程度上在库function1
中定义,该库也
定义libutils
。您的程序未引用function2
,但是
function2
的定义引用了一个或多个已定义的符号,例如function2
,
不知何故,在另一个库function3
中。
您是否需要在您的链接中添加libanother
和libanother
程序?
从首要原则出发,我们知道如果链接引用libutils
的文件,则必须在链接中添加libanother
。
function3
的定义中引用了function3
。因此,您必须与function2
链接
如果您链接的文件包含libanother
的定义。
您需要链接这样的文件吗?
这取决于function2
本身的实现。
案例1
假设您已将libutils
实现为静态库,
归档了一个定义libutils
的目标文件和另一个目标文件
定义function1
。像这样:
function1.c
function2
function2.c
#include <stdio.h>
void function1(void)
{
puts(__func__);
}
编译源文件:
extern void function3();
void function2(void)
{
function3();
}
归档目标文件:
$ gcc -Wall -c function1.c function2.c
像这样链接$ ar rcs libutils.a function1.o function2.o
:
prog
没问题。运行:
$ gcc -o prog main.o libutils.a
让我们再次进行链接,这次使用诊断程序来显示什么 目标文件和共享库实际上已链接:
$ ./prog
Hello World!
大多数输入是样板对象文件和共享库,它们
$ gcc -o prog main.o libutils.a -Wl,-trace
/usr/bin/ld: mode elf_x86_64
/usr/lib/gcc/x86_64-linux-gnu/8/../../../x86_64-linux-gnu/Scrt1.o
/usr/lib/gcc/x86_64-linux-gnu/8/../../../x86_64-linux-gnu/crti.o
/usr/lib/gcc/x86_64-linux-gnu/8/crtbeginS.o
main.o
(libutils.a)function1.o
libgcc_s.so.1 (/usr/lib/gcc/x86_64-linux-gnu/8/libgcc_s.so.1)
/lib/x86_64-linux-gnu/libc.so.6 (//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)
/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/8/libgcc_s.so.1)
/usr/lib/gcc/x86_64-linux-gnu/8/crtendS.o
/usr/lib/gcc/x86_64-linux-gnu/8/../../../x86_64-linux-gnu/crtn.o
默认添加到程序链接。 我们构建的文件只是:
gcc
档案成员main.o
(libutils.a)function1.o
根本没有链接,因为
没有链接引用libutils.a(function2.o)
的文件。所以没关系
function2
对libutils.a(function2.o)
进行了未定义的引用。 function3
可能不存在。链接:
libutils.a(function2.o)
与以下链接完全相同:
$ gcc -o prog main.o libutils.a
案例2
假设您已将gcc -o prog main.o function1.o
实现为包含以下内容的静态库
一个同时定义libutils
和function1
的目标文件:
function12.c
function2
编译该文件:
#include <stdio.h>
void function1(void)
{
puts(__func__);
}
extern void function3(void);
void function2(void)
{
function3();
}
重新创建$ gcc -Wall -c function12.c
:
libutils.a
重新链接$ rm libutils.a
$ ar rcs libutils.a function12.o
:
prog
这一次,如$ gcc -o prog main.o libutils.a -Wl,-trace
/usr/bin/ld: mode elf_x86_64
/usr/lib/gcc/x86_64-linux-gnu/8/../../../x86_64-linux-gnu/Scrt1.o
/usr/lib/gcc/x86_64-linux-gnu/8/../../../x86_64-linux-gnu/crti.o
/usr/lib/gcc/x86_64-linux-gnu/8/crtbeginS.o
main.o
(libutils.a)function12.o
libgcc_s.so.1 (/usr/lib/gcc/x86_64-linux-gnu/8/libgcc_s.so.1)
/lib/x86_64-linux-gnu/libc.so.6 (//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)
/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/8/libgcc_s.so.1)
/usr/lib/gcc/x86_64-linux-gnu/8/crtendS.o
/usr/lib/gcc/x86_64-linux-gnu/8/../../../x86_64-linux-gnu/crtn.o
/usr/bin/ld: libutils.a(function12.o): in function `function2':
function12.c:(.text+0x32): undefined reference to `function3'
/usr/bin/ld: link errors found, deleting executable `prog'
collect2: error: ld returned 1 exit status
输出所示,我们的目标文件是
-trace
已链接。 main.o
(libutils.a)function12.o
引用了main.o
,因此链接器搜索了function1
用于定义libutils.a
的目标文件。找到function1
并链接了
它,包括function12.o
的定义以及function2
的定义。
function1
的定义引入了对function2
的未解决的引用,
进入程序。没有文件链接可以解决该引用,因此
链接失败:
function3
程序不能调用 function12.c:(.text+0x32): undefined reference to `function3'
没关系-
就像无法调用function2
一样。 3
案例3
通过链接位置无关的链接,您已经将function1
实现为共享库
从libutils
和function1.o
编译的目标文件function2.o
和function1.c
分别:
function2.c
请注意,尽管共享库$ gcc -Wall -fPIC -c function1.c function2.c
$ gcc -shared -o libutils.so function1.o function2.o
是产生的ELF文件
通过 linkage (不是目标文件的libutils.so
档案),我们能够成功链接它
尽管ar
包含对function2.o
的未定义引用。
可以将共享库与未定义的引用链接在一起。查看其符号表:
function3
有$ nm libutils.so
0000000000004030 b completed.7930
w __cxa_finalize@@GLIBC_2.2.5
0000000000001060 t deregister_tm_clones
00000000000010d0 t __do_global_dtors_aux
0000000000003e18 t __do_global_dtors_aux_fini_array_entry
0000000000004028 d __dso_handle
0000000000003e20 d _DYNAMIC
0000000000001150 t _fini
0000000000001110 t frame_dummy
0000000000003e10 t __frame_dummy_init_array_entry
00000000000020f0 r __FRAME_END__
0000000000002020 r __func__.2361
0000000000001115 T function1
0000000000001142 T function2
U function3
0000000000004000 d _GLOBAL_OFFSET_TABLE_
w __gmon_start__
000000000000202c r __GNU_EH_FRAME_HDR
0000000000001000 t _init
w _ITM_deregisterTMCloneTable
w _ITM_registerTMCloneTable
U printf@@GLIBC_2.2.5
0000000000001090 t register_tm_clones
。但是您不能将程序与未定义的引用链接起来:
U function3
当我们链接时:
$ gcc -o prog main.o libutils.so -Wl,-trace
/usr/bin/ld: mode elf_x86_64
/usr/lib/gcc/x86_64-linux-gnu/8/../../../x86_64-linux-gnu/Scrt1.o
/usr/lib/gcc/x86_64-linux-gnu/8/../../../x86_64-linux-gnu/crti.o
/usr/lib/gcc/x86_64-linux-gnu/8/crtbeginS.o
main.o
libutils.so
libgcc_s.so.1 (/usr/lib/gcc/x86_64-linux-gnu/8/libgcc_s.so.1)
/lib/x86_64-linux-gnu/libc.so.6 (//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)
/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/8/libgcc_s.so.1)
/usr/lib/gcc/x86_64-linux-gnu/8/crtendS.o
/usr/lib/gcc/x86_64-linux-gnu/8/../../../x86_64-linux-gnu/crtn.o
/usr/bin/ld: libutils.so: undefined reference to `function3'
/usr/bin/ld: link errors found, deleting executable `prog'
collect2: error: ld returned 1 exit status
链接失败,就像:
main.o
libutils.so
或者实际上:
main.o
(libutils.a)function12.o
所以:
是否需要链接所有依赖项?
是的,当您链接程序时。但是了解什么依赖项 一个程序是:
程序的依赖项是目标文件和/或共享库 定义在目标文件和共享库中引用的符号 实际上是链接的。
一个静态库永远都不是一个。它是目标文件的容器, 链接器将提取并链接程序的 依赖项,并忽略 其余的。
粗略地说,要链接对象文件,它实际上是合并对象的各个部分 文件组成输出程序的相应部分。
粗略地说,要链接共享库$ gcc -o prog main.o function1.o function2.o
/usr/bin/ld: function2.o: in function `function2':
function2.c:(.text+0x5): undefined reference to `function3'
collect2: error: ld returned 1 exit status
,链接器只需在其中写入一些简明信息即可
运行时加载程序将解析的标准格式的程序,
指示它查找并加载liby.so
并合并它所在的部分
组成执行程序的过程。
[2]您可以检查GCC工具链是否默认为liby.so
通过在默认GCC链接的详细输出中grepping该选项,例如
--as-needed
[3]我们可以使用非默认编译选项-ffunction-sections
编译目标文件
并使用非默认链接器选项-gc-sections
将它们链接到程序中,并使用
使链接器能够舍弃功能定义
无法调用,因此它们不会引起任何强加无用的引用
对程序的依赖。
答案 1 :(得分:0)
如果第二个库中没有调用,则不需要。通常,如果在链接过程中未提供未定义的库或其定义的链接功能,链接器将给出错误。