Les variables initialisées à zéro section .bss occupent-elles de l'espace dans le fichier elf?

StackOverflow https://stackoverflow.com/questions/610682

  •  03-07-2019
  •  | 
  •  

Question

Si je comprends bien, la section .bss des fichiers ELF est utilisée pour allouer de l'espace aux variables initialisées à zéro. Notre chaîne d’outils génère des fichiers ELF, d’où ma question: la section .bss doit-elle contenir tous ces zéros? Cela semble être un tel gaspillage d'espaces que lorsque, par exemple, j'alloue un tableau global de dix mégaoctets, le fichier ELF contient dix mégaoctets de zéros. Qu'est-ce que je vois mal ici?

Était-ce utile?

La solution

Cela fait quelque temps que je travaille avec ELF. Mais je pense que je me souviens encore de ce genre de choses. Non, il ne contient pas physiquement ces zéros. Si vous regardez dans un en-tête de programme de fichier ELF, vous verrez que chaque en-tête a deux chiffres: l'un est la taille du fichier. Et un autre est la taille de la section lorsqu'elle est allouée 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 ceux qui sont copiés dans la mémoire virtuelle lorsque le fichier est chargé pour exécution. D'autres en-têtes contiennent d'autres informations, telles que les bibliothèques partagées nécessaires. Comme vous le voyez, les éléments FileSize et MemSiz diffèrent considérablement pour l'en-tête qui contient la section bss (le deuxième LOAD un):

0x00104 (file-size) 0x61bac (mem-size)

Pour cet exemple de code:

int a[100000];
int main() { }

La spécification ELF indique que la partie d'un segment dont la taille de mémoire est supérieure à la taille de fichier est simplement renseignée avec des zéros dans la mémoire virtuelle. Le mappage segment à section du deuxième en-tête LOAD est le suivant:

03     .ctors .dtors .jcr .dynamic .got .got.plt .data .bss

Donc, il y a aussi d'autres sections. Pour les constructeurs / destructeurs C ++. La même chose pour Java. Ensuite, il contient une copie de la section .dynamic et d'autres éléments utiles pour la liaison dynamique (je pense que c'est l'emplacement contenant les bibliothèques partagées nécessaires, entre autres éléments). Après cela, la section .data qui contient les globales initialisées et les variables statiques locales. À la fin, la section .bss apparaît. Elle est remplie par des zéros au moment du chargement car la taille du fichier ne le couvre pas.

En passant, vous pouvez voir dans quelle section de sortie un symbole particulier va être placé en utilisant l'option de l'éditeur de liens -M . Pour gcc, vous utilisez -Wl, -M pour transmettre l’option à l’éditeur de liens. L'exemple ci-dessus montre que un est alloué dans .bss . Cela peut vous aider à vérifier que vos objets non initialisés se retrouvent vraiment dans .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 conserve par défaut les éléments globaux non initialisés dans une section COMMON, par souci de compatibilité avec les anciens compilateurs, ce qui permet de définir les éléments globaux deux fois dans un programme sans erreurs de définition multiples. Utilisez -fno-common pour que GCC utilise les sections .bss pour les fichiers objet (ne fait aucune différence pour l'exécutable lié final, car, comme vous le voyez, il sera de toute façon inséré dans une section de sortie .bss Ceci est contrôlé par le script de l'éditeur de liens . Affichez-le avec ld -verbose ). Mais cela ne devrait pas vous effrayer, c'est juste un détail interne. Voir la page de manuel de gcc.

Autres conseils

La section .bss d'un fichier ELF est utilisée pour les données statiques qui ne sont pas initialisées par programme mais dont la garantie est de définir la valeur zéro lors de l'exécution. Voici un petit exemple qui expliquera 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 le .bss puisqu'il n'est pas initialisé. bss_test2 est toutefois placé dans le segment .data avec un groupe de zéros. Le chargeur d’exécution alloue en principe la quantité d’espace réservé au .bss et le met à zéro avant l’exécution de tout code utilisateur.

Vous pouvez voir la différence en utilisant objdump , nm ou des utilitaires similaires:

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 généralement l’une des premières surprises auxquelles se heurtent les développeurs intégrés: ne jamais initialiser la statique de manière explicite. Le chargeur d'exécution (généralement) s'en occupe. Dès que vous initialisez quoi que ce soit explicitement, vous demandez au compilateur / lieur d'inclure les données dans l'image exécutable.

Une section .bss n'est pas stockée dans un fichier exécutable. Parmi les sections les plus courantes ( .text , .data , .bss ), seul le .text (code réel ) et .data (données initialisées) sont présents dans un fichier ELF.

C'est exact. Le fichier .bss n'est pas présent physiquement dans le fichier. Seules les informations sur sa taille sont présentes pour que le chargeur dynamique attribue la section .bss au programme d'application. La règle thumb étant uniquement LOAD, TLS Segment récupère la mémoire du programme d’application, les autres sont utilisés pour le chargeur dynamique.

À propos des fichiers exécutables statiques, les sections bss se voient également attribuer un espace dans l'exécutable

Application intégrée où il n'y a pas de chargeur, c'est courant.

Suman

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top