2009-08-13 5 views
8

Ich bin auf diesen Code gestoßen und muss verstehen, was er tut. Es scheint nur zu zwei Bytes zu deklarieren und dann nichts zu tun ...Was macht diese x86 Inline-Assembly?

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

Vielen Dank!

+0

Interessant. Es ist so lange her, dass ich mir das alles angeschaut habe, ich bin mir nicht sicher. Sie können angeben, welchen Assembler Sie verwenden. Ich weiß nicht, ob dies den Inhalt, die Adresse oder beides (!) Von "x" setzt. Es würde mich nicht überraschen, wenn x auf einen Speicherkarten-Port zeigt, der von einem Gerät asynchron aktualisiert wird, und somit das Schlüsselwort "volatile". Jemand, der dieses Zeug tatsächlich macht, wird bald auftauchen, nehme ich an. – Roboprog

+2

Wetten, dass der ursprüngliche Programmierer Kommentare verwendet hat! –

+0

Einfachster Weg: einfach kompilieren und dann zerlegen. –

Antwort

11

Dies erzeugt zwei Bytes (0F 31) direkt in den Code-Stream. Dies ist ein RDTSC-Befehl, der den Zeitstempel-Zähler in EDX liest: EAX, der dann durch die Ausgabebedingung "= A" in die Variable 'x' kopiert wird. (X)

+0

Ah ok !! Und die "= A" (x) -Syntax (ich benutze gcc4.1), um% eax und% edx zusammen zu verwenden - wird es auf x86_64 arch funktionieren? Ich denke schon, aber ich weiß nicht viel über die Montage. –

+0

Ja - die Einschränkung 'A' bedeutet einen 64-Bit-Wert im EDX: EAX-Registerpaar in den i386- und x86_64-GCC-Maschinenbeschreibungen –

+0

@MK. und Chris: Nein, im 64bit Code wird ein 'uint64_t' mit einem' "= A" 'Constraint tatsächlich nur einen von' rax' oder 'rdx' wählen, als ob du' "= ad" 'benutzt hättest. Es teilt Werte nicht in zwei gleiche Hälften für Sie auf :([Diese Version, die zusammen die untere und obere Hälfte zusammenfügt] (https://godbolt.org/g/nQfz7O) kompiliert zum optimalen Code für -m32 und -m64, weil im 32bit-Code das OR weg optimiert. –

4

0F 31 ist der x86-Opcode für den Befehl RDTSC (Lesezeitstempelzähler); Es speichert den gelesenen Wert in den EDX- und EAX-Registern.

Die Anweisung _ _ asm__ deklariert nicht nur zwei Bytes, sondern platziert Inline-Assembly in den C-Code. Vermutlich hat das Programm eine Möglichkeit, den Wert in diesen Registern unmittelbar danach zu verwenden.

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

2

Es ist eine 0F 31 opcode Einfügen, die nach this site ist:

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

Dann wird das Ergebnis in der x variable

0

Es ist inline ASM for rdtsc, mit dem Speichern Maschinencode-Kodierung geschrieben, um wirklich alte Assembler zu unterstützen, die das Mnemonic nicht kennen.

Leider funktioniert es nur richtig in 32-Bit-Code, weil "=A" nicht teilt 64-Bit-Operanden in der Hälfte in 64-Bit-Code. (Die gcc manual even uses rdtsc an an example to illustrate this)

Der sichere Weg, dies zu schreiben, die mit gcc -m32 oder -m64, um einem optimalen Code kompiliert ist:

#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; 
} 

In 32-Bit-Code, es ist nur rdtsc/ret, aber in 64-Bit Code es tut die notwendige Verschiebung/oder beide Hälften in rax für den Rückgabewert zu bekommen.

Siehe auf der Godbolt compiler explorer.