2009-08-13 8 views
8

Je suis tombé sur ce code et j'ai besoin de comprendre ce qu'il fait. Il semble juste déclarer deux octets et puis ne rien faire ...Que fait cet assemblage en ligne x86?

uint64_t x; 
__asm__ __volatile__ (".byte 0x0f, 0x31" : "=A" (x)); 

Merci!

+0

Intéressant. Ça fait tellement longtemps que je n'ai rien vu de tout ça, je ne suis pas sûr. Vous pourriez spécifier quel assembleur vous utilisez. Je ne sais pas si cela définit le contenu, l'adresse, ou les deux (!) De "x". Cela ne me surprendrait pas si x pointe sur un port mappé en mémoire, mis à jour par un périphérique de manière asynchrone, et donc le mot-clé "volatile". Quelqu'un qui fait ce genre de choses arrivera bientôt, je suppose. – Roboprog

+2

Pariez que vous souhaitez le programmeur d'origine utilisé des commentaires! –

+0

Le moyen le plus simple: il suffit de le compiler puis de le démonter. –

Répondre

11

Ceci génère deux octets (0F 31) directement dans le flux de code. C'est une instruction RDTSC, qui lit le compteur d'horodatage dans EDX: EAX, qui sera ensuite copié dans la variable 'x' par la contrainte de sortie "= A" (x)

+0

Ah ok !! Et la syntaxe "= A" (x) (j'utilise gcc4.1) pour utiliser ensemble% eax et% edx - cela fonctionnera-t-il sur x86_64 arch? Je le pense mais je ne connais pas grand-chose à l'assemblage. –

+0

Oui - la contrainte 'A' signifie une valeur de 64 bits dans la paire de registres EDX: EAX dans les descriptions de machines gcc i386 et x86_64 –

+0

@MK. et Chris: Non, dans le code 64 bits, un 'uint64_t' ayant une contrainte' "= A" 'choisira en fait un de' rax' ou 'rdx', comme si vous utilisiez' "= ad" '. Il ne divise pas les valeurs en deux moitiés égales pour vous :([Cette version qui ORs ensemble la moitié basse et haute] (https://godbolt.org/g/nQfz7O) compile en code optimal pour -m32 et -m64, parce que dans le code 32 bits, le bloc opératoire optimise –

4

0F 31 est l'opcode x86 pour l'instruction RDTSC (compteur d'horodatage de lecture); il place la valeur lue dans les registres EDX et EAX.

La directive _ _ asm__ ne se contente pas de déclarer deux octets, elle place l'assembly en ligne dans le code C. Vraisemblablement, le programme a un moyen d'utiliser la valeur dans ces registres immédiatement après.

http://en.wikipedia.org/wiki/Time_Stamp_Counter

2

C'est l'insertion d'un 0F 31 opcode, qui, selon this site est:

0F 31 P1+ f2 RDTSC EAX EDX IA32_T...  Read Time-Stamp Counter 

Ensuite, il stocke le résultat dans la variable x

0

Il est asm inline for rdtsc, avec le codage machine-code écrit pour soutenir les assembleurs vraiment vieux qui ne connaissent pas le mnémonique.

Malheureusement, cela ne fonctionne correctement que dans le code 32 bits car "=A" ne divise pas les opérandes 64 bits en deux dans le code 64 bits. (Le gcc manual even uses rdtsc an an example to illustrate this)

Le moyen sûr d'écrire ce qui compile le code optimal avec gcc -m32 ou -m64, est:

#include <stdint.h> 
uint64_t timestamp_safe(void) 
{ 
    unsigned long tsc_low, tsc_high; // not uint32_t: saves a zero-extend for -m64 (but not x32 :/) 
    asm volatile("rdtsc" : "=d"(tsc_high), "=a" (tsc_low)); 
    return ((uint64_t)tsc_high << 32) | tsc_low; 
} 

Dans le code 32bit, il est juste rdtsc/ret, mais 64bit code il fait le décalage nécessaire/ou pour obtenir les deux moitiés en rax pour la valeur de retour.

Voir sur le Godbolt compiler explorer.