Ich habe die folgende Fehlermeldung erhalten, als ich versuchte, eine C-Anwendung in 64-Bit-FreeBSD zu kompilieren:
Umzug R_X86_64_32S kann nicht verwendet werden, wenn ein gemeinsames Objekt erstellt wird; mit -fPIC neu kompilieren
Was ist R_X86_64_32S
Umzug und was ist R_X86_64_64
?
Ich habe nach dem Fehler und seinen möglichen Ursachen gegoogelt – Es wäre großartig, wenn jemand sagen könnte, was R_X86_64_32S wirklich bedeutet.
Das R_X86_64_32S
und R_X86_64_64
sind Namen von Verschiebungstypen für Code, der für die AMD64-Architektur kompiliert wurde. Sie können alle in der nachschlagen amd64 ABI. Demnach R_X86_64_64
gliedert sich in:
- R_X86_64 – alle Namen haben diesen Präfix
- 64 – Direkte 64-Bit-Verschiebung
und R_X86_64_32S
zu:
- R_X86_64 – Präfix
- 32S – Wert auf 32 Bit abschneiden und Vorzeichen erweitern
was im Grunde in beiden Fällen “den Wert des Symbols, auf das durch diese Verschiebung gezeigt wird, plus einen beliebigen Zusatz” bedeutet. Zum R_X86_64_32S
Der Linker überprüft dann, ob der generierte Wert mit Vorzeichen auf den ursprünglichen 64-Bit-Wert erweitert wird.
Nun wird in einer ausführbaren Datei den Code- und Datensegmenten eine bestimmte virtuelle Basisadresse gegeben. Der ausführbare Code wird nicht gemeinsam genutzt, und jede ausführbare Datei erhält ihren eigenen frischen Adressraum. Das bedeutet, dass der Compiler genau weiß, wo der Datenabschnitt sein wird, und direkt darauf verweisen kann. Bibliotheken hingegen können nur wissen, dass sich ihr Datenabschnitt an einem bestimmten Offset von der Basisadresse befinden wird; der Wert dieser Basisadresse kann nur zur Laufzeit bekannt sein. Daher müssen alle Bibliotheken mit Code erstellt werden, der unabhängig davon ausgeführt werden kann, wo er in den Speicher gestellt wird, bekannt als positionsunabhängiger Code (oder kurz PIC).
Wenn es nun darum geht, Ihr Problem zu lösen, spricht die Fehlermeldung für sich.
Damit dies alles sinnvoll ist, müssen Sie zuerst:
- siehe ein minimales Beispiel für einen Umzug: https://stackoverflow.com/a/30507725/895245
- den Grundaufbau einer ELF-Datei verstehen: https://stackoverflow.com/a/30648229/895245
Normen
R_X86_64_64
, R_X86_64_32
und R_X86_64_32S
werden alle durch die definiert System V AMD ABIdie die AMD64-Besonderheiten des ELF-Dateiformats enthält.
Sie sind alle möglichen Werte für die ELF32_R_TYPE
Feld eines Umzugseintrags, angegeben in der System V ABI 4.1 (1997) die die architekturneutralen Teile des ELF-Formats spezifiziert. Dieser Standard spezifiziert nur das Feld, aber nicht seine arch-abhängigen Werte.
Unter 4.4.1 „Umzugsarten“ sehen wir die Übersichtstabelle:
Name Field Calculation
------------ ------ -----------
R_X86_64_64 word64 A + S
R_X86_64_32 word32 A + S
R_X86_64_32S word32 A + S
Wir werden diese Tabelle später erklären.
Und der Hinweis:
Das R_X86_64_32
und R_X86_64_32S
Verschiebungen kürzen den berechneten Wert auf 32 Bit. Der Linker muss überprüfen, ob der generierte Wert für die R_X86_64_32 (R_X86_64_32S)-Verschiebung Null-erweitert (vorzeichenerweitert) auf den ursprünglichen 64-Bit-Wert.
Beispiel für R_X86_64_64 und R_X86_64_32
Lassen Sie uns zunächst untersuchen R_X86_64_64
und R_X86_64_32
:
.section .text
/* Both a and b contain the address of s. */
a: .long s
b: .quad s
s:
Dann:
as --64 -o main.o main.S
objdump -dzr main.o
Enthält:
0000000000000000 <a>:
0: 00 00 add %al,(%rax)
0: R_X86_64_32 .text+0xc
2: 00 00 add %al,(%rax)
0000000000000004 <b>:
4: 00 00 add %al,(%rax)
4: R_X86_64_64 .text+0xc
6: 00 00 add %al,(%rax)
8: 00 00 add %al,(%rax)
a: 00 00 add %al,(%rax)
Getestet auf Ubuntu 14.04, Binutils 2.24.
Ignorieren Sie vorerst die Disassemblierung (die bedeutungslos ist, da es sich um Daten handelt) und schauen Sie nur auf die Labels, Bytes und Verschiebungen.
Der erste Umzug:
0: R_X86_64_32 .text+0xc
Was bedeutet:
0
: wirkt auf Byte 0 (label a
)
R_X86_64_
: Präfix, das von allen Verschiebungstypen des AMD64-Systems V ABI verwendet wird
32
: die 64-Bit-Adresse des Labels s
wird auf eine 32-Bit-Adresse gekürzt, weil wir nur a angegeben haben .long
(4 Bytes)
.text
: Wir sind auf der .text
Sektion
0xc
: Dies ist das Summanddas ein Feld des Umzugseintrags ist
Die Umzugsadresse errechnet sich wie folgt:
A + S
Wo:
A
: der Nachtrag, hier 0xC
S
: hier der Wert des Symbols vor der Verschiebung 00 00 00 00 == 0
Daher lautet die neue Adresse nach dem Umzug 0xC == 12 Bytes in der .text
Sektion.
Das ist genau das, was wir erwarten, da s
kommt nach a .long
(4 Byte) und a .quad
(8 Bytes).
R_X86_64_64
ist analog, aber einfacher, da hier die Adresse nicht gekürzt werden muss s
. Dies wird durch das Standard-Through angezeigt word64
Anstatt von word32
auf der Field
Säule.
R_X86_64_32S gegen R_X86_64_32
Der Unterschied zwischen R_X86_64_32S
vs R_X86_64_32
ist, wenn der Linker sich beschwert “mit Verschiebung abgeschnitten, um zu passen”:
-
32
: beschwert sich, wenn der nach der Verschiebung abgeschnittene Wert nicht Null ist, den alten Wert erweitern, dh die abgeschnittenen Bytes müssen Null sein:
Z.B: FF FF FF FF 80 00 00 00
zu 80 00 00 00
erzeugt eine Beschwerde, weil FF FF FF FF
ist nicht null.
-
32S
: beschwert sich, wenn der nach der Verschiebung abgeschnittene Wert dies nicht tut Schild erweitern der alte Wert.
Z.B: FF FF FF FF 80 00 00 00
zu 80 00 00 00
ist in Ordnung, weil das letzte bisschen 80 00 00 00
und die abgeschnittenen Bits sind alle 1.
Siehe auch: Was bedeutet dieser GCC-Fehler “… relocation truncated to fit…”?
R_X86_64_32S
kann generiert werden mit:
.section .text
.global _start
_start:
mov s, %eax
s:
Dann:
as --64 -o main.o main.S
objdump -dzr main.o
Gibt:
0000000000000000 <_start>:
0: 8b 04 25 00 00 00 00 mov 0x0,%eax
3: R_X86_64_32S .text+0x7
Jetzt können wir beobachten, wie die “Verschiebung” abgeschnitten wird, um darauf zu passen 32S
mit einem Linker-Skript:
SECTIONS
{
. = 0xFFFFFFFF80000000;
.text :
{
*(*)
}
}
Jetzt:
ld -Tlink.ld a.o
Ist in Ordnung, denn: 0xFFFFFFFF80000000
wird abgeschnitten 80000000
was eine Zeichenerweiterung ist.
Aber wenn wir das Linker-Skript ändern in:
. = 0xFFFF0FFF80000000;
Es erzeugt jetzt den Fehler, weil das 0
machte es nicht mehr zu einer Zeichenerweiterung.
Begründung für die Verwendung 32S
für den Speicherzugriff aber 32
für Sofortnachrichten: Wann ist es für einen Assembler besser, eine vorzeichenerweiterte Verschiebung wie R_X86_64_32S anstelle einer Nullerweiterung wie R_X86_64_32 zu verwenden?
R_X86_64_32S und PIE (positionsunabhängige ausführbare Dateien
R_X86_64_32S kann nicht in positionsunabhängigen Executables verwendet werden, zB done with gcc -pie
andernfalls schlägt die Verknüpfung fehl mit:
relocation R_X86_64_32S against `.text' can not be used when making a PIE object; recompile with -fPIC
l
Ich habe ein Minimalbeispiel bereitgestellt, das dies erklärt unter: Was ist die Option -fPIE für positionsunabhängige ausführbare Dateien in gcc und ld?
Das bedeutet, dass ein gemeinsames Objekt ohne Verwendung kompiliert wurde -fPIC
Flagge, wie Sie sollten:
gcc -shared foo.c -o libfoo.so # Wrong
Sie müssen anrufen
gcc -shared -fPIC foo.c -o libfoo.so # Right
Unter der ELF-Plattform (Linux) werden gemeinsam genutzte Objekte mit positionsunabhängigem Code kompiliert – Code, der von jeder Stelle im Speicher ausgeführt werden kann. Wenn dieses Flag nicht angegeben ist, ist der generierte Code positionsabhängig, sodass es nicht möglich ist, diesen gemeinsam genutzten Code zu verwenden Objekt.
Ich bin auf dieses Problem gestoßen und habe festgestellt, dass diese Antwort mir nicht geholfen hat. Ich habe versucht, eine statische Bibliothek mit einer gemeinsam genutzten Bibliothek zu verknüpfen. Ich habe auch untersucht, ob Sie den Schalter -fPIC früher in die Befehlszeile eingefügt haben (wie in den Antworten an anderer Stelle empfohlen). Das einzige, was das Problem für mich behoben hat, war das Ändern der statischen Bibliothek in gemeinsam genutzt. Ich vermute, dass die Fehlermeldung über -fPIC aus einer Reihe von Gründen auftreten kann, aber im Grunde sollten Sie sich ansehen, wie Ihre Bibliotheken erstellt werden, und misstrauisch gegenüber Bibliotheken sein, die auf unterschiedliche Weise erstellt werden.
In meinem Fall trat das Problem auf, weil das zu kompilierende Programm erwartete, gemeinsam genutzte Bibliotheken in einem Remote-Verzeichnis zu finden, während nur die entsprechenden statischen Bibliotheken dort fälschlicherweise vorhanden waren.
Tatsächlich handelte es sich bei diesem Verschiebungsfehler um einen getarnten Datei-nicht-gefunden-Fehler.
Ich habe in diesem anderen Thread https://stackoverflow.com/a/42388145/5459638 detailliert beschrieben, wie ich damit umgegangen bin
Die obige Antwort zeigt, was diese Verschiebungen sind, und ich habe festgestellt, dass das Erstellen von x86_64-Objekten mit dem Flag GCC -mcmodel=large R_X86_64_32S verhindern kann, da der Compiler keine Annahme über die verschobene Adresse in diesem Modell hat.
Im folgenden Fall:
extern int myarr[];
int test(int i)
{
return myarr[i];
}
Mit gebaut gcc -O2 -fno-pie -c test_array.c
und zerlegen mit objdump -drz test_array.o
wir haben:
0: 48 63 ff movslq %edi,%rdi
3: 8b 04 bd 00 00 00 00 mov 0x0(,%rdi,4),%eax
6: R_X86_64_32S myarr
a: c3 ret
Mit -mcmodel=large, dh gcc -mcmodel=large -O2 -fno-pie -c test_array.c
wir haben:
0: 48 b8 00 00 00 00 00 movabs $0x0,%rax
7: 00 00 00
2: R_X86_64_64 myarr
a: 48 63 ff movslq %edi,%rdi
d: 8b 04 b8 mov (%rax,%rdi,4),%eax
10: c3 ret
Verwandte: Linux-Distributionen haben kürzlich damit begonnen, dies zu aktivieren Positionsunabhängige ausführbare Dateien standardmäßig für gcc. Sie erhalten diesen Fehler, wenn Sie versuchen, einen Nicht-PIC-Code in eine ausführbare Datei zu verlinken, einschließlich asm, der so etwas wie verwendet
mov $symbol, %edi
Anstatt vonlea symbol(%rip), %rdi
. Sehen Sie, dass absolute 32-Bit-Adressen in x86-64-Linux nicht mehr zulässig sind? für Informationen zur Verwendunggcc -no-pie -fno-pie
um herkömmliche positionsabhängige ausführbare Dateien zu erstellen, wenn Sie dies wünschen.– Peter Cordes
5. November 2017 um 7:45 Uhr
@PeterCordes Ich denke, Ihr Kommentar würde eine eigene Antwort verdienen
– Manuel Selva
30. März 2018 um 19:12 Uhr
@ManuelSelva: Der Link in meinem Kommentar führt zu meiner Antwort zu diesem Thema. Es ist nicht gerade eine Antwort auf diese Frage (was Umzüge sind). Die vorhandenen Antworten hier sind gut.
– Peter Cordes
30. März 2018 um 19:18 Uhr