Requête sur-ffunction-section & -fdata-sections options de gcc
mentionnés ci-dessous dans le CCAG Page pour la fonction des sections et des sections de données options:
-ffunction-sections -fdata-sections
Place chaque fonction ou élément de données dans sa propre section dans le fichier de sortie si la cible supporte des sections arbitraires. Le nom de la fonction ou le nom de l'élément de données qui détermine la partie du nom dans le fichier de sortie. Utilisez ces options sur les systèmes où le linker peut effectuer des optimisations pour améliorer la localisation de référence dans l'espace d'instruction. La plupart des les systèmes utilisant le format d'objet ELF et les processeurs SPARC fonctionnant avec Solaris 2 ont des linkers avec de telles optimisations. AIX pourrait avoir ces optimisations à l'avenir.
n'utilisez ces options que lorsqu'il y a des avantages significatifs à le faire. lorsque vous spécifiez ces options, l'assembleur et l'éditeur de liens créeront des fichiers objets et exécutables plus gros et seront également plus lents. vous ne pourrez pas utiliser gprof sur tous les systèmes si vous spécifiez cette option et vous pouvez avoir des problèmes avec le débogage si vous spécifiez cette option et -g.
j'avais l'impression que ces options aideraient à réduire la taille du fichier exécutable. Pourquoi cette page dit-elle qu'elle va créer des fichiers exécutables plus gros? Ai-je raté quelque chose?
5 réponses
lorsque vous utilisez ces options de compilateur, vous pouvez ajouter l'option de linker -Wl,--gc-sections
qui supprimera tout le code non utilisé.
fait intéressant, en utilisant -fdata-sections
peut rendre les piscines littérales de vos fonctions, et donc vos fonctions elles-mêmes plus grandes. J'ai remarqué cela sur ARM en particulier, mais il est probable que ce soit vrai ailleurs. Le binaire que je testais n'a augmenté que d'un quart de pour cent, mais il a augmenté. En regardant le démontage des fonctions modifiées, il était clair pourquoi.
si toutes les entrées BSS (ou données) de votre fichier objet sont affectées à une seule section alors le compilateur peut stocker l'adresse de cette section dans les fonctions literal pool et générer des charges avec des décalages connus à partir de cette adresse dans la fonction pour accéder à vos données. Mais si vous activez -fdata-sections
il place chaque morceau de données BSS (ou de données) dans sa propre section, et comme il ne sait pas laquelle de ces sections pourrait être des ordures collectées plus tard, ou quel ordre le linker placera toutes ces sections dans l'image finale exécutable, il ne peut plus charger de données en utilisant des offsets d'une seule adresse. 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 le linker a compris ce qui va dans l'image finale et où, alors il peut aller et réparer ces entrées de pool littéral avec l'adresse réelle des données.
Donc oui, même avec -Wl,--gc-sections
l'image résultante peut être plus grande parce que le texte de la fonction réelle est plus grand.
ci-dessous j'ai ajouté un exemple minimal
le code ci-dessous est suffisant pour voir le comportement que je suis parle. S'il vous plaît ne vous laissez pas déconcerter par la déclaration volatile et l'utilisation de variables globales, qui sont toutes deux discutables en 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)
D'abord, 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
arm-none-eabi-nm
nous voyons que queue_empty est de 20 octets de long (14 hex), et le arm-none-eabi-objdump
sortie montre qu'il y a un seul mot de déplacement à la fin de la fonction, c'est l'adresse de la section BSS (la section pour les 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, compensant par 0 et 4 octets respectivement. Ces deux charges sont les charges des valeurs de tête et de queue. Nous pouvons voir ces décalages dans la première colonne de la sortie de arm-none-eabi-nm
. nop
à la fin de la la fonction est-à-mot aligner l'adresse de l'littérale de la piscine.
nous verrons ensuite ce qui se passe lorsque les sections-fdata sont ajoutées.
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
nous voyons immédiatement que la longueur de queue_empty a augmenté de quatre octets à 24 octets (18 hex), et qu'il y a maintenant deux réinstallations à faire dans le pool littéral de queue_empty. Ces réinstallations correspondent aux adresses des deux sections BSS qui ont été créées, une pour chaque variable globale. Il doit y avoir deux adresses ici parce que le compilateur ne peut pas connaître la position relative que le linker finira par mettre les deux sections. En regardant les instructions au début de queue_empty, nous voyons qu'il y a une charge supplémentaire, le compilateur doit générer de chargement séparé paires pour obtenir l'adresse de la section, puis la valeur de la variable dans cette section. L'instruction supplémentaire dans cette version de jeu_empty ne rend pas le corps de la fonction plus long, il prend juste l'endroit qui était auparavant un nop, mais ce ne sera pas le cas en général.
Vous pouvez utiliser -ffunction-sections
et -fdata-sections
sur les bibliothèques statiques, ce qui va augmenter la taille de la bibliothèque statique, car chaque fonction et variable de données globales seront placées dans une section séparée.
puis utiliser -Wl,--gc-sections
sur le programme de liaison avec cette bibliothèque statique, qui supprimera les sections inutilisées.
ainsi, le binaire final sera plus petit thant sans ces drapeaux.
attention,-Wl,--gc-sections
peut casser des choses.
j'obtiens de meilleurs résultats en ajoutant une étape supplémentaire et en construisant un .a
archive:
- premièrement, gcc et G++ sont utilisés avec
-ffunction-sections
-fdata-sections
drapeaux - ensuite, tous les
.o
les objets sont placés dans un.a
archive avecar rcs file.a *.o
- enfin, le linker est appelé avec
-Wl,-gc-sections,-u,main
options - pour tous, l'optimisation est défini à
-Os
.
Je l'ai essayé il y a quelques temps et en regardant les résultats, il semble que l'augmentation de taille vient de l'ordre des objets avec un alignement différent. Normalement, le linker trie les objets pour garder le rembourrage entre eux petit, mais il semble que cela ne fonctionne qu'à l'intérieur d'une section, pas à travers les différentes sections. Ainsi, vous obtenez souvent un rembourrage supplémentaire entre les sections de données pour chaque fonction augmentant l'espace global.
pour une Liv statique avec-Wl,-gc-sections le retrait de la section non utilisée vais probablement faire plus de place pour la petite augmentation.