Le code/données statiques aux adresses basses, la pile aux adresses hautes, est le modèle traditionnel . x86-64 suit cela ; i386 était le plus inhabituel. (Avec "le tas" au milieu, même si ce n'est pas une chose réelle dans asm ; il y a .data/.bss au-dessus de .text, brk
ajoutant plus d'espace juste après .bss, et mmap choisissant des adresses aléatoires entre les deux).
La disposition de l'i386 laissait de la place pour placer la pile sous le code, mais les Linux modernes ne le font pas de toute façon. Vous avez toujours des adresses de pile comme 0xffffe000
en code 32 bits ( par exemple, sous un noyau 64 bits ). Je ne suis pas sûr de l'endroit où une construction moderne d'un noyau 32 bits placerait les piles de l'espace utilisateur. Bien sûr, cela ne concerne que la pile du thread principal ; les piles des nouveaux threads doivent être allouées manuellement, généralement avec mmap.
Pourquoi 0x400000 (4 MiB) spécifiquement pour le programme ld
l'adresse de base par défaut ?
Suffisamment élevé pour éviter mmap_min_addr
(par défaut 64k) et laisser un espace pour que la déréférence NULL soit toujours susceptible d'échouer bruyamment, au lieu de lire silencieusement le code. Même si c'est comme ptr[i]
avec quelques grands i
. Mais sinon, le bas de l'espace d'adressage virtuel est un bon endroit,
Aussi pour optimiser les tables de pages : ils sont un arbre radix clairsemé (diagramme en cette réponse ). Idéalement, les pages utilisées partagent autant de niveaux supérieurs de l'arbre que possible, de sorte que les niveaux supérieurs de l'arbre ont principalement des entrées "non présentes". Le noyau a moins à allouer et à gérer, et le marcheur de table de pages HW peut mettre en cache interne les entrées de niveau supérieur (cache PDE) pour accélérer les manques TLB dans les pages 4k lorsqu'elles sont dans la même région de 2M, 1G ou 512G. Et le(s) page-walker(s) accède à la mémoire par le biais du cache Par conséquent, des tables de pages plus petites signifient également une réduction de l'encombrement du cache pour ces accès.
0x400000 = 4MiB. C'est le début d'un groupe de pages de 2MiB près du début de l'espace d'adressage virtuel de 1GiB. Ainsi, un exécutable avec un code plus important et/ou des données statiques qui ont besoin de plusieurs pages les aura toutes dans le même sous-arbre des tables de pages, touchant aussi peu que possible les différentes régions de 1G et 2M.
Bien, presque le moins de régions 1G possible : à partir de 0x40000000
(1 GiB) l'aurait placé au tout début d'une région de 1GiB, sans sauter les deux premières grandes pages de 2MiB de celle-ci. Mais cela n'a d'importance que si la taille de vos données statiques était juste en dessous de 1GiB, sinon vous tenez toujours dans la première région de 1GiB d'énorme page, ou vous vous étendez dans la 2ème de toute façon.
0 votes
uclibc.org/docs/psABI-x86_64.pdf est la dernière version (0.99.7), selon le Wiki OSDev .
0 votes
0x400000 représente 4MiB, ce qui pourrait avoir un rapport avec la prise en charge d'énormes pages. La section 3.3.3 n'autorise la taille des pages que jusqu'à 64KiB.
0 votes
@ivan_pozdeev : github.com/hjl-tools/x86-psABI/wiki/X86-psABI a des liens vers des PDFs construits à partir de la révision git HEAD des sources de LaTeX. Les tables de pages x86-64 peuvent utiliser des hugepages de 2MB (et même 1GiB), et Linux le fait de manière transparente/opportuniste pour la mémoire anonyme, mais pas pour le mappage par fichier. Je pense que 4MB est suffisamment éloigné de 0 pour qu'un déréférencement par pointeur NULL indexé ne soit pas indexé dans des pages valides. Heh, je vois que vous avez dit la même chose dans votre réponse.
0 votes
Pourquoi pas ? Pourquoi une autre adresse devrait-elle être choisie ?