Die ursprüngliche Frage war Is this assembly function call safe/complete?
. Die Antwort darauf lautet: Nein. Auch wenn es in diesem einfachen Beispiel so aussieht (insbesondere wenn Optimierungen deaktiviert sind), verstoßen Sie gegen Regeln, die schließlich zu Fehlern führen (solche, die schwer zu finden sind).
Ich möchte die (offensichtliche) Follow-up-Frage ansprechen, wie man es sicher macht, aber ohne Rückmeldung vom OP auf die eigentliche Absicht, kann ich das nicht wirklich tun.
Also, ich werde das Beste tun, was ich kann mit dem, was wir haben, und versuchen, die Dinge zu beschreiben, die es unsicher machen und einige der Dinge, die man dagegen tun kann.
Beginnen wir damit, dass die asm vereinfacht:
__asm__(
"mov %0, %%edi;"
:
: "g"(a)
);
Auch mit dieser einzigen Aussage, dieser Code ist bereits unsicher. Warum? Weil wir den Wert eines Registers (edi) ändern, ohne dass der Compiler davon erfährt.
Wie kann der Compiler nicht wissen, dass Sie fragen? Schließlich ist es genau dort in der asm! Die Antwort kommt von dieser Linie in den gcc docs:
GCC nicht den Assembler-Anweisungen selbst nicht analysieren und nicht wissen, was sie bedeuten, oder sogar, ob sie gültig Assembler eingegeben werden.
In diesem Fall, wie lässt du gcc wissen, was vor sich geht? Die Antwort liegt in der Verwendung der Constraints (der Dinge nach den Doppelpunkten), um den Einfluss der ASM zu beschreiben.
vielleicht einfachste Weg, um diesen Code zu beheben würde so aussehen:
__asm__(
"mov %0, %%edi;"
:
: "g"(a)
: edi
);
Dies fügt edi zum clobber list. Kurz gesagt, dies teilt gcc mit, dass der Wert von edi durch den Code geändert wird, und dass gcc nicht davon ausgehen sollte, dass ein bestimmter Wert darin enthalten ist, wenn asm beendet wird.
Nun, während das das einfachste ist, ist es nicht unbedingt der beste Weg. Betrachten Sie diesen Code:
__asm__(
""
:
: "D"(a)
);
Dieses eine machine constraint verwendet gcc sagen, damit Sie den Wert der Variablen a
in das edi-Register zu setzen. Auf diese Weise lädt gcc das Register für Sie zu einem "passenden" Zeitpunkt, vielleicht indem Sie immer a
in edi behalten.
Es gibt eine (signifikante) Einschränkung für diesen Code: Indem wir den Parameter hinter den zweiten Doppelpunkt setzen, deklarieren wir ihn als Eingabe. Eingabeparameter müssen schreibgeschützt sein (dh sie müssen den gleichen Wert beim Verlassen der asm haben).
In Ihrem Fall bedeutet die call
Anweisung, dass wir nicht garantieren können, dass edi nicht geändert wird, also funktioniert das nicht ganz. Es gibt ein paar Möglichkeiten, damit umzugehen. Am einfachsten ist es, die Einschränkung nach dem ersten Doppelpunkt nach oben zu verschieben, um sie zu einer Ausgabe zu machen, und "+D"
anzugeben, um anzugeben, dass der Wert read + write lautet. Aber dann wird der Inhalt von a
nach dem asm ziemlich undefiniert sein (printf könnte irgendetwas einstellen). Wenn a
zerstören nicht akzeptabel ist, es gibt immer etwas wie folgt aus:
int junk;
__asm__ volatile (
""
: "=D" (junk)
: "0"(a)
);
Dieser gcc erzählt, der die asm auf starten, sollten sie den Wert der Variablen a
in der gleichen Stelle wie Ausgabebedingung # 0 (dh edi) setzen . Es sagt auch, dass edi bei der Ausgabe nicht mehr a
sein wird, sondern die Variable junk
enthalten wird.
Edit: Da die Variable 'junk' nicht verwendet wird, müssen wir den Qualifier volatile
hinzufügen. Volatile war implizit, wenn keine Ausgabeparameter vorhanden waren.
Ein weiterer Punkt in dieser Zeile: Sie beenden es mit einem Semikolon. Dies ist legal und wird wie erwartet funktionieren. Wenn Sie jedoch jemals die Befehlszeilenoption -S
verwenden möchten, um genau zu sehen, welcher Code generiert wurde (und wenn Sie mit inline asm arbeiten wollen), werden Sie feststellen, dass schwer lesbarer Code erzeugt wird. Ich würde empfehlen, \n\t
anstelle eines Semikolons zu verwenden.
Alles, und wir sind immer noch in der ersten Zeile ...
Offensichtlich ist das gleiche mit den anderen beiden mov
Aussagen gelten würde.
Das bringt uns zur call
Aussage.
Sowohl Michael und ich haben eine Reihe von Gründen aufgelistet Aufruf in Inline-Asm ist schwierig.
- Behandlung aller Register, die durch den ABI des Funktionsaufrufs verfälscht werden können.
- Umgang mit der roten Zone.
- Handhabung Ausrichtung.
- Speicher Clobber.
Wenn das Ziel hier ist "Lernen", dann fühlen Sie sich frei zu experimentieren. Aber ich weiß nicht, dass ich mich im Produktionscode jemals wohl fühlen würde. Selbst wenn es so aussieht, als würde es funktionieren, würde ich nie sicher sein, dass es keinen seltsamen Fall gab, den ich vermisst hätte. Das ist abgesehen von meinen normalen Bedenken über using inline asm at all.
Ich weiß, das ist eine Menge Informationen. Wahrscheinlich mehr, als Sie als Einführung in den Befehl asm
von gcc gesucht haben, aber Sie haben sich einen herausfordernden Platz zum Starten ausgesucht.
Wenn Sie dies noch nicht getan haben, verbringen Sie Ihre Zeit damit, alle Dokumente in gcc's Assembly Language interface zu betrachten. Es gibt viele gute Informationen und Beispiele, die versuchen zu erklären, wie alles funktioniert.
Sie könnten versuchen: http://codereview.stackexchange.com/ – 4386427
Kurz gesagt: Dieser Code ist unsicher. Sie ändern Register in der Inline-Asm, ohne den Compiler zu benachrichtigen. Es gibt Dinge, die Sie tun können, um es besser zu machen (verwenden Sie [x86 machine constraints] (https://gcc.gnu.org/onlinedocs/gcc/Machine-Constraints.html), fügen Sie den Speicher-Clobber, Clobber-Register hinzu, die der ABI erlaubt foo zu ändern (eax?), etc.), aber vielleicht würden Sie eine bessere Antwort bekommen, wenn Sie sagen würden, was Sie erreichen wollten. –
Ich nehme an, das ist 64-Bit-Code? (Zumindest scheint es so von der Aufrufkonvention zu sein). Das Aufrufen einer Funktion in 64-Bit-Code ist wesentlich komplizierter als Sie vielleicht wissen. Ich habe vor kurzem eine Antwort geschrieben, die für Sie von Nutzen sein kann (bezüglich 64-Bit-Code, Inline-Assembler und Aufruffunktionen). In diesem Fall rufe ich 'printf' an, aber es gilt ziemlich genau für den Aufruf einer beliebigen Funktion in 64-Bit-Code von Inline Assembler: http://Stackoverflow.com/a/37503773/3857942 –