sub rsp, <size>
pour réserver de l'espace dans la pile avant d'y toucher, si vous utilisez plus de 128 octets sous RSP.
Quand il plante, regardez votre carte mémoire de processus. Vous utilisez peut-être de la mémoire si loin en dessous de RSP que le noyau n'augmente pas le mappage de la pile et qu'il s'agit donc simplement d'un accès ordinaire à une page non mappée =erreur de page non valide => le noyau fournit SIGSEGV.
(L'ABI ne définit qu'une zone rouge de 128 octets, mais en pratique, la seule chose qui peut obstruer cette mémoire est un gestionnaire de signal (que vous n'avez pas installé) ou GDB exécutant print some_func()
en utilisant la pile de votre programme pour appeler une fonction dans votre programme.)
Normalement, Linux est assez disposé à développer le mappage de pile sans toucher aux pages intermédiaires, mais vérifie apparemment la valeur de RSP. Normalement, vous déplacez RSP au lieu d'utiliser simplement la mémoire bien en dessous du pointeur de pile (car il n'y a aucune garantie que ce soit sûr). Voir Comment la mémoire de la pile est-elle allouée lors de l'utilisation d'instructions 'push' ou 'sub' x86 ?
Autre doublon :quelle exception peut être générée lors de la soustraction du registre ESP ou RSP ? (pile croissante) où l'utilisation de sub rsp, 5555555
avant de toucher la nouvelle pile de mémoire était suffisante.
Stack ASLR peut démarrer RSP à différents endroits par rapport à une limite de page , de sorte que vous pourriez à peine vous en sortir parfois. Linux mappe initialement 132 Ko d'espace de pile , et qui inclut l'espace pour l'environnement et les arguments sur la pile à l'entrée de _start
. Votre 128 Kio est très proche de cela, il est donc tout à fait plausible que cela fonctionne parfois de manière aléatoire.
Et BTW, il n'y a aucune raison de copier réellement la mémoire dans l'espace utilisateur, surtout pas 1 octet à la fois. Passez simplement la même adresse au write
.
Ou au moins filtrez sur place si possible, afin que votre empreinte de cache soit plus petite.
De plus, la manière normale de charger un octet est movzx eax, byte [mem]
. Utilisez uniquement mov al, [mem]
si vous souhaitez spécifiquement fusionner avec l'ancienne valeur de RAX. Sur certains processeurs, mov
à al
a une fausse dépendance sur l'ancienne valeur que vous pouvez casser en écrivant le registre complet.
Et BTW, si votre programme toujours utilise cet espace, autant l'allouer statiquement dans le BSS. Cela rend possible un adressage indexé plus efficace si vous choisissez d'assembler un exécutable dépendant de la position (non-PIE).
La zone rouge dans amd64 ne fait que 128 octets de long, mais vous utilisez 131072 octets sous rsp. Déplacez le pointeur de la pile vers le bas pour englober les tampons que vous souhaitez stocker sur la pile.