web-dev-qa-db-fra.com

Requête sur les options de section -function-section

Le ci-dessous mentionné dans la page GCC pour les options de sections de fonction et de sections de données:

-ffunction-sections
-fdata-sections

Placez chaque fonction ou élément de données dans sa propre section du fichier de sortie si la cible prend en charge des sections arbitraires. Le nom de la fonction ou le nom de la donnée détermine le nom de la section dans le fichier de sortie. Utilisez ces options sur les systèmes sur lesquels l'éditeur de liens peut effectuer des optimisations pour améliorer la localité de référence dans l'espace d'instructions. La plupart des systèmes utilisant le format d'objet ELF et les processeurs SPARC exécutant Solaris 2 ont des lieurs dotés de telles optimisations. AIX peut avoir ces optimisations dans le futur. 

N'utilisez ces options que lorsque cela présente des avantages significatifs. Lorsque vous spécifiez ces options, l'assembleur et l'éditeur de liens créent des objets et des fichiers exécutables plus volumineux et sont également plus lents. Vous ne pourrez pas utiliser gprof sur tous les systèmes si vous spécifiez cette option et vous pourriez avoir des problèmes de débogage si vous spécifiez à la fois cette option et -g. 

J'avais l'impression que ces options contribueraient à réduire la taille du fichier exécutable. Pourquoi cette page dit-elle qu'elle créera des fichiers exécutables plus volumineux? Est-ce que je manque quelque chose?

28
Jay

Lorsque vous utilisez ces options du compilateur, vous pouvez ajouter l'option de l'éditeur de liens -Wl,--gc-sections qui supprimera tout le code non utilisé.

24
leppie

Il est intéressant de noter que l'utilisation de -fdata-sections peut élargir les pools littéraux de vos fonctions, et donc les fonctions elles-mêmes. Je l'ai remarqué sur ARM en particulier, mais c'est probablement le cas ailleurs. Le binaire que je testais n'a augmenté que de 25%, mais il a augmenté. En regardant le démontage des fonctions modifiées, il était clair pourquoi.

Si toutes les entrées BSS (ou DATA) de votre fichier objet sont allouées à une seule section, le compilateur peut stocker l'adresse de cette section dans le pool de littéral de fonctions et générer des charges avec des décalages connus à partir de cette adresse dans la fonction pour accéder à vos données. Les données. Mais si vous activez -fdata-sections, chaque élément de données BSS (ou DATA) est inséré dans sa propre section. Comme il ne sait pas laquelle de ces sections pourrait être récupérée ultérieurement, ni dans quel ordre l'éditeur de liens placera toutes ces sections dans l’image exécutable finale, il ne peut plus charger de données en utilisant des décalages à partir d’une adresse unique. Donc, au lieu de cela, il doit allouer une entrée dans le pool littéral par données utilisées, et une fois que l'éditeur de liens a déterminé ce qui va dans l'image finale et où, il peut alors corriger ces entrées de pool littéral avec l'adresse réelle de les données.

Alors oui, même avec -Wl,--gc-sections, l'image résultante peut être plus grande car le texte de la fonction est plus grand.

Ci-dessous, j'ai ajouté un exemple minimal

Le code ci-dessous est suffisant pour voir le comportement dont je parle. Ne soyez pas rebutés par la déclaration volatile et l'utilisation de variables globales, qui sont toutes deux discutables dans le code réel. Ici, ils assurent la création de deux sections de données lorsque -fdata-sections est utilisé.

static volatile int head;
static volatile int tail;

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

La version de GCC utilisée pour ce test est:

gcc version 6.1.1 20160526 (Arch Repository)

Premièrement, sans -fdata-sections, nous obtenons ce qui suit.

> 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

Dans arm-none-eabi-nm, nous voyons que queue_empty a une longueur de 20 octets (14 hex) et la sortie de arm-none-eabi-objdump indique qu’il existe un seul mot de relocalisation à la fin de la fonction, c’est l’adresse de la section BSS (la section des données non initialisées). La première instruction de la fonction charge cette valeur (l'adresse du BSS) dans r3. Les deux instructions suivantes se chargent par rapport à r3, en décalant respectivement de 0 et 4 octets. Ces deux charges sont les charges des valeurs de head et tail. Nous pouvons voir ces décalages dans la première colonne de la sortie de arm-none-eabi-nm. La nop à la fin de la fonction consiste à Word aligner l'adresse du pool littéral.

Nous verrons ensuite ce qui se passe lorsque -fdata-sections est ajouté.

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

Immédiatement, nous voyons que la longueur de queue_empty a augmenté de quatre octets, passant à 24 octets (18 hex), et qu’il faut maintenant deux relocalisations à effectuer dans le pool littéral de queue_empty. Ces relocalisations correspondent aux adresses des deux sections BSS créées, une pour chaque variable globale. Il doit y avoir deux adresses ici car le compilateur ne peut pas connaître la position relative dans laquelle l’éditeur de liens finira par insérer les deux sections. En regardant les instructions au début de queue_empty, on voit qu’il ya une charge supplémentaire, le compilateur doit générer des paires de charge séparées pour obtenir l'adresse de la section, puis la valeur de la variable dans cette section. L'instruction supplémentaire dans cette version de queue_empty ne rallonge pas le corps de la fonction, elle prend juste la tache qui était auparavant un nop, mais ce ne sera pas le cas en général.

27
Anton Staaf

Vous pouvez utiliser -ffunction-sections et -fdata-sections sur des bibliothèques statiques, ce qui augmentera la taille de la bibliothèque statique, car chaque fonction et chaque variable de données globale seront placées dans une section distincte.

Et utilisez ensuite -Wl,--gc-sections sur le programme en liaison avec cette bibliothèque statique, ce qui supprimera les sections inutilisées.

Ainsi, le binaire final sera plus petit que sans ces drapeaux.

Soyez prudent cependant, car -Wl,--gc-sections peut casser des choses.

14
fwhacking

J'obtiens de meilleurs résultats en ajoutant une étape supplémentaire et en construisant une archive .a:

  1. d'abord, gcc et g ++ sont utilisés avec les drapeaux -ffunction-sections-fdata-sections 
  2. ensuite, tous les objets .o sont placés dans une archive .a avec ar rcs file.a *.o
  3. enfin, l'éditeur de liens est appelé avec les options -Wl,-gc-sections,-u,main 
  4. pour tous, l'optimisation est définie sur -Os.
4
Rei Vilo

Je l'ai essayé il y a quelque temps et, en regardant les résultats, il semble que l'augmentation de la taille vienne de l'ordre des objets avec un alignement différent. Normalement, l'éditeur de liens trie les objets de manière à minimiser le remplissage entre eux, mais il semble que cela ne fonctionne que dans une section et non pas dans les sections individuelles. Ainsi, vous obtenez souvent un remplissage supplémentaire entre les sections de données pour chaque fonction, ce qui augmente l'espace total.

Pour une bibliothèque statique avec -Wl, -gc-sections, la suppression des sections inutilisées rapportera probablement plus que la légère augmentation.

0
Goswin von Brederlow