Was bedeutet dieser GCC-Fehler “… relocation truncated to fit…”?

Lesezeit: 10 Minuten

Benutzeravatar von ysap
Jap

Ich programmiere die Host-Seite eines Host-Accelerator-Systems. Der Host läuft auf dem PC unter Ubuntu Linux und kommuniziert über eine USB-Verbindung mit der Embedded-Hardware. Die Kommunikation erfolgt durch Kopieren von Speicherblöcken in den und aus dem Speicher der eingebetteten Hardware.

Auf dem Speicher des Boards gibt es einen Speicherbereich, den ich als Postfach verwende, wo ich die Daten schreibe und lese. Das Postfach ist als Struktur definiert, und ich verwende dieselbe Definition, um ein Spiegelpostfach in meinem Hostbereich zuzuweisen.

Ich habe diese Technik in der Vergangenheit erfolgreich verwendet, also habe ich jetzt das Host-Eclipse-Projekt in den Arbeitsbereich meines aktuellen Projekts kopiert und die entsprechenden Namensänderungen vorgenommen. Das Merkwürdige ist, dass ich beim Bauen des Host-Projekts nun folgende Meldung bekomme:

Erstellungsziel: fft2d_host
Aufruf: GCC C Linker
gcc -L/opt/adapteva/esdk/tools/host/x86_64/lib -o “fft2d_host” ./src/fft2d_host.o -le_host -lrt

./src/fft2d_host.o: In Funktion `main’:

fft2d_host.c:(.text+0x280): Verschiebung abgeschnitten, um zu passen: R_X86_64_PC32 gegen das Symbol „Mailbox“, definiert im COMMON-Abschnitt in ./src/fft2d_host.o

Was bedeutet dieser Fehler und warum baut er nicht auf dem aktuellen Projekt auf, während es mit dem älteren Projekt in Ordnung ist?

Benutzeravatar von Chris Stratton
Chris Stratton

Sie versuchen, Ihr Projekt so zu verknüpfen, dass das Ziel einer relativen Adressierung weiter entfernt ist, als dies mit der 32-Bit-Verschiebung des gewählten relativen Adressierungsmodus unterstützt werden kann. Dies könnte daran liegen, dass das aktuelle Projekt größer ist, Objektdateien in einer anderen Reihenfolge verknüpft werden oder dass ein unnötig umfangreiches Zuordnungsschema im Spiel ist.

Diese Frage ist ein perfektes Beispiel dafür, warum es oft produktiv ist, eine Websuche nach dem allgemeinen Teil einer Fehlermeldung durchzuführen – Sie finden Dinge wie diese:

http://www.technovelty.org/code/c/relocation-truncated.html

Welche bietet einige heilende Anregungen.

  • Hier ist ein Vorschlag: Möglicherweise erstellen Sie versehentlich 64-Bit-Objekte ohne -fPIC. Das hat mich eine Zeit lang gefesselt.

    – Konrad Meyer

    9. November 2015 um 6:07 Uhr

  • Eine Websuche nach dem generischen Teil der Fehlermeldung brachte mich hierher!

    – MM

    14. Mai 2020 um 3:54 Uhr

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

Minimales Beispiel, das den Fehler generiert

main.S bewegt sich ein die Anschrift hinein %eax (32-Bit).

Netz

_start:
    mov $_start, %eax

linker.ld

SECTIONS
{
    /* This says where `.text` will go in the executable. */
    . = 0x100000000;
    .text :
    {
        *(*)
    }
}

Kompilieren auf x86-64:

as -o main.o main.S
ld -o main.out -T linker.ld main.o

Resultat von ld:

(.text+0x1): relocation truncated to fit: R_X86_64_32 against `.text'

Denk daran, dass:

  • as setzt alles auf die .text wenn kein anderer Abschnitt angegeben ist
  • ld nutzt die .text als Standardeinstiegspunkt, wenn ENTRY. Daher _start ist das allererste Byte von .text.

So beheben Sie das Problem: Verwenden Sie dies linker.ld stattdessen und subtrahiere 1 von Anfang an:

SECTIONS
{
    . = 0xFFFFFFFF;
    .text :
    {
        *(*)
    }
}

Anmerkungen:

  • können wir nicht machen _start global in diesem Beispiel mit .global _start, sonst schlägt es immer noch fehl. Ich denke, das passiert, weil globale Symbole Ausrichtungsbeschränkungen haben (0xFFFFFFF0 funktioniert). TODO wo ist das im ELF-Standard dokumentiert?

  • das .text Segment hat auch eine Ausrichtungsbeschränkung von p_align == 2M. Aber unser Linker ist schlau genug, um das Segment zu platzieren 0xFFE00000mit Nullen auffüllen bis 0xFFFFFFFF und einstellen e_entry == 0xFFFFFFFF. Dies funktioniert, generiert jedoch eine übergroße ausführbare Datei.

Getestet auf Ubuntu 14.04 AMD64, Binutils 2.24.

Erläuterung

Zuerst müssen Sie anhand eines Minimalbeispiels verstehen, was ein Umzug ist: https://stackoverflow.com/a/30507725/895245

Als nächstes werfen Sie einen Blick auf objdump -Sr main.o:

0000000000000000 <_start>:
   0:   b8 00 00 00 00          mov    $0x0,%eax
                        1: R_X86_64_32  .text

Wenn wir uns ansehen, wie Anweisungen im Intel-Handbuch codiert sind, sehen wir Folgendes:

  • b8 sagt, dass dies eine ist mov zu %eax
  • 0 ist ein sofortiger Wert, zu dem verschoben werden soll %eax. Relocation ändert es dann so, dass es die Adresse von enthält _start.

Beim Wechseln zu 32-Bit-Registern muss das Direktregister ebenfalls 32-Bit sein.

Aber hier muss der Umzug diese 32-Bit ändern, um die Adresse zu setzen _start in sie ein, nachdem die Verknüpfung erfolgt ist.

0x100000000 passt nicht in 32-Bit, aber 0xFFFFFFFF tut. Daher der Fehler.

Dieser Fehler kann nur bei Verschiebungen auftreten, die eine Kürzung erzeugen, z R_X86_64_32 (8 Byte bis 4 Byte), aber nie an R_X86_64_64.

Und es gibt einige Arten von Umzügen, die erforderlich sind Schild Verlängerung statt Nullverlängerung wie hier gezeigt, zB R_X86_64_32S. Siehe auch: https://stackoverflow.com/a/33289761/895245

R_AARCH64_PREL32

Gefragt bei: Wie kann verhindert werden, dass “main.o:(.eh_frame+0x1c): relocation truncated to fit: R_AARCH64_PREL32 against `.text'” beim Erstellen eines aarch64-Baremetal-Programms?

  • Wenn Sie eine Assembly schreiben, können Sie die von objdump bereitgestellten Informationen auch mit einer Assembly-Listing-Datei abrufen. Fügen Sie -al=(file) wie folgt in Ihren Assembly-Befehl ein: as ... test.s -al=test.lst Überlegen Sie auch, was der Ursprung für eine relative Adresse ist. In manchen Fällen ist es nicht die Adresse des Befehls, es könnte die Adresse des nächsten Befehls sein. In der 6502-Assembly wird BEQ $ aus diesem Grund als F0 FE kodiert.

    – Cardiff-Weltraummann

    12. Mai 2018 um 18:56 Uhr


Auf Cygwin -mcmodel=medium ist bereits Standard und hilft nicht. Zu mir hinzufügen -Wl,--image-base -Wl,0x10000000 to GCC Linker hat den Fehler behoben.

  • @garyp Ich weiß nicht, wie Midenok es herausgefunden hat, aber ich sehe das 0x1000 0000 ist die MS-Standardbasisadresse für eine 32-Bit-DLL, wohingegen 0x1 8000 0000 ist die Standardeinstellung für 64-Bit-DLLs: MS-Linker /BASE-Optionsdokumente

    – cxw

    9. Juli 2018 um 12:04 Uhr


Benutzeravatar von Rufflewind
Rüschenwind

Ich bin auf dieses Problem gestoßen, als ich ein Programm erstellt habe, das sehr viel Stapelspeicher benötigt (über 2 GiB). Die Lösung bestand darin, die Flagge hinzuzufügen -mcmodel=mediumdas sowohl von GCC- als auch von Intel-Compilern unterstützt wird.

Benutzeravatar von twol
zwöl

Oft bedeutet dieser Fehler Ihr Programm ist zu groß, und oft ist es zu groß, weil es ein oder mehrere sehr große Datenobjekte enthält. Zum Beispiel,

char large_array[1ul << 31];
int other_global;
int main(void) { return other_global; }

erzeugt unter x86-64/Linux den Fehler „Relocation truncated to fit“, wenn es im Standardmodus und ohne Optimierung kompiliert wird. (Wenn Sie die Optimierung einschalten, könnte es das zumindest theoretisch herausfinden large_array ist unbenutzt bzw other_global wird nie geschrieben und generiert daher Code, der das Problem nicht auslöst.)

Was los ist, ist, dass GCC standardmäßig sein “kleines Codemodell” auf dieser Architektur verwendet, bei der der gesamte Code des Programms und die statisch zugewiesenen Daten in die untersten 2 GB des Adressraums passen müssen. (Die genaue Obergrenze liegt bei etwa 2 GB – 2 MB, da die untersten 2 MB des Adressraums eines Programms dauerhaft unbrauchbar sind. Wenn Sie eine gemeinsam genutzte Bibliothek oder eine positionsunabhängige ausführbare Datei kompilieren, müssen der gesamte Code und die Daten dennoch in zwei passen Gigabyte, aber sie werden nicht mehr an den unteren Rand des Adressraums genagelt.) large_array verbraucht den ganzen Platz von selbst, also other_global wird eine Adresse über dem Limit zugewiesen und der Code für generiert main kann es nicht erreichen. Sie erhalten vom Linker einen kryptischen Fehler statt eines hilfreichen “large_array ist zu groß” vom Compiler, da der Compiler das in komplexeren Fällen nicht wissen kann other_global wird außer Reichweite sein, also wird es nicht einmal für die einfachen Fälle versucht.

Meistens besteht die richtige Antwort auf diesen Fehler darin, Ihr Programm so umzugestalten, dass es keine gigantischen statischen Arrays und/oder Gigabyte an Maschinencode benötigt. Wenn Sie sie jedoch aus irgendeinem Grund wirklich haben müssen, können Sie die verwenden “mittlere” oder “große” Codemodelle Grenzen aufzuheben, um den Preis einer etwas weniger effizienten Codegenerierung. Diese Codemodelle sind x86-64-spezifisch; Ähnliches existiert für die meisten anderen Architekturen, aber der genaue Satz von “Modellen” und die damit verbundenen Grenzen variieren. (Auf einer 32-Bit-Architektur könnten Sie beispielsweise ein “kleines” Modell haben, bei dem die Gesamtmenge an Code und Daten auf etwa 2 begrenzt war24 Bytes.)

  • Vielen Dank. Wie in der Frage erwähnt, handelt es sich nicht um einen x86-Code. Das ist lange her, aber ich bin mir sowieso nicht sicher, wie die Größe des Codemodells mit einem eingebetteten Prozessor zusammenhängt. War das nicht eine x86-spezifische Einschränkung/Eigenschaft?

    – jap

    9. November 2017 um 20:43 Uhr

  • @ysap Jede CPU-Architektur hat Einschränkungen wie this – wird normalerweise dadurch festgelegt, wie viele Bits in den unmittelbaren Operanden eines Maschinenbefehls passen. Ich habe diese Antwort geschrieben, weil eine viel neuere Frage als Duplikat dieser Frage geschlossen wurde und ich nicht der Meinung war, dass die vorhandenen Antworten das Problem dieser Person sehr gut ansprechen, und ich habe x86-64 nur als praktisches Beispiel verwendet. (Die Fehlermeldung in der Frage enthält den Begriff R_X86_64_PC32, was sich jedoch so anhört, als würden Sie Code für x86-64 kompilieren. Vielleicht war Ihr eigentliches Problem, dass Ihr Makefile nicht den Cross-Compiler aufrief, den es haben sollte.)

    – zol

    9. November 2017 um 21:26 Uhr


  • Ich habe sicher Cross-Compiling gemacht, aber hey, selbst diese Firma existiert nicht mehr … Danke für den Beitrag.

    – jap

    9. November 2017 um 22:49 Uhr

Bartons Benutzeravatar
Barton

Denken Sie daran, Fehlermeldungen der Reihe nach anzugehen. In meinem Fall war der Fehler darüber “undefinierte Referenz”, und ich habe ihn visuell zu dem interessanteren Fehler “Umzug abgeschnitten” übersprungen. Tatsächlich war mein Problem eine alte Bibliothek, die die Meldung “undefinierte Referenz” verursachte. Nachdem ich das behoben hatte, verschwand auch die “Verlagerung abgeschnitten”.

  • Vielen Dank. Wie in der Frage erwähnt, handelt es sich nicht um einen x86-Code. Das ist lange her, aber ich bin mir sowieso nicht sicher, wie die Größe des Codemodells mit einem eingebetteten Prozessor zusammenhängt. War das nicht eine x86-spezifische Einschränkung/Eigenschaft?

    – jap

    9. November 2017 um 20:43 Uhr

  • @ysap Jede CPU-Architektur hat Einschränkungen wie this – wird normalerweise dadurch festgelegt, wie viele Bits in den unmittelbaren Operanden eines Maschinenbefehls passen. Ich habe diese Antwort geschrieben, weil eine viel neuere Frage als Duplikat dieser Frage geschlossen wurde und ich nicht der Meinung war, dass die vorhandenen Antworten das Problem dieser Person sehr gut ansprechen, und ich habe x86-64 nur als praktisches Beispiel verwendet. (Die Fehlermeldung in der Frage enthält den Begriff R_X86_64_PC32, was sich jedoch so anhört, als würden Sie Code für x86-64 kompilieren. Vielleicht war Ihr eigentliches Problem, dass Ihr Makefile nicht den Cross-Compiler aufrief, den es haben sollte.)

    – zol

    9. November 2017 um 21:26 Uhr


  • Ich habe sicher Cross-Compiling gemacht, aber hey, selbst diese Firma existiert nicht mehr … Danke für den Beitrag.

    – jap

    9. November 2017 um 22:49 Uhr

Ich kann mich irren, aber meiner Erfahrung nach gibt es einen anderen möglichen Grund für den Fehler, wobei die Hauptursache eine Compiler- (oder Plattform-) Einschränkung ist, die leicht zu reproduzieren und zu umgehen ist. Als nächstes das einfachste Beispiel

  1. Definieren Sie ein Array von 1 GB mit:
    char a[1024 x 1024 x 1024];

Ergebnis: es funktioniert, keine Warnungen. Kann natürlich 1073741824 anstelle des Dreifachprodukts verwenden

  1. Verdoppeln Sie das vorherige Array:
    char a[2 x 1024 x 1024 x 1024];

Ergebnis in GCC: “Fehler: Größe des Arrays ‘a’ ist negativ” => Das ist ein Hinweis darauf, dass das akzeptierte/erwartete Array-Argument vom Typ ist unterzeichnet int

  1. Formulieren Sie auf der Grundlage des Vorhergehenden das Argument:
    char a[(unsigned)2 x 1024 x 1024 x 1024];

Ergebnis: Fehler Verlagerung passend gekürzt erscheint zusammen mit dieser Warnung: “Ganzzahlüberlauf in Ausdruck vom Typ ‘int'”

  1. Problemumgehung: Verwenden Sie dynamischen Speicher. Die Funktion malloc() nimmt ein Argument vom Typ size_t, das eine Typdefinition von ist unsigned lang lang int wodurch die Begrenzung vermieden wird

Dies war meine Erfahrung mit GCC unter Windows. Nur meine 2 Cent.

  • Willkommen bei SO und danke für deine Antwort! Die Frage wurde vor etwa 10 Jahren gestellt, und ich bin jetzt weit von dieser Aufgabe entfernt, daher kann ich Ihre Antwort nicht bewerten. Bitte seien Sie ermutigt, weitere Antworten, Fragen und Kommentare zu posten. Andere Benutzer zum Lesen und Kommentieren aufrufen.

    – jap

    27. Mai um 20:46 Uhr

1415800cookie-checkWas bedeutet dieser GCC-Fehler “… relocation truncated to fit…”?

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

Privacy policy