Pourquoi l'adresse 0x400000 est-elle choisie comme début de segment de texte dans x86 64 ABI?

Dans ce document sur p. 27 il est dit que le segment de texte commence à 0x400000. Pourquoi a cette adresse en particulier choisi? Est-il la raison pour cela? La même adresse est choisie dans GNU ld sur Linux :

$ ld -verbose | grep -i text-segment
  PROVIDE (__executable_start = SEGMENT_START("text-segment", 0x400000)); . = SEGMENT_START("text-segment", 0x400000) + SIZEOF_HEADERS;

c'est surprenant car cette adresse est plus grande en exécutables x86 32 bits:

$ ld -verbose | grep -i text-segment
  PROVIDE (__executable_start = SEGMENT_START("text-segment", 0x08048000)); . = SEGMENT_START("text-segment", 0x08048000) + SIZEOF_HEADERS;

j'ai lu cette question qui traite pourquoi l'adresse 0x080xxxxx a été choisie pour i386 mais cela n'explique pas un changement dans x86_64. Il est difficile de trouver aucune explication sur cette question. Quelqu'un a une idée?

30
demandé sur Peter Cordes 2016-09-25 20:04:11

1 réponses

résultat net: certaines limites techniques que amd64 a dans l'utilisation des grandes adresses suggèrent de consacrer le plus bas 2GiB de l'espace d'adresse au code et aux données pour l'efficacité. La pile a donc été déplacée hors de cette plage.


In i386 ABI 1

  • stack est situé avant le code, en croissance d'un peu moins de 0x8048000 vers le bas. Qui prévoit "un peu plus de 128 MB pour la pile et environ 2 Go pour le texte et les données " (p. 3-22).
  • segments dynamiques à partir de 0x80000000 (2GiB),
  • et le noyau occupe la" zone réservée "au sommet que la spécification permet d'être jusqu'à 1GiB , commençant au moins 0xC0000000 (p. 3-21) ( qui est ce qu'il fait typiquement ).
  • il n'est pas nécessaire que le programme principal soit indépendant de la position.
  • une implémentation n'est pas nécessaire pour saisir l'accès au pointeur nul (p. 3-21), mais il est raisonnable de s'attendre à ce qu'une partie de l'espace de pile au-dessus de 128MiB (qui est 288KiB ) soit réservée à cette fin.

amd64 ( dont L'ABI est formulé comme un amendement à l'ABI i386 (p. 9)) a un bien plus grand (48-bit) espace d'adresse mais la plupart des instructions n'acceptent que les opérandes immédiates 32-bit (qui comprennent les adresses directes et les offsets dans les instructions de saut), nécessitant plus de travail et moins de code efficace (surtout si l'on tient compte de l'interdépendance des instructions) pour traiter des valeurs plus grandes. Les mesures visant à contourner ces limites sont résumées par les auteurs en introduisant quelques "modèles de code" qu'ils recommandent d'utiliser pour "permettre au compilateur de générer un meilleur code". (p. 33)

  • plus précisément, le premier d'entre eux, "Small code model", suggère d'utiliser les adresses "dans la gamme de 0 à 2 31 -2 24 -1 ou de 0x00000000 à 0x7effffff qui permet quelques références relatives très efficaces et l'itération du tableau. C'est 1.98GiB qui est plus que suffisant pour de nombreux programmes.
  • "Moyen modèle de code" est basée sur la précédent, fractionnement les données dans une partie" rapide "sous la limite ci-dessus et la partie" plus lent " restant qui nécessite une instruction spéciale pour accéder. Alors que le code reste sous la limite.
  • et seul le modèle "large" ne fait aucune hypothèse sur les tailles, exigeant du compilateur "d'utiliser l'instruction movabs , comme dans le milieu modèle de code, même pour traiter des adresses à l'intérieur de la section de texte. En outre, indirects les branches sont nécessaires pour brancher à des adresses dont décalage du pointeur d'instruction en cours est inconnue." ils suggèrent ensuite de scinder la base de code en plusieurs bibliothèques partagées puisque ces mesures ne s'appliquent pas aux références relatives avec des offsets qui sont connus pour être dans les limites (comme décrit dans"Small position independent code model").

ainsi la pile a été déplacée sous l'espace de bibliothèque partagée ( 0x80000000000 , 128GiB ) parce que ses adresses ne sont jamais des opérandes immédiates, toujours référencées soit indirectement soit avec lea / mov d'une autre référence, donc seules les limitations relatives de décalage s'appliquent.


ce qui précède explique pourquoi l'adresse de chargement a été déplacée à une adresse inférieure. Maintenant, pourquoi a-t-il été déplacé à exactement 0x400000 ( 4MiB )? Ici, je suis venu vide ainsi, résumant ce que j'ai lu dans les spécifications ABI, Je ne peux que deviner qu'il se sentait "juste droite":

  • C'est assez grand pour attraper n'importe quel décalage de structure probablement incorrect, en tenant compte des plus grandes unités de données que amd64 opère, mais assez petit pour ne pas gaspiller beaucoup de la valeur de départ 2GiB de l'espace d'adresse.
  • il est égal à la plus grande taille de page pratique à ce jour et est un multiple de toutes les autres tailles d'Unité de mémoire virtuelle que l'on peut penser.

1 notez que les Linuxes x32 actuels ont dévié de cette disposition plus et plus au fil du temps. Mais nous parlons de L'ABI spec ici depuis le amd64 un est formellement basé sur elle plutôt que n'importe quelle disposition dérivée (voir son paragraphe pour la citation).

30
répondu ivan_pozdeev 2017-05-23 12:01:49