Si je comprends bien, l' .bss
section ELFE de fichiers est utilisé pour allouer de l'espace pour initialisé à zéro des variables. Notre outil de la chaîne de produit ELFE de fichiers, d'où ma question: est ce que l' .bss
section ont en fait pour contenir tous ces zéros? C'est un terrible gaspillage d'espaces que quand, par exemple, j'alloue un mondial des dix mégaoctets tableau, il en résulte une dizaine de mégaoctets de zéros dans le fichier ELF. Que suis-je voir le problème ici?
Réponses
Trop de publicités?A été un certain temps depuis que j'ai travaillé avec ELF. Mais je pense que je me souviens encore de ce genre de choses. Non, il n'est pas physiquement contiennent ces zéros. Si vous regardez dans un ELFE fichier d'en-tête du programme, puis vous verrez de chaque en-tête a deux nombres: l'Un est la taille du fichier. Et l'autre est la taille de la section a quand alloué dans la mémoire virtuelle (readelf -l ./a.out
):
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
PHDR 0x000034 0x08048034 0x08048034 0x000e0 0x000e0 R E 0x4
INTERP 0x000114 0x08048114 0x08048114 0x00013 0x00013 R 0x1
[Requesting program interpreter: /lib/ld-linux.so.2]
LOAD 0x000000 0x08048000 0x08048000 0x00454 0x00454 R E 0x1000
LOAD 0x000454 0x08049454 0x08049454 0x00104 0x61bac RW 0x1000
DYNAMIC 0x000468 0x08049468 0x08049468 0x000d0 0x000d0 RW 0x4
NOTE 0x000128 0x08048128 0x08048128 0x00020 0x00020 R 0x4
GNU_STACK 0x000000 0x00000000 0x00000000 0x00000 0x00000 RW 0x4
Les en-têtes de type LOAD
sont celle qui sont copiés dans la mémoire virtuelle lorsque le fichier est chargé de l'exécution. D'autres en-têtes contiennent d'autres informations, comme les bibliothèques partagées qui sont nécessaires. Comme vous le voyez, l' FileSize
et MemSiz
différer de façon importante de l'en-tête qui contient l' bss
section (le deuxième LOAD
- en un):
0x00104 (file-size) 0x61bac (mem-size)
Pour cet exemple de code:
int a[100000];
int main() { }
L'ELFE de la spécification indique que la partie d'un segment de la mem-taille est plus grande que la taille de fichier est juste rempli avec des zéros dans la mémoire virtuelle. Le segment de la section de la cartographie de la deuxième LOAD
- tête est comme ceci:
03 .ctors .dtors .jcr .dynamic .got .got.plt .data .bss
Donc, il ya quelques autres articles de la là aussi. Pour le C++ constructeur/destructeurs. La même chose pour Java. Ensuite, il contient une copie de l' .dynamic
section et d'autres choses utiles pour la liaison dynamique (je crois que c'est l'endroit qui contient les bibliothèques partagées nécessaires parmi d'autres choses). Après que l' .data
section qui contient initialisé les variables globales et locales des variables statiques. À la fin, l' .bss
section apparaît, qui est rempli par des zéros au moment du chargement, car de taille de fichier pour ne pas le couvrir.
Par ailleurs, vous pouvez voir en sortie de section un symbole particulier va être placés par l'aide de la -M
option de l'éditeur de liens. Pour gcc, vous utilisez -Wl,-M
pour mettre l'option par le biais de l'éditeur de liens. L'exemple ci-dessus montre que l' a
est allouée dans .bss
. Il peut vous aider à vous assurer que vos objets non initialisées vraiment finir par en .bss
et pas ailleurs:
.bss 0x08049560 0x61aa0
[many input .o files...]
*(COMMON)
*fill* 0x08049568 0x18 00
COMMON 0x08049580 0x61a80 /tmp/cc2GT6nS.o
0x08049580 a
0x080ab000 . = ALIGN ((. != 0x0)?0x4:0x1)
0x080ab000 . = ALIGN (0x4)
0x080ab000 . = ALIGN (0x4)
0x080ab000 _end = .
GCC continue non initialisée globales dans une section COMMUNE par défaut, pour la compatibilité avec les anciens compilateurs, qui permettent d'avoir des variables globales définies deux fois dans un programme, sans définition de plusieurs erreurs. Utiliser -fno-common
faire GCC utiliser le .sev sections pour les fichiers de l'objet (ne pas faire une différence pour la finale exécutable lié, parce que, comme vous le voyez, c'est ce qui va devenir un .sev section de sortie de toute façon. Ceci est contrôlé par le linker script. Afficher en ld -verbose
). Mais cela ne doit pas vous faire peur, c'est juste un détail interne. Voir la page de man de gcc.
L' .bss
section dans un ELFE de fichier est utilisé pour les données statiques qui n'est pas initialisé par programme, mais la garantie d'être mis à zéro au moment de l'exécution. Voici un petit exemple qui va vous expliquer la différence.
int main() {
static int bss_test1[100];
static int bss_test2[100] = {0};
return 0;
}
Dans ce cas - bss_test1
est placé dans l' .bss
, car il est non initialisée. bss_test2
, cependant, est placé dans l' .data
segment avec un tas de zéros. Le chargeur dynamique fondamentalement alloue la quantité d'espace réservé pour l' .bss
et de zéros avant tout userland code commence à exécuter.
Vous pouvez voir la différence à l'aide de objdump
, nm
, ou d'autres utilitaires:
moozletoots$ objdump -t a.out | grep bss_test
08049780 l O .bss 00000190 bss_test1.3
080494c0 l O .data 00000190 bss_test2.4
C'est habituellement l'une des premières surprises que les développeurs de logiciels embarqués exécuter en... jamais initialiser la statique à zéro de manière explicite. Le chargeur dynamique (en général) qui s'en occupe. Dès que vous initialisez rien explicitement, en vous disant que le compilateur/linker pour inclure les données dans le fichier exécutable de l'image.