什么是GNU链接器中的部分链接?

gov*_*ind 13 c linker

我能找到的最佳解释来自官方文件:

-r --relocateable生成可重定位输出 - 即生成一个输出文件,该输出文件又可以作为ld的输入.这通常称为部分链接.作为副作用,在支持标准Unix幻数的环境中,此选项还将输出文件的幻数设置为OMAGIC.如果未指定此选项,则会生成绝对文件.链接C++程序时,此选项不会解析对构造函数的引用; 要做到这一点,请使用-Ur.此选项与`-i'的作用相同.

我特别想知道链接器输入中出现的符号会发生什么.当我有一个包含单个目标文件component.o的静态库libstatic.a时,请考虑具体情况.现在,我想创建另一个静态库libfinal.a,它将作为libstatic.a的接口.我用这个命令来创建它:

ld -r -o libfinal.a wrapper.o -L. -lstatic

其中wrapper.o提供了独有的API来调用libstatic.a中定义的函数

请问libfinal.a将只具有合并的归档文件wrapper.ocomponent.o或可待解决的所有引用wrapper.ocomponent.o来解决(连接),然后放入libfinal.a

Edit_1:根据所取得的进展更新问题:组件库()的objdump分别显示每个函数的部分(如预期的那样).在组合库中,由部分链接(flag)创建的只有一个部分.我想这意味着内部链接已经发生,而且不仅仅是创建一个普通的存档.libstatic.aobjdump -D libstatic.a.textlibfinal.a-r.text

Cir*_*四事件 8

最小的可运行示例

在这里,我给出一个最小的示例,并以两种方式对其进行编译以生成功能相同的可执行文件:

  • 一个f12.c没有部分链接的合并文件f12.o
  • 两个单独的f1.cf2.c其第一部分连接成f12_r.o

main.c

#include <assert.h>
#include <stdlib.h>

int f_1_2(void);
int f_2_1(void);

int main(void) {
    assert(f_1_2() + f_2_1() == 5);
    return EXIT_SUCCESS;
}
Run Code Online (Sandbox Code Playgroud)

f1.c

#include "f1.h"
Run Code Online (Sandbox Code Playgroud)

f2.c

#include "f2.h"
Run Code Online (Sandbox Code Playgroud)

f12.c

#include "f1.h"
#include "f2.h"
Run Code Online (Sandbox Code Playgroud)

1小时

int f_2(void);

int f_1_2(void) {
    return f_2() + 1;
}

int f_1(void) {
    return 1;
}
Run Code Online (Sandbox Code Playgroud)

2小时

int f_1(void);

int f_2_1(void) {
    return f_1() + 1;
}

int f_2(void) {
    return 2;
}
Run Code Online (Sandbox Code Playgroud)

运行

#!/usr/bin/env bash
set -eux
cflags='-ggdb3 -std=c99 -O0 -fPIE -pie'
gcc $cflags -c -o f1.o f1.c
gcc $cflags -c -o f2.o f2.c
gcc $cflags -c -o f12.o f12.c
ld -o f12_r.o -r f1.o f2.o
gcc $cflags -c -o main.o main.c
gcc $cflags -o main.out f12.o main.o
gcc $cflags -o main_r.out f12_r.o main.o
./main.out
./main_r.out
Run Code Online (Sandbox Code Playgroud)

GitHub上游

如果我们尝试相同的操作但不尝试ld -r,则会收到最终警告:

+ ld -o f12_r.o f1.o f2.o
ld: warning: cannot find entry symbol _start; defaulting to 0000000000401000
+ gcc -ggdb3 -std=c99 -O0 -fPIE -pie -o main_r.out f12_r.o main.o
/usr/bin/ld: error in f12_r.o(.eh_frame); no .eh_frame_hdr table will be created
Run Code Online (Sandbox Code Playgroud)

它们都不会使该工具退出非0状态,并且最终的可执行文件仍在运行,因此我不确定它的严重程度。待办事项了解。

二元分析

如果您不熟悉重定位,请先阅读以下内容:链接器做什么?

关键问题是部分链接如何加快链接速度。我唯一想到的就是通过解决预链接文件之间的引用。我现在专注于此。

但是,它并没有按照以下要求进行操作:解决部分链接中的相对重定位,因此我希望它不会显着加快链接速度。

我已经通过以下方式确认了这一点:

objdump -S f12.o
objdump -S f12_r.o
Run Code Online (Sandbox Code Playgroud)

两者都产生相同的输出,其中包含:

int f_1_2(void) {
   0:   55                      push   %rbp
   1:   48 89 e5                mov    %rsp,%rbp
    return f_2() + 1;
   4:   e8 00 00 00 00          callq  9 <f_1_2+0x9>
   9:   83 c0 01                add    $0x1,%eax
}
   c:   5d                      pop    %rbp
   d:   c3                      retq
Run Code Online (Sandbox Code Playgroud)

所以我们看到,调用f_1_2尚未在任何情况下得到解决,因为相对偏移地址依然是0: e8 00 00 00 00e8是操作码)。

这也告诉我,GCC不会在TODO原理的最终链接之前解决函数调用,有可能迫使其解决吗?

基准测试

我曾在LD vs Gold进行基准测试:用黄金代替ld-有经验吗?因此,我决定重用它以查看部分链接是否会导致任何链接加速。

我使用以下脚本生成了测试对象:

./generate-objects 100 1000 100
Run Code Online (Sandbox Code Playgroud)

然后我从最极端的链接案例入手:预链接除主文件以外的所有内容,然后对最终链接进行基准测试:

mv main.o ..
ld -o partial.o -r *.o
time gcc               partial.o ../main.o
time gcc -fuse-ld=gold partial.o ../main.o
Run Code Online (Sandbox Code Playgroud)

挂钟时间以秒为单位的结果如下:

          No partial link   Partial link
No Gold   6.15              5.756
Gold      4.06              4.457
Run Code Online (Sandbox Code Playgroud)

因此:

  • 存在时差,但不是很明显
  • 没有黄金,它走得更快,但是有了黄金,它变得更慢!

因此,根据此实验,似乎部分链接可能根本不会加快链接时间,因此我建议您先尝试使用GOLD。

让我知道您是否可以提供一个具体的示例,其中增量链接可以显着提高速度。

案例研究:Linux内核

Linux内核是一个大型项目的一个示例,该项目曾经使用增量链接,因此也许我们可以从中学到一些东西。

此后它已移至ar T精简归档文件,如下所示:https : //unix.stackexchange.com/questions/5518/what-is-the-difference-between-the-following-kernel-makefile-terms-vmlinux-vml/482978 #482978

初始提交和依据位于:a5967db9af51a84f5e181600954714a9e4c69f1f(包含在中v4.9),其提交消息显示:

ld -r is an incremental link used to create built-in.o files in build
subdirectories. It produces relocatable object files containing all
its input files, and these are are then pulled together and relocated
in the final link. Aside from the bloat, this constrains the final
link relocations, which has bitten large powerpc builds with
unresolvable relocations in the final link.
Run Code Online (Sandbox Code Playgroud)

Documentation / process / changes.rst中也提到了这一点:

Binutils
--------

The build system has, as of 4.13, switched to using thin archives (`ar T`)
rather than incremental linking (`ld -r`) for built-in.a intermediate steps.
This requires binutils 2.20 or newer.
Run Code Online (Sandbox Code Playgroud)

TODO:了解何时引入了增量链接,并查看是否存在最小的测试用例,我们可以使用它来加快速度:https : //unix.stackexchange.com/questions/491312/why-does-the-linux内核构建系统使用增量链接或ar薄型拱门

已在Ubuntu 18.10,GCC 8.2.0,Lenovo ThinkPad P51笔记本电脑,Intel Core i7-7820HQ CPU(4核/ 8线程),2个Samsung M471A2K43BB1-CRC RAM(2个16GiB),Samsung MZVLB512HAJQ-000L7 SSD(3,000 MB / s)上进行了测试)。


Max*_*kin 0

ld创建可执行文件和共享库,而不是目标文件存档(.a 文件)。

ar创建和修改目标文件档案。


-r, --relocateable当您想要解析 a 的某些(未解析的)符号.so并生成另一个时,该选项很有用.so

  • 似乎“-r”标志也可以用于部分链接。请参阅问题中我的**Edit_1**。至于验证,我可以将使用“-r”标志创建的最终库与我的应用程序链接起来。 (2认同)