10
votes

Comment utiliser "GS:" dans l'ensemble Windows 64 bits (par exemple, le code TLS de portage)

Comment un programme d'espace utilisateur peut configurer "GS:" moins de 64 bits (actuellement XP-64)?
(En configurant, définissez GS: 0 à une adresse linéaire arbitraire 64 bits).

J'essaie de porter un environnement "JIT" à X86-64 qui a été développé à l'origine pour Win32.

Un aspect malheureux design est que le code identique doit fonctionner sur plusieurs threads de l'espace utilisateur (par exemple, "fibres"). La version Win32 du code utilise le sélecteur GS pour cela et génère le préfixe approprié pour accéder aux données locales - "MOVA EX, GS: [décalage]" indique les données correctes pour la tâche actuelle. Le code de la version Win32 chargerait une valeur dans GS, si seulement elle avait une valeur qui fonctionnerait.

jusqu'à présent, j'ai pu trouver que Windows 64 bits ne prend pas en charge le LDT. , la méthode utilisée sous Win32 ne fonctionnera pas. Cependant, l'ensemble d'instructions X86-64 comprend des "swapgs", ainsi qu'un procédé de charge GS sans utiliser la segmentation héritée - mais cela ne fonctionne que dans l'espace du noyau.

selon les manuels X64, même si Win64 Autorisé l'accès aux descripteurs - ce qu'il ne doit pas - il n'y a aucun moyen de définir les 32 bits de la base de segment élevé. Le seul moyen de définir ces éléments est via GS_BASE_MSR (et correspondant FS_BASE_MSR - Les autres bases de segment sont ignorées en mode 64 bits). L'instruction WRSRR est Ring00, donc je ne peux donc pas l'utiliser directement.

J'espère une fonction ZW * qui me permet de changer "GS:" dans l'espace utilisateur ou d'un autre coin obscur de l'API Windows. Je crois que Windows utilise toujours FS: Pour son propre TLS, un mécanisme doit donc être disponible?


Ce code d'exemple illustre le problème. Je m'excuse à l'avance pour avoir utilisé le code d'octet - vs ne fera pas l'assemblage en ligne pour la compilation 64 bits et j'essayais de garder cela comme un fichier à des fins d'illustration.

Le programme affiche "PASS" sur XP-32 et ne pas sur XP-X64.


xxx

0 commentaires

7 Réponses :


1
votes

n'avez jamais modifié GS en code X64, alors je me trompe peut-être, mais ne devriez-vous pas être capable de modifier GS par PUSH / POP ou par LGS?

MISE À JOUR: Les manuels Intel disent également que MOV SECGREG, REG est autorisé en mode 64 bits.


0 commentaires

4
votes

Vous pouvez modifier le contexte de thread via le SetthreadContext API directement. Cependant, vous devez vous assurer que le fil ne fonctionne pas tant que le contexte est modifié. Suspendre et modifier le contexte de un autre fil ou déclencher une fausse exception SEH et modifier le contexte de thread dans le gestionnaire de Seh. Le système d'exploitation changera ensuite le contexte du thread pour vous et réorganisera le thread.

update: strong> p>

code d'échantillon pour la deuxième approche: p>

__try
{
    __asm int 3 // trigger fake exception
}
__except(filter(GetExceptionCode(), GetExceptionInformation()))
{
}

int filter(unsigned int code, struct _EXCEPTION_POINTERS *ep)
{
    ep->ContextRecord->SegGs = 23;
    ep->ContextRecord->Eip++;
    return EXCEPTION_CONTINUE_EXECUTION;
}


4 commentaires

Je pense que c'est plus simple - "MOV AX, 23H; MOV GS, AX;" - Il y a deux problèmes - (1) Comment obtenir une base linéaire sur le descripteur 20h en mode 64 bits? et (2) à l'aide d'une adresse linéaire de 64 bits (traverser le registre du segment ne définit que les 32 bits 32 aussi loin que je peux dire)


Je vois ton problème maintenant. Eh bien, si vous ne pouvez pas modifier la LDT (car ce concept n'est plus utilisé), votre seule chance que cela reste de modifier le GDT (si cela existe même en X64 - puisque X64 est principalement lié au modèle d'espace d'adresses plat), et celui-là n'est que le mode de noyau de forme accessible. Je pense que ce que vous voulez faire n'est pas possible à partir du mode utilisateur (je pourrais me tromper, cependant)


Il suffit de lire sur Wikipedia que Windows bugcheck si vous essayez de modifier le GDT sur X64: en.wikipedia.org/ wiki / global_descriptor_table


X64 a le mécanisme alternatif ("gs.base msr"), mais WRSRR est une instruction RING0. Cela aurait été bien si la structure de contexte avait GSBase, mais cela ne semble pas être le cas dans les en-têtes que j'ai.



2
votes

3 commentaires

J'ai besoin de GS pour pointer vers les données spécifiques à fil d'application - Il existe plusieurs fils d'application par fil O / S, donc je ne peux donc pas compter sur le système d'exploitation.


Avec plusieurs fils d'application par fil d'exploitation, vous devez planifier vos propres threads d'application. Dans mon expérience, il y a une petite quantité de données qui doit être accessible rapidement via GS ou autre. Demandez au planificateur de copier ces données à la zone TLS. Demandez également au planificateur de copier un pointeur sur "le reste" de vos données de fil d'application à une dernière cellule TLS. Maintenant, tout est adressable via GS: le truc critique est dans TLS, moins critique accessible via GS et une charge supplémentaire. Vous n'aimerez peut-être pas ce choix, mais si vous ne pouvez pas changer de GS, vous brûlez de manière permanente GP ou vous le faites.


6 ans après que cette réponse a été publiée, quelqu'un jette un bowvote, mais ne peut pas être dérangé de dire pourquoi. Joli.



1
votes

puisque x86_64 a beaucoup plus de registres que x86, une option que vous voudrez peut-être envisager si vous ne pouvez pas utiliser GS serait simplement d'utiliser l'un des registres à usage général (par exemple, EBP) en tant que pointeur de base et faire pour la différence avec les nouveaux registres R8-R15.


1 commentaires

Bien qu'il s'agisse d'une solution de contournement plausible, X86-64 n'a pas ajouté de nouveau terme à l'adressage - le code existant fait une utilisation intensive d'une adressage de base à l'échelle échelle +, avec le terme GS comme 3ème trimestre. Je pense que je peux utiliser UTILISER 'LEA' suivi d'un formulaire de deux enregistrements, mais je dois également trouver des cas comme "MOVA EX, MEM", qui accepte un préfixe mais ont besoin de remplacer complètement pour utiliser l'adressage basé sur un registre.



1
votes

Que se passe-t-il si vous venez de passer à des threads OS? Est la performance que mal?

Vous pouvez utiliser un seul emplacement TLS de taille pointé pour stocker la base de votre zone de stockage de votre fil légère. Vous devrez simplement échanger un pointeur pendant votre commutateur de contexte. Chargez l'un des nouveaux registres temporaires de là chaque fois que vous avez besoin de la valeur et que vous n'avez pas à vous soucier de l'utilisation de l'un des rares appels de fonction préservés.

Une autre solution prise en charge serait d'utiliser le API de fibre pour planifier vos fils légers. Vous modifieriez ensuite le JIT pour apporter des appels corrects à FLSGET / SETVALUE .

Désolé, cela ressemble à l'ancien code est écrit pour s'appuyer sur les préfixes de segment pour adresser et que le LDT ne soit tout simplement pas disponible pour ce type de chose. Vous allez devoir résoudre un peu la génération de code.

Le code existant utilise une utilisation intensive de l'indice de base + adressage de base, avec le terme GS comme un 3ème trimestre. Je pense que je peux utiliser utiliser 'Lea' suivi d'un formulaire de deux enregistrements

sonne comme un bon plan.

Des cas tels que "MOV EAX, MEM", qui acceptent un préfixe mais ont besoin de remplacer complètement pour utiliser l'adressage basé sur un registre

Peut-être que vous pourriez peut-être déplacer ceux-ci pour résoudre + adressage de compensation. Le registre offset pourrait être le registre tenant la base de votre bloc TLS.


0 commentaires

1
votes

Pourquoi ne pas utiliser getfiberdata ou essayez-vous d'éviter les deux instructions supplémentaires?


0 commentaires

0
votes

x86-64 n'a pas ajouté de nouveau terme à l'adressage - le code existant utilise de lourdes utilisation de l'indice de base + l'adressage de base, avec le terme GS comme 3ème trimestre. P>

Je suis assez confus par votre question, mais j'espère que cet assembleur aide. Je ne l'ai pas encore porté dans C Code C, mais cela le fera sous peu: p>

lecture __ DeclSpec (thread) code> DATA P>

void SetActiveThread(rage::scrThread* thread)
{
    char* moduleTls = *(char**)__readgsqword(88);
    *reinterpret_cast<rage::scrThread**>(moduleTls + 2096) = thread;
}


0 commentaires