Was bedeuten R_X86_64_32S und R_X86_64_64 Umzug?

Lesezeit: 10 Minuten

Benutzeravatar von Raj
Raj

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.

  • 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 von lea symbol(%rip), %rdi. Sehen Sie, dass absolute 32-Bit-Adressen in x86-64-Linux nicht mehr zulässig sind? für Informationen zur Verwendung gcc -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

Benutzeravatar von Michael Foukarakis
Michael Foukarakis

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.

Ciro Santilli Benutzeravatar von OurBigBook.com
Ciro Santilli OurBigBook.com

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 80000000was 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 -pieandernfalls 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?

  • mov s, %eax ist ein etwas verwirrendes Beispiel für eine signierte absolute 32-Bit-Verschiebung, weil Sie das normalerweise nur aus Versehen schreiben würden. Es ist leicht zu übersehen, dass Sie die ausgelassen haben (%rip)und dass es ein Ladevorgang ist, kein mov-immediate ($swas nullerweitert absolut oder mit signiert wäre mov $s, %rax). Besser wäre mov s(%rdi), %eax (oder LEA), oder sub $s, %rdi (oder cmp $s, %rdi). Die Verwendung einer statischen Adresse mit Register-Offsets in einem Adressierungsmodus ist einer der wichtigsten Anwendungsfälle für absolute 32-Bit-Adressen.

    – Peter Cordes

    30. März 2018 um 19:25 Uhr

  • verwandt: Absolute 32-Bit-Adressen sind in x86-64-Linux nicht mehr zulässig?: Konfiguration von gcc with -pie -fpie da die Standardeinstellung jetzt bei Linux-Distributionen üblich ist, wird dies Leute beißen, die ausführbare Dateien erstellen, sowie absichtlich gemeinsam genutzte Bibliotheken erstellen.

    – Peter Cordes

    30. März 2018 um 19:26 Uhr

  • @Ciro, weist -mcmodel=large den Compiler an, standardmäßig R_X86_64_64 zu verwenden?

    – Dmitri Michuschin

    13. Juli um 22:05 Uhr

  • @DmitryMikushin Entschuldigung, ich weiß es nicht, lass es mich wissen, wenn du es herausfindest.

    – Ciro Santilli OurBigBook.com

    14. Juli um 10:44 Uhr

  • @CiroSantilliПутлерКапут六四事 Ich denke schon. Das Lustige ist jedoch, dass GCC-CRT-Startobjekte R_X86_64_32 bleiben. Man muss sie manuell mit dem Flag -mcmode=large neu erstellen, wie hier gezeigt: sourceware.org/bugzilla/show_bug.cgi?id=26386

    – Dmitri Michuschin

    14. Juli um 11:39 Uhr

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 habe das gleiche Problem, aber ich bekomme immer noch den Fehler, auch wenn -fPIC hinzugefügt wurde: gcc -std=c99 -Wall -pedantic -shared -fopenmp -fPIC -static test.c -o libtest.so. Irgendwelche Ideen warum? Vielen Dank!

    – Ciprian Tomoiagă

    10. Dezember 2014 um 0:38 Uhr

  • Dieses Beispiel zeigt deutlich, wie das gcc -fPIC-Flag verwendet wird, um „x86-64-32s“- und „r-x86-64-64“-bezogene Fehler zu beheben.

    – alphaGeek

    21. Dezember 2021 um 10:32 Uhr

  • @CiprianTomoiagă spät zur Party, aber in meinem Fall … war in meiner Abhängigkeitskette eine statische Bibliothek vergraben, die nicht mit -fPIC kompiliert wurde. Also bekam ich diesen Fehler, obwohl die äußerste Bibliothek mit -fPIC in der Anleitung kompiliert wurde. Keine Wunder, denke ich. Die Behebung bestand darin, die Kette zurückzugehen und die statische Bibliothek zu finden, die ohne -fPIC kompiliert wurde, und sie zu ändern.

    – Timothy John Laird

    28. Dezember 2021 um 22:04 Uhr


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.

Benutzeravatar von XavierStuvw
XavierStuvw

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

Benutzeravatar von Iru Dog
Iru-Hund

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.owir 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.cwir 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    

1413260cookie-checkWas bedeuten R_X86_64_32S und R_X86_64_64 Umzug?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy