Comment sauvegarder les registres sur x86 64 pour une routine d'interruption de service?

je regarde un vieux code d'un projet scolaire, et en essayant de le compiler sur mon ordinateur portable j'ai rencontré quelques problèmes. Il a été écrit à l'origine pour une ancienne version 32 bits de gcc. Bref, j'essayais de convertir une partie de l'assemblage en code compatible 64 bits et j'ai eu quelques problèmes.

Voici le code original:

pusha
pushl   %ds
pushl   %es
pushl   %fs
pushl   %gs
pushl   %ss

pusha n'est pas valide en mode 64 bits. Donc, quelle serait la bonne façon de faire ceci dans x86_64 assembly alors qu'en 64 bits mode?

Il y a obtenu d'être une raison pourquoi pusha n'est pas valide en mode 64 bits, j'ai donc un sentiment manuellement en poussant tous les registres peut être pas une bonne idée.

21

4 réponses

Apprendre code qui fait ce genre de chose. Par exemple:

en fait, "pousser manuellement" le regs est le seul moyen sur AMD64 depuis PUSHA n'existe pas là-bas. AMD64 n'est pas unique à cet égard - la plupart des CPU non-x86 nécessitent des sauvegardes/restaurations registre par Registre à un moment donné.

mais si vous inspectez de près le code source référencé, vous constaterez que tous les gestionnaires d'interruptions n'ont pas besoin de sauvegarder/restaurer l'ensemble du jeu de registres, donc il y a de la place pour des optimisations.

7
répondu FrankH. 2013-05-14 02:14:38

AMD avait besoin d'espace pour ajouter de nouveaux codes Op pour REX préfixes et quelques autres nouvelles instructions quand ils ont développé les extensions x86 64 bits. Ils ont changé la signification de certains opérateurs de ces nouvelles instructions.

plusieurs des instructions étaient simplement des instructions abrégées ou n'étaient pas nécessaires. PUSHA a été l'une des victimes. On ne sait pas très bien pourquoi ils ont banni PUSHA cependant, il ne semble pas y avoir de chevauchement entre les nouvelles instruction opcodes. Peut-être qu'ils sont réservés à l' PUSHA et POPA opcodes pour une utilisation future, car ils sont complètement redondants et ne seront pas plus rapides et ne se produiront pas assez fréquemment dans le code pour avoir de l'importance.

l'ordre de PUSHA a l'ordre de l'instruction de codage: eax,ecx,edx,ebx,esp,ebp,esi,edi. Notez qu'il redondante poussé esp! Vous avez besoin de savoir esp pour trouver les données dont il a poussé!

si vous convertissez code de 64 bits PUSHA le code n'est pas bon de toute façon, vous devez le mettre à jour pour pousser les nouveaux registres r8 thru r15. Vous devez également sauvegarder et restaurer un État SSE beaucoup plus grand,xmm8 thru xmm15. En supposant que vous allez tabasser.

si le code du gestionnaire d'interruption est simplement un raccourci qui renvoie au code C, vous n'avez pas besoin de sauvegarder tous les registres. Vous pouvez supposer que le compilateur C générera du code qui préservera rbx,rbp, rsi,rdi et r12 thru r15. Vous devez seulement sauver et restaurer rax,rcx,rdx et r8 thru r11. (Note: sur les plateformes Linux ou autres systèmes V ABI, le compilateur préservera rbx,rbp,r12 -r15, vous pouvez vous attendre rsi et rdi ridiculiser).

les registres de segment n'ont aucune valeur en mode long (si le thread interrompu est en mode de compatibilité 32 bits, vous devez préserver les registres de segment, merci ughoavgfhw). En fait, ils se sont débarrassés de la plupart de la segmentation en mode long, mais FS est toujours réservé pour les systèmes d'exploitation à utiliser comme adresse de base pour les données locales de thread. La valeur de registre lui-même n'a pas d'importance, la base de FS et GS sont définies par le biais de MSRs 0xC0000100 et 0xC0000101. En supposant que vous n'utiliserez pas FS vous n'avez pas besoin de vous en inquiéter, rappelez-vous juste que n'importe quel thread local de données accédé par le code C pourrait utiliser n'importe quelle les tls de random thread. Faites attention car les bibliothèques d'exécution C utilisent TLS pour certaines fonctionnalités (exemple: strtok utilise généralement TLS).

Chargement d'une valeur dans FS ou GS (même en mode utilisateur) va remplacer le FSBASE ou GSBASE MSR. Depuis certains systèmes d'exploitation utilisent GS en tant que stockage" processeur local " (ils ont besoin d'un moyen d'avoir un pointeur vers une structure pour chaque CPU), ils ont besoin de le garder quelque part qui ne sera pas bloqué par le chargement GS en mode utilisateur. De résolvez ce problème, il y a deux MSR réservés pour le GSBASE enregistrer: un actif et un caché. En mode noyau, le noyau est GSBASE est tenu dans l'habituel GSBASE MSR et le mode utilisateur de base est dans l'autre (caché) GSBASE MSR. Lorsque le contexte passe du mode noyau à un mode utilisateur, et lors de la sauvegarde d'un contexte de mode utilisateur et de l'entrée en mode noyau, le code de changement de contexte doit exécuter L'instruction SWAPGS, qui échange les valeurs des GSBASE MSR. Depuis le noyau GSBASE est bien caché dans L'autre MSR en mode utilisateur, le code du mode utilisateur ne peut pas assommer le noyau GSBASE en chargeant une valeur dans GS. Lorsque le mode CPU renoue avec le noyau, le code de sauvegarde du contexte s'exécute SWAPGS et restaurer le noyau GSBASE.

15
répondu doug65536 2017-08-14 08:25:59

pusha n'est pas valide en mode 64 bits, car il est redondant. Pousser chaque registre individuellement est exactement la chose à faire.

4
répondu user200783 2011-07-26 22:32:33
.macro pushaq
    push %rax
    push %rcx
    push %rdx
    push %rbx
    push %rbp
    push %rsi
    push %rdi
.endm # pushaq

et

.macro popaq
    pop %rdi    
    pop %rsi    
    pop %rbp    
    pop %rbx    
    pop %rdx    
    pop %rcx
    pop %rax
.endm # popaq

et éventuellement ajouter les autres registres r8-15 si on a besoin de

0
répondu baz 2017-08-23 20:40:56