什么是 GNU 链接器中的部分链接? [英] What is Partial Linking in GNU Linker?

查看:37
本文介绍了什么是 GNU 链接器中的部分链接?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我能找到的最好的解释来自官方文档:

The best explanation I was able to find was from the official document:

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

-r --relocateable Generate relocatable output--i.e., generate an output file that can in turn serve as input to ld. This is often called partial linking. As a side effect, in environments that support standard Unix magic numbers, this option also sets the output file's magic number to OMAGIC. If this option is not specified, an absolute file is produced. When linking C++ programs, this option will not resolve references to constructors; to do that, use -Ur. This option does the same thing as `-i'.

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

I am specifically interested in knowing what happens to the symbols present in inputs to linker. Take a specific case when I have a static library libstatic.a which contains a single object file component.o. Now, I want to create another static library libfinal.a which will work as an interface to libstatic.a. I use this command to create it:

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

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

Where wrapper.o provides exclusive APIs to call the functions defined in libstatic.a

libfinal.a 是否只是具有 wrapper.ocomponent.o 或所有可能的引用的组合存档- wrapper.ocomponent.o 之间的解析被解析(链接)然后放入 libfinal.a?

Will the libfinal.a be just a combined archive having wrapper.o and component.o or all the references which can-be-resolved between wrapper.o and component.o be resolved(linking) and then placed into libfinal.a?

Edit_1:根据取得的进展更新问题:组件库libstatic.aobjdump(objdump -D libstatic.a)分别显示.text部分对于每个功能(如预期的那样).而在通过部分链接 (-rflag) 创建的组合库 libfinal.a 中只有一个 .text 部分.我想这意味着发生了内部链接,而不仅仅是创建一个普通的存档.

Edit_1: Updating the question based on the progress made: The objdump of the component library libstatic.a (objdump -D libstatic.a) shows .text sections separately for each function (as expected). Whereas in the combined library libfinal.a, which has been created by partial linking (-rflag) there is just one single .text section. I guess this means that an internal-linking has taken place and it's not just creating a plain archive.

推荐答案

最小可运行示例

这里我生成了一个最小的示例,并以两种方式编译它以生成功能相同的可执行文件:

Here I produce a minimal example and compile it in two ways to produce functionally identical executables:

  • 一个组合的 f12.c 文件,没有部分链接链接到 f12.o
  • 两个独立的 f1.cf2.c 首先部分链接到 f12_r.o
  • one combined f12.c file without partial linking linking into f12.o
  • two separate f1.c and f2.c which are first partially linked into 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;
}

f1.c

#include "f1.h"

f2.c

#include "f2.h"

f12.c

#include "f1.h"
#include "f2.h"

f1.h

int f_2(void);

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

int f_1(void) {
    return 1;
}

f2.h

int f_1(void);

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

int f_2(void) {
    return 2;
}

运行.sh

#!/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

GitHub 上游.

如果我们尝试同样的事情但没有 ld -r,那么我们会得到最终的警告:

If we try the same thing but without ld -r, then we get the final warnings:

+ 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

它们都不会使工具退出非 0,并且最终的可执行文件仍然运行,所以我不确定它有多糟糕.TODO 明白.

none of them makes makes the tool exit non-0, and the final executable still runs, so I'm not sure how bad it is. TODO understand.

二进制分析

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

If you are not familiar with relocation, first read this: What do linkers do?

关键问题是部分链接如何加快链接速度.我唯一能想到的就是解决跨预链接文件的引用.我现在专注于这个.

The key question is how could partial linking speed up the link. The only thing I could think of was by resolving references across pre-linked files. I've focused on this for now.

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

However, it does not do that as asked at: Resolve relative relocations in partial link so I would expect it not to speed up link significantly.

我已确认:

objdump -S f12.o
objdump -S f12_r.o

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

both of which produce identical outputs that contain:

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

所以我们看到对 f_1_2 的调用在任何一种情况下都没有得到解决,因为相对偏移地址仍然是 0:e8 00 00 00 00(e8 是操作码).

so we see that the call to f_1_2 has not yet been resolved in either case because the relative offset address is still 0: e8 00 00 00 00 (e8 is the opcode).

这也教会了我GCC在最终链接之前没有解析函数调用或者TODO原理,可以强制它解析吗?

This also taught me that GCC does not resolve function calls before the final link either TODO rationale, possible to force it to resolve?

基准测试

我在以下位置对 LD 与 GOLD 进行了基准测试:用黄金替换 ld -有什么经验吗? 所以我决定重用它,看看部分链接是否会导致链接加速.

I had benchmarked LD vs GOLD at: Replacing ld with gold - any experience? so I decided to reuse it to see if partial linking leads to any link speedup.

我使用这个脚本:

./generate-objects 100 1000 100

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

and then I started with the most extreme link case possible: pre-link everything except the main file, and then benchmark the final link:

mv main.o ..
ld -o partial.o -r *.o
time gcc               partial.o ../main.o
time gcc -fuse-ld=gold partial.o ../main.o

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

The wall clock time results in seconds were as follows:

          No partial link   Partial link
No Gold   6.15              5.756
Gold      4.06              4.457

因此:

  • 存在时差,但不是很显着
  • 没有金币它跑得更快,但有了金币它变得更慢了!

因此,根据这个实验,部分链接似乎根本不会加快你的链接时间,我只是建议你先尝试 GOLD.

Therefore, based on this experiment, it seems that partial linking may not speed up your link time, at all, and I'd just recommend you to try GOLD instead to start with.

如果您能提供一个具体示例,让我知道增量链接会导致显着加速.

Let me know if you can produce a concrete example where incremental linking leads to significant speedup.

案例研究:Linux 内核

Linux 内核是过去使用增量链接的大型项目的一个例子,所以也许我们可以从中学到一些东西.

The Linux kernel is one example of a large project that used to use incremental linking, so maybe we can learn something from it.

它已移至 ar T 瘦档案,如下所示:https://unix.stackexchange.com/questions/5518/what-is-the-difference-between-the-following-kernel-makefile-terms-vmlinux-vml/482978#482978

It has since moved to ar T thin archives as shown at: https://unix.stackexchange.com/questions/5518/what-is-the-difference-between-the-following-kernel-makefile-terms-vmlinux-vml/482978#482978

初始提交和理由是:a5967db9af51a84f5e181600954714a9e4c69f1f(包含在 v4.9 中)其提交消息显示:

The initial commit and rationale are at: a5967db9af51a84f5e181600954714a9e4c69f1f (included in v4.9) whose commit message says:

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.

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.

TODO:找出何时引入增量链接,看看是否有一个最小的测试用例可以让我们更快地看到它:https://unix.stackexchange.com/questions/491312/why-does-the-linux-kernel-build-system-use-incremental-linking-or-ar-t-thin-arch

TODO: find out when incremental linking was introduced, and see if there is a minimal test case that we can use to see it going faster: https://unix.stackexchange.com/questions/491312/why-does-the-linux-kernel-build-system-use-incremental-linking-or-ar-t-thin-arch

在 Ubuntu 18.10、GCC 8.2.0、Lenovo ThinkPad P51 笔记本电脑、Intel Core i7-7820HQ CPU(4 核/8 线程)、2x Samsung M471A2K43BB1-CRC RAM (2x 16GiB)、Samsung MZVLB512HAJQ-000L7 SSD (3,000MB/s).

Tested on Ubuntu 18.10, GCC 8.2.0, Lenovo ThinkPad P51 laptop, Intel Core i7-7820HQ CPU (4 cores / 8 threads), 2x Samsung M471A2K43BB1-CRC RAM (2x 16GiB), Samsung MZVLB512HAJQ-000L7 SSD (3,000 MB/s).

这篇关于什么是 GNU 链接器中的部分链接?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆