Pourquoi est le point d'entrée d'exécution ELF adresse virtuelle du formulaire 0x80xxxxx et non zéro 0x0?

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

Question

Lors de son exécution, le programme va commencer à courir à partir 0x80482c0 d'adresse virtuelle. Cette adresse ne pointe pas à notre procédure de main(), mais à une procédure nommée _start qui est créé par l'éditeur de liens.

Ma recherche Google à ce jour me conduit à quelques spéculations historiques (vagues) comme ceci:

  

Il y a folklore 0x08048000 était autrefois STACK_TOP (qui est, la pile grandit vers le bas depuis près 0x08048000 vers 0) sur un port de * NIX à i386 qui a été promulguée par un groupe de Santa Cruz, en Californie. Ce fut quand 128 Mo de RAM était cher, et 4 Go de RAM était impensable.

Quelqu'un peut-il confirmer / nier?

Était-ce utile?

La solution

Comme Mads a souligné, afin de capturer La plupart des accès par des pointeurs nuls, les systèmes Unix ont tendance à faire la page à l'adresse zéro « unmapped ». Ainsi, les accès immédiatement déclencher une exception CPU, autrement dit une erreur de segmentation. Ceci est tout à fait mieux que de laisser l'application aller voyous. La table de vecteurs d'exception, cependant, peut être à une adresse, au moins sur les processeurs x86 (il y a un registre spécial pour que, chargé de l'opcode lidt).

L'adresse du point de départ fait partie d'un ensemble de conventions qui décrivent la manière dont la mémoire est disposé. L'éditeur de liens, quand il produit un binaire exécutable, doit connaître ces conventions, donc ils ne sont pas susceptibles de changer. En gros, pour Linux, les conventions de mise en page de la mémoire sont héritées des premières versions de Linux, au début des années 90. Un processus doit avoir accès à plusieurs domaines:

  • Le code doit être dans une plage qui comprend le point de départ.
  • Il doit y avoir une pile.
  • Il doit y avoir un tas, avec une limite qui augmente avec les appels système brk() et de sbrk().
  • Il doit y avoir de place pour les appels système mmap(), y compris le chargement de la bibliothèque partagée.

De nos jours, le tas, où malloc() va, est soutenu par des appels mmap() qui obtiennent des morceaux de mémoire quel que soit l'adresse du noyau juge bon. Mais dans les temps anciens, Linux était comme les systèmes Unix précédents, et son tas requis un grand espace dans un morceau sans interruption, ce qui pourrait se développer vers les adresses de plus en plus. Donc, tout ce qui était de la convention, il a dû bourrer le code et la pile vers les adresses basses, et donner à chaque partie de l'espace d'adressage après un point donné au tas.

Mais il y a aussi la pile, ce qui est généralement assez faible, mais pourrait croître de façon spectaculaire dans certaines occasions. La pile grossit vers le bas, et quand la pile est pleine, nous voulons vraiment le processus crash on pouvait s'y attendre plutôt que d'écraser des données. Donc, il devait y avoir une grande surface pour la pile, avec, au bas de cette zone, une page non cartographiées. Et voici! Il y a une page non cartographiées à l'adresse zéro, pour attraper déréférencement de pointeur nul. Par conséquent, il a été défini que la pile obtiendrait les 128 premiers Mo d'espace d'adressage, à l'exception de la première page. Cela signifie que le code devait aller après ces 128 Mo, à une adresse similaire à 0x080xxxxx.

Comme Michael souligne, « perdre » 128 Mo d'espace d'adresse était pas une grosse affaire, car l'espace d'adressage était très grande en ce qui concerne ce qui pourrait être effectivement utilisé. À ce moment-là, le noyau Linux limitait l'espace d'adressage pour un seul processus à 1 Go, sur un maximum de 4 Go permise par le matériel, et qui n'a pas été considéré comme un gros problème.

Autres conseils

Pourquoi ne pas commencer à l'adresse 0x0? Il y a au moins deux raisons:

  • Parce que l'adresse zéro est célèbre connu comme un pointeur NULL, et utilisé par les langages de programmation des pointeurs de contrôle sain d'esprit. Vous ne pouvez pas utiliser une valeur d'adresse pour que, si vous allez exécuter le code il.
  • Le contenu réel à l'adresse 0 est souvent (mais pas toujours) la table de vecteurs d'exception, et est donc pas accessible dans les modes non-privilégiés. Consultez la documentation de votre architecture spécifique.

En ce qui concerne le _start de entrypoint vs main: Si vous établissez un lien contre l'exécution C (les bibliothèques standard C), la bibliothèque enveloppe la fonction nommée main, il peut initialiser l'environnement avant main est appelé. Sous Linux, ce sont les argc et argv paramètres de l'application, les env variables, et probablement des primitives de synchronisation et serrures. Il assure également que le retour de la principale passe sur le code d'état et appelle la fonction _exit, qui met fin au processus.

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