Die beste Erklärung, die ich finden konnte, stammt aus dem offiziellen Dokument:
-r –relocateable Verschiebbare Ausgabe generieren – dh eine Ausgabedatei generieren, die wiederum als Eingabe für ld dienen kann. Dies wird oft als partielle Verknüpfung bezeichnet. Als Nebeneffekt setzt diese Option in Umgebungen, die standardmäßige magische Unix-Zahlen unterstützen, auch die magische Zahl der Ausgabedatei auf OMAGIC. Wenn diese Option nicht angegeben ist, wird eine absolute Datei erstellt. Beim Linken von C++-Programmen löst diese Option keine Verweise auf Konstruktoren auf; Verwenden Sie dazu -Ur. Diese Option macht dasselbe wie `-i’.
Ich bin besonders daran interessiert zu wissen, was mit den Symbolen passiert, die in den Eingaben für den Linker vorhanden sind. Nehmen Sie einen bestimmten Fall, wenn ich eine statische Bibliothek habe libstatic.a die eine einzelne Objektdatei enthält Komponente.o. Jetzt möchte ich eine weitere statische Bibliothek erstellen libfinal.a die als Schnittstelle zu fungieren wird libstatic.a. Ich verwende diesen Befehl, um es zu erstellen:
ld -r -o libfinal.a wrapper.o -L. -lstatic
Wo Wrapper.o bietet exklusive APIs zum Aufrufen der in libstatic.a definierten Funktionen
Wird die libfinal.a nur ein kombiniertes Archiv haben Wrapper.o und Komponente.o oder alle Referenzen, die zwischen aufgelöst werden können Wrapper.o und Komponente.o aufgelöst (verlinken) und dann in platziert werden libfinal.a?
Bearbeiten_1: Aktualisierung der Frage basierend auf den erzielten Fortschritten: Die objdump der Komponentenbibliothek libstatic.a
(objdump -D libstatic.a
) zeigt an .text
Abschnitte getrennt für jede Funktion (wie erwartet). Während in der kombinierten Bibliothek libfinal.a
die durch teilweises Verlinken entstanden ist (-r
Flagge) gibt es nur eine einzige .text
Sektion. Ich denke, das bedeutet, dass eine interne Verlinkung stattgefunden hat und nicht nur ein einfaches Archiv erstellt wird.
Minimales lauffähiges Beispiel
Hier produziere ich ein Minimalbeispiel und kompiliere es auf zwei Arten, um funktional identische ausführbare Dateien zu erzeugen:
- eins kombiniert
f12.c
Datei ohne teilweise Verlinkung Verlinkung in f12.o
- zwei getrennt
f1.c
und f2.c
die zunächst teilweise verlinkt sind f12_r.o
Haupt 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;
}
run.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-Upstream.
Wenn wir dasselbe versuchen, aber ohne ld -r
dann erhalten wir die letzten Warnungen:
+ 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
Keiner von ihnen bewirkt, dass das Tool nicht 0 beendet, und die endgültige ausführbare Datei wird immer noch ausgeführt, daher bin ich mir nicht sicher, wie schlimm es ist. TODO verstehen.
Binäre Analyse
Wenn Sie mit Relocation nicht vertraut sind, lesen Sie zuerst Folgendes: Was machen Linker?
Die Schlüsselfrage ist, wie eine teilweise Verlinkung die Verlinkung beschleunigen könnte. Das einzige, was mir einfiel, war das Auflösen von Referenzen über vorverknüpfte Dateien hinweg. Darauf habe ich mich jetzt konzentriert.
Allerdings tut es das nicht wie bei: Resolve relative reslocations in partial link, also würde ich erwarten, dass es den Link nicht wesentlich beschleunigt.
Ich habe dies bestätigt mit:
objdump -S f12.o
objdump -S f12_r.o
beide erzeugen identische Ausgaben, die Folgendes enthalten:
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
so sehen wir, dass der Aufruf an f_1_2
ist in beiden Fällen noch nicht gelöst, da die relative Offset-Adresse noch 0 ist: e8 00 00 00 00
(e8
ist der Opcode).
Dies lehrte mich auch, dass GCC keine Funktionsaufrufe vor dem letzten Link auflöst, entweder TODO-Grundprinzip, möglich, es zur Auflösung zu zwingen?
Benchmark
Ich hatte LD vs. GOLD bei: Ersetzen von LD durch Gold – irgendwelche Erfahrungen? Also habe ich mich entschieden, es wiederzuverwenden, um zu sehen, ob eine teilweise Verknüpfung zu einer Link-Beschleunigung führt.
Die Testobjekte habe ich mit generiert dieses Skript:
./generate-objects 100 1000 100
und dann habe ich mit dem extremsten möglichen Link-Fall begonnen: alles außer der Hauptdatei vorverlinken und dann den endgültigen Link bewerten:
mv main.o ..
ld -o partial.o -r *.o
time gcc partial.o ../main.o
time gcc -fuse-ld=gold partial.o ../main.o
Die Ergebnisse der Wanduhrzeit in Sekunden waren wie folgt:
No partial link Partial link
No Gold 6.15 5.756
Gold 4.06 4.457
Deswegen:
- der Zeitunterschied existiert, ist aber nicht sehr signifikant
- ohne Gold ging es schneller, aber mit GOLD wurde es langsamer!
Basierend auf diesem Experiment scheint es daher, dass die teilweise Verlinkung Ihre Linkzeit überhaupt nicht beschleunigt, und ich würde Ihnen nur empfehlen, stattdessen GOLD zu versuchen, um damit zu beginnen.
Lassen Sie mich wissen, ob Sie ein konkretes Beispiel vorlegen können, bei dem die inkrementelle Verknüpfung zu einer erheblichen Beschleunigung führt.
Fallstudie: Der Linux-Kernel
Der Linux-Kernel ist ein Beispiel für ein großes Projekt, das früher inkrementelles Linken verwendete, also können wir vielleicht etwas davon lernen.
Inzwischen ist es umgezogen ar T
dünne Archive wie gezeigt unter: https://unix.stackexchange.com/questions/5518/what-is-the-difference-between-the-following-kernel-makefile-terms-vmlinux-vml/482978#482978
Das anfängliche Commit und die Begründung finden Sie unter: a5967db9af51a84f5e181600954714a9e4c69f1f (enthalten in v4.9
), deren Commit-Nachricht lautet:
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.
dies wird auch unter erwähnt Dokumentation/Prozess/Änderungen.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: Finden Sie heraus, wann die inkrementelle Verknüpfung eingeführt wurde, und prüfen Sie, ob es einen minimalen Testfall gibt, mit dem wir sehen können, dass es schneller geht: https://unix.stackexchange.com/questions/491312/why-does-the-linux-kernel-build-system-use-incremental-linking-or-ar-t-thin-arch
Getestet auf Ubuntu 18.10, GCC 8.2.0, Lenovo ThinkPad P51 Laptop, Intel Core i7-7820HQ CPU (4 Kerne / 8 Threads), 2x Samsung M471A2K43BB1-CRC RAM (2x 16GiB), Samsung MZVLB512HAJQ-000L7 SSD (3.000 MB/s ).
ld
erstellt ausführbare Dateien und gemeinsam genutzte Bibliotheken, keine Objektdateiarchive (.a-Dateien).
ar
erstellt und modifiziert Objektdateiarchive.
-r, --relocateable
Die Option ist nützlich, wenn Sie bestimmte (nicht aufgelöste) Symbole von a auflösen möchten .so
und einen anderen produzieren .so
.