2014-01-20 7 views
7

Ich versuche, Cache im Linux-Kernel-Raum zu deaktivieren/aktivieren.C verwendet Assemble: Operandentyp Mismatch für Push

Der Code ich benutze, ist

__asm__ __volatile__(
     "pushw %eax\n\t" /*line 646*/ 
     "movl %cr0,%eax\n\t" 
     "orl $0x40000000,%eax\n\t" 
     "movl %eax,%cr0\n\t" 
     "wbinvd\n\t" 
     "pop %eax"); 

Nachdem ich kompilieren, ich die Fehlermeldung bekam wie folgt:

memory.c: Assembler messages: 
memory.c:645: Error: operand type mismatch for `push' 
memory.c:646: Error: unsupported for `mov' 
memory.c:648: Error: unsupported for `mov' 
memory.c:650: Error: operand type mismatch for `pop' 
make[4]: *** [memory.o] Error 1 

Meine Maschine ist Intel (R) Xeon (R) Prozessor E5-1650 v2 @ 3,50 GHz. 64-Bit-Maschine.

Könnte jemand mir helfen, aufzuzeigen, welcher Teil falsch ist und wie ich ihn beheben kann?

Ich vermute es liegt daran, dass die Instruktion und das Register nicht übereinstimmen. Aber ich bin verwirrt, wie ich es beheben kann. :(

Vielen Dank im Voraus

+1

ist nicht pushw für Wortgröße (16bit)? eax ist 32bit, versuchen 'pushl' – Leeor

+0

Hallo @Leeor, Vielen Dank für Ihren Kommentar! aber pushl meldet Fehler: memory.c: 645: Fehler: ungültiges Befehlssuffix für 'push '; Ich habe auch versucht, pushq, die nicht funktioniert – Mike

Antwort

1

Laut Intel - http://download.intel.com/products/processor/manual/325383.pdf Ein Wort ist 16 Bits so pushw einen 16-Bit-Operanden erwartet Das Register EAX 32 Bit und muss mit PUSHL geschoben werden bearbeiten!..: Montage- Sie für 32-Bit oder 64-Bit?

+0

meine Maschine ist 64bit, also ich denke, eax ist 64bit registrieren? – Mike

+1

Dies kann nützlich sein - http://www.x86-64.org/documentation/assembly.html Es spricht über Push-Anweisungen um Absatz 5. –

+0

auf x86 Registerlängen ändern sich nie. Bei der Größenänderung verwenden sie einen neuen Namen/Präfix, so dass Sie alle Registertypen von Byte bis 8 Byte adressieren können. Sie sollten über die Architektur lesen, bevor Sie Assembly schreiben –

9

Obwohl die meisten 32-Bit-Register in 64-Bit-Architekturen bestehen bleiben, sind sie nicht mehr in der Lage mit dem Stapel der interagieren. Daher zu schieben oder Pop versuchen %eax eine illegale Operation ist. Also, wenn Wenn Sie mit dem Stack spielen möchten, müssen Sie %rax verwenden, was den 64bit-Architekturen entspricht %eax.

+0

Viel besserer Vorschlag: Verwenden Sie einen Clobber, anstatt sich selbst zu speichern/wiederherzustellen. –

1

Wenn Sie es nie herausgefunden, verwenden pushq %rax bei der Compilierung 64-Bit-

2

Der richtige Ansatz ist zu eine clobber auf %eax erklären, statt Speichern/Wiederherstellen it yourself. Der Compiler kann wahrscheinlich etwas effizienteres als Push/Pop machen, wie die Verwendung verschiedener Register für alle Werte, die live bleiben sollen. Dies bedeutet auch, dass Sie keinen anderen Code für 64bit benötigen, um %rax zu speichern/wiederherzustellen.

Beachten Sie, dass pushq %rax/popq %rax würde nicht in User-Space-Code auf x86-64 sicher sein. Es gibt no way to tell gcc that inline-asm clobbers the red-zone. Es wäre sicher im Kernel-Code, wo der ABI keine rote Zone verwendet, aber wieder, es besiegt immer noch den Zweck der GNU C Inline-Asmsyntax.


Es gibt eine zusätzliche Falten hier: mov %cr0, %eaxisn't a valid 64bit instruction. Sie müssen ein 64-Bit-Register verwenden.

Wenn der Compiler ein Register für uns auswählt, wird dieses Problem gelöst und dem Compiler mehr Freiheit gegeben, also ist es sowieso besser. Deklarieren Sie eine C-Variable mit einem 64-Bit-Typ im x86-64-ABI und 32-Bit im i386-ABI. (zB long, da dies für den Linux-Kernel ABI ist, nicht Windows wo long immer 32bit ist. uintptr_t ist eine andere Option, die im Linux-Kernel funktionieren würde. Aber nicht im User-Space: x32 ist langer Modus mit 32bit-Zeigern).

)
// is this enable or disable? I didn't check the manual 
void set_caching_x86(void) { 
    long tmp;  // mov to/from cr requires a 64bit reg in 64bit mode 
    asm volatile(
     "mov %%cr0, %[tmp]\n\t"  // Note the double-% when we want a literal % in the asm output 
     "or $0x40000000, %[tmp]\n\t" 
     "mov %[tmp], %%cr0\n\t" 
     "wbinvd\n\t" 
     : [tmp] "=r" (tmp) // outputs 
     : // no inputs 
     : // no clobbers. "memory" clobber isn't needed, this just affects performance, not contents 
    ); 
} 

Diese compiles and assembles to what we want, mit oder ohne -m32, wie man auf dem Godbolt Compiler Explorer sehen können.

Wenn Sie von Hand schreiben, ist es einfacher, die Operandengröße durch die Operanden implizieren zu lassen, anstatt immer ein Suffix auf dem Mnemonic zu verwenden. d. h., push %eax hätte funktioniert (im 32-Bit-Modus), aber immer noch schlimmer, als der Compiler sich darum kümmern zu müssen.

Wir hätten %k[tmp] verwenden können, um %eax (oder was auch immer) auch im 64-Bit-Modus zu bekommen, aber das würde die oberen 32b null. Die Ausgabe von 1 Byte auf einem REX-Präfix für die or-Anweisung ist es wert, für CPUs zukunftssicherer zu sein, die sich darum kümmern, was Sie in die oberen 32b eines Steuerregisters schreiben.

Die volatile stellt sicher, dass die asm-Anweisung nicht weg optimiert wird, auch wenn der Ausgabewert nie verwendet wird.

4

Es gibt einige Probleme mit Ihrer Inline-Assembly-Anweisung, von denen die meisten durch die Fehlermeldungen angezeigt werden. Die erste Fehlermeldung Error: operand type mismatch for `push' entspricht der pushw %eax Anweisung. Der Fehler ist darauf zurückzuführen, dass das von Ihnen verwendete Operandengrößen-Suffix w nicht mit der tatsächlichen Größe des Operanden %eax übereinstimmt. Sie haben ihm gesagt, dass er den Befehl verwenden soll, um einen 16-Bit-Wert auf den Stack zu schieben, stellt aber ein 32-Bit-Register als Operanden zur Verfügung. Sie könnten das beheben, indem Sie pushw %ax verwenden, aber das ist nicht das, was Sie wollen. Es würde nur die unteren 16 Bits des RAX-Registers beibehalten, nicht das gesamte Register. Eine andere "offensichtliche" Lösung wäre pushl %eax, aber es gibt zwei Probleme damit Um andere Probleme zu beheben, müssen Sie zuerst das gesamte RAX-Register modifizieren, und das bedeutet, dass Sie alle 64 Bits davon speichern müssen, nicht nur die unteren 32 Bits. Die zweite besteht darin, dass es keine 32-Bit-PUSH-Anweisung im 64-Bit-Modus gibt, so dass Sie gezwungen sind, pushq %rax10 zu verwenden. Die folgenden zwei Fehlermeldungen sind beide Error: unsupported for `mov'. Diese Fehlermeldungen entsprechen den Anweisungen movl %cr0,%eax und movl %eax,%cr0. und beide sind das Ergebnis desselben Problems. Im 64-Bit-Modus gibt es keine 32-Bit-Operandengrößenversion dieser Anweisungen. Sie müssen einen 64-Bit-Operanden verwenden, daher wird RAX anstelle von EAX verwendet. Dies ist der Punkt, an dem die 64-Bit-RAX-Dateien verschrottet werden und warum ich gesagt habe, dass Sie das gesamte Register behalten müssen.

Die letzte Fehlermeldung lautet Error: operand type mismatch for `pop'. Dies ist ein Ergebnis eines ähnlichen Problems wie das erste. In diesem Fall haben Sie kein Operandengrößensuffix verwendet, was bedeutet, dass Assembler versuchen wird, die Operandengröße basierend auf den Operanden zu bestimmen. Da Sie einen 32-Bit-Operanden %eax verwendet haben, wird eine 32-Bit-Operandengröße verwendet. Wie auch bei PUSH gibt es 32-Bit-POP-Anweisungen im 64-Bit-Modus, so dass Sie %eax entweder nicht verwenden können. In jedem Fall, da der PUSH-Befehl 64-Bit sein muss, muss der POP-Befehl 64-Bit entsprechen, so dass der Fix popq %rax verwenden soll.

Schließlich ist ein Problem, das nicht durch eine Fehlermeldung angezeigt wird, dass im 64-Bit-Modus die Größe von CR0 zu 64-Bit erweitert wird. Während die zusätzlichen 32 Bits derzeit reserviert sind und auf Null gesetzt werden müssen, könnten sie in zukünftigen Prozessoren definiert werden. Also sollte die orl $0x40000000,%eax Anweisung die oberen 64 Bits beibehalten. Leider werden die oberen 32-Bit-Bits von RAX nicht gelöscht, was bedeutet, dass dieser Befehl auch unbeabsichtigt irgendwelche der Bits löscht, die zukünftige CPUs Bedeutung geben könnten. So sollte es mit orq $0x40000000,%rax ersetzt werden.

So ist die festgelegte Abfolge von Anweisungen wäre:

pushq %rax 
    movq %cr0, %rax 
    orq  $0x40000000, %rax 
    movq %rax, %cr0 
    wbinvd 
    popq %rax 

Das ist nicht, was ich werde vorschlagen, aber in Ihrer Inline-Assembly verwenden. Es ist möglich, dies zu vereinfachen, indem GCC das verwendete Register auswählt. Auf diese Weise müssen Sie es nicht bewahren. Hier ist, was ich stattdessen vorschlagen würde:

long long dummy; 
asm volatile ("movq %%cr0, %0\n\t" 
       "orq $0x40000000, %0\n\t" 
       "movq %0, %%cr0\n\t" 
       "wbinvd" 
       : "=r" (dummy) : :); 
+0

'lang lang' ist 64bit sogar im 32bit Modus. Ich dachte, es würde nicht funktionieren, aber anscheinend tut es das. Mit '-m32' reserviert der Compiler wahrscheinlich zwei Register dafür, aber '% 0' erweitert sich nur auf einen von ihnen? Vielleicht ist es die 'A' Contraint:' edx: eax'? In meiner Antwort schlug ich vor, 'long' zu verwenden. (Ich hatte 'tmp' verwendet, bis ich deine Antwort innerhalb weniger Minuten von mir sah und darauf hinwies, dass x86-64 ein cr nicht zu einem 32bit reg. Bewegen kann) –

+0

@PeterCordes Ich versuche nicht etwas zu schreiben, das funktionieren würde in 64-Bit-Modi. Und, yah, ich habe das ORL-Problem bereits behoben, ich habe es zufällig bemerkt, kurz bevor du deinen Beitrag aktualisiert hast. –

Verwandte Themen