是否需要链接所有依赖项?

时间:2019-01-07 09:00:42

标签: linux gcc g++ ld

假设我们有一个库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是共享库还是静态库?

2 个答案:

答案 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前端之一(gccg++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函数的程序 mainfunction1定义了一个功能, main.o,即调用call_function1;因此function1中引用了function1main.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中。

您是否需要在您的链接中添加libanotherlibanother 程序?

从首要原则出发,我们知道如果链接引用libutils的文件,则必须在链接中添加libanotherfunction3的定义中引用了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)的文件。所以没关系 function2libutils.a(function2.o)进行了未定义的引用。 function3 可能不存在。链接:

libutils.a(function2.o)

与以下链接完全相同:

$ gcc -o prog main.o libutils.a

案例2

假设您已将gcc -o prog main.o function1.o 实现为包含以下内容的静态库 一个同时定义libutilsfunction1的目标文件:

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实现为共享库 从libutilsfunction1.o编译的目标文件function2.ofunction1.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

所以:

  

是否需要链接所有依赖项?

是的,当您链接程序时。但是了解什么依赖项 一个程序是:

程序的依赖项是目标文件和/或共享库 定义在目标文件和共享库中引用的符号 实际上是链接的。

一个静态库永远都不是一个。它是目标文件的容器, 链接器将提取并链接程序的 依赖项,并忽略 其余的。


[1]链接器将对象文件链接到程序中的作用是根本上的。 与链接共享库的操作不同。

粗略地说,要链接对象文件,它实际上是合并对象的各个部分 文件组成输出程序的相应部分。

粗略地说,要链接共享库$ 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)

如果第二个库中没有调用,则不需要。通常,如果在链接过程中未提供未定义的库或其定义的链接功能,链接器将给出错误。