wake-up-neo.com

Abfrage nach -function-section & -fdata-section Optionen von gcc

Die auf der GCC-Seite unten aufgeführten Optionen für die Funktionsabschnitte und Datenabschnitte:

-ffunction-sections
-fdata-sections

Platzieren Sie jede Funktion oder jedes Datenelement in einem eigenen Abschnitt in der Ausgabedatei, wenn das Ziel beliebige Abschnitte unterstützt. Der Name der Funktion oder der Name des Datenelements bestimmt den Namen des Abschnitts in der Ausgabedatei. Verwenden Sie diese Optionen auf Systemen, auf denen der Linker Optimierungen durchführen kann, um die Referenzlage im Befehlsbereich zu verbessern. Die meisten Systeme, die das ELF-Objektformat und SPARC -Prozessoren unter Solaris 2 verwenden, verfügen über Linker mit derartigen Optimierungen. AIX kann diese Optimierungen in der Zukunft haben. 

Verwenden Sie diese Optionen nur, wenn sich daraus erhebliche Vorteile ergeben. Wenn Sie diese Optionen angeben, werden vom Assembler und vom Linker größere Objekt- und ausführbare Dateien erstellt und auch langsamer. Wenn Sie diese Option angeben, können Sie gprof nicht auf allen Systemen verwenden. Wenn Sie diese Option und -g angeben, können Sie Probleme beim Debuggen haben. 

Ich hatte den Eindruck, dass diese Optionen dazu beitragen werden, die Größe der ausführbaren Datei zu reduzieren. Warum wird auf dieser Seite angegeben, dass größere ausführbare Dateien erstellt werden? Fehlt mir etwas?

28
Jay

Wenn Sie diese Compileroptionen verwenden, können Sie die Linkeroption -Wl,--gc-sections hinzufügen, mit der der gesamte nicht verwendete Code entfernt wird.

24
leppie

Interessanterweise kann die Verwendung von -fdata-sections die Literalpools Ihrer Funktionen und damit auch Ihre Funktionen selbst vergrößern. Ich habe dies insbesondere an ARM bemerkt, aber es dürfte an anderer Stelle wahr sein. Das von mir getestete Binärprogramm wuchs nur um ein Viertel, aber es wuchs. Bei der Demontage der geänderten Funktionen war klar, warum.

Wenn alle BSS- (oder DATA-) Einträge in Ihrer Objektdatei einem einzigen Abschnitt zugeordnet sind, kann der Compiler die Adresse dieses Abschnitts im Funktionsliteralpool speichern und Lasten mit bekannten Offsets von dieser Adresse in der Funktion für den Zugriff auf Ihre generieren Daten. Wenn Sie jedoch -fdata-sections aktivieren, werden alle BSS- (oder DATA-) Daten in einem eigenen Abschnitt gespeichert, und da sie nicht wissen, in welchem ​​dieser Abschnitte möglicherweise Müll gesammelt wird oder in welcher Reihenfolge der Linker all diese Abschnitte einordnet Das endgültige ausführbare Image kann Daten mit Offsets von einer einzelnen Adresse nicht mehr laden. Es muss also stattdessen ein Eintrag im Literalpool nach verwendeten Daten zugewiesen werden. Sobald der Linker herausgefunden hat, was sich in dem endgültigen Image befindet und wo, wird er diese Literalpooleinträge mit der tatsächlichen Adresse von korrigieren können die Daten.

Ja, auch mit -Wl,--gc-sections kann das resultierende Bild größer sein, da der eigentliche Funktionstext größer ist.

Unten habe ich ein minimales Beispiel hinzugefügt

Der folgende Code reicht aus, um das Verhalten zu sehen, über das ich spreche. Lassen Sie sich nicht von der flüchtigen Deklaration und der Verwendung globaler Variablen abschrecken, die beide in realem Code fragwürdig sind. Hier stellen sie sicher, dass zwei Datenabschnitte erstellt werden, wenn -fdata-section verwendet wird.

static volatile int head;
static volatile int tail;

int queue_empty(void)
{
    return head == tail;
}

Die für diesen Test verwendete GCC-Version lautet:

gcc version 6.1.1 20160526 (Arch Repository)

Erstens erhalten wir ohne -fdata-section das Folgende.

> arm-none-eabi-gcc -march=armv6-m \
                    -mcpu=cortex-m0 \
                    -mthumb \
                    -Os \
                    -c \
                    -o test.o \
                    test.c

> arm-none-eabi-objdump -dr test.o

00000000 <queue_empty>:
 0: 4b03     ldr   r3, [pc, #12]   ; (10 <queue_empty+0x10>)
 2: 6818     ldr   r0, [r3, #0]
 4: 685b     ldr   r3, [r3, #4]
 6: 1ac0     subs  r0, r0, r3
 8: 4243     negs  r3, r0
 a: 4158     adcs  r0, r3
 c: 4770     bx    lr
 e: 46c0     nop                   ; (mov r8, r8)
10: 00000000 .Word 0x00000000
             10: R_ARM_ABS32 .bss

> arm-none-eabi-nm -S test.o

00000000 00000004 b head
00000000 00000014 T queue_empty
00000004 00000004 b tail

Aus arm-none-eabi-nm sehen wir, dass queue_empty 20 Byte lang ist (14 hex), und die Ausgabe arm-none-eabi-objdump zeigt an, dass sich am Ende der Funktion ein einzelnes Versetzungswort befindet. Dies ist die Adresse des BSS-Abschnitts (der Abschnitt für nicht initialisierte Daten). Die erste Anweisung in der Funktion lädt diesen Wert (die Adresse des BSS) in r3. Die nächsten beiden Befehle werden relativ zu r3 geladen und werden um 0 bzw. 4 Byte versetzt. Diese beiden Lasten sind die Lasten der Werte von Kopf und Schwanz. Diese Offsets sehen wir in der ersten Spalte der Ausgabe von arm-none-eabi-nm. Die Variable nop am Ende der Funktion besteht darin, die Adresse des Literal-Pools in Word auszurichten.

Als Nächstes sehen wir, was passiert, wenn -fdata-section hinzugefügt wird.

arm-none-eabi-gcc -march=armv6-m \
                  -mcpu=cortex-m0 \
                  -mthumb \
                  -Os \
                  -fdata-sections \
                  -c \
                  -o test.o \
                  test.c

arm-none-eabi-objdump -dr test.o

00000000 <queue_empty>:
 0: 4b03     ldr   r3, [pc, #12]    ; (10 <queue_empty+0x10>)
 2: 6818     ldr   r0, [r3, #0]
 4: 4b03     ldr   r3, [pc, #12]    ; (14 <queue_empty+0x14>)
 6: 681b     ldr   r3, [r3, #0]
 8: 1ac0     subs  r0, r0, r3
 a: 4243     negs  r3, r0
 c: 4158     adcs  r0, r3
 e: 4770     bx    lr
    ...
             10: R_ARM_ABS32 .bss.head
             14: R_ARM_ABS32 .bss.tail

arm-none-eabi-nm -S test.o

00000000 00000004 b head
00000000 00000018 T queue_empty
00000000 00000004 b tail

Sofort sehen wir, dass die Länge von queue_empty um vier Bytes auf 24 Bytes (18 Hexadezimalwerte) gestiegen ist und dass jetzt zwei Umsiedlungen im literal-Pool von queue_empty ausgeführt werden müssen. Diese Verschiebungen entsprechen den Adressen der beiden erstellten BSS-Abschnitte, eine für jede globale Variable. Hier müssen zwei Adressen vorhanden sein, da der Compiler die relative Position, in die der Linker die beiden Abschnitte einfügen wird, nicht kennen kann. Wenn Sie die Anweisungen am Anfang von queue_empty betrachten, sehen Sie, dass der Compiler eine zusätzliche Last enthält muss separate Ladepaare generieren, um die Adresse des Abschnitts und dann den Wert der Variablen in diesem Abschnitt zu ermitteln. Die zusätzliche Anweisung in dieser Version von queue_empty verlängert den Rumpf der Funktion nicht, er nimmt nur die Stelle, die zuvor ein Nop war, aber das ist im Allgemeinen nicht der Fall.

27
Anton Staaf

Sie können -ffunction-sections und -fdata-sections für statische Bibliotheken verwenden, wodurch sich die Größe der statischen Bibliothek erhöht, da jede Funktion und globale Datenvariable in einem separaten Abschnitt abgelegt wird.

Und dann verwenden Sie -Wl,--gc-sections für das Programm, das mit dieser statischen Bibliothek verlinkt wird, wodurch nicht verwendete Abschnitte entfernt werden.

Somit ist der letzte Binärwert kleiner als ohne diese Flags.

Seien Sie vorsichtig, da -Wl,--gc-sections Dinge zerstören kann.

14
fwhacking

Ich bekomme bessere Ergebnisse, indem ich einen zusätzlichen Schritt hinzufüge und ein .a-Archiv aufbaue:

  1. zuerst werden gcc und g ++ mit -ffunction-sections-fdata-sections-Flags verwendet 
  2. dann werden alle .o-Objekte mit .a in ein ar rcs file.a *.o-Archiv gestellt.
  3. schließlich wird der Linker mit -Wl,-gc-sections,-u,main-Optionen aufgerufen 
  4. für alle ist die Optimierung auf -Os eingestellt.
4
Rei Vilo

Ich habe es vor einiger Zeit ausprobiert, und wenn ich mir die Ergebnisse anschaue, scheint es, als ob die Größenvergrößerung von der Reihenfolge der Objekte mit unterschiedlicher Ausrichtung herrührt. Normalerweise sortiert der Linker Objekte, um die Auffüllung zwischen ihnen klein zu halten, aber es sieht so aus, als würde dies nur innerhalb eines Abschnitts funktionieren, nicht in den einzelnen Abschnitten. So erhalten Sie für jede Funktion häufig zusätzliche Abstände zwischen den Datenabschnitten, wodurch der Gesamtraum vergrößert wird.

Bei einer statischen Bibliothek mit -Wl, -gc-Abschnitten wird das Entfernen von nicht verwendeten Abschnitten jedoch höchstwahrscheinlich mehr als die kleine Erhöhung ausmachen.