Während ich versuchte, alten Code wieder funktionsfähig zu machen (https://github.com/chaos4ever/chaos/blob/master/libraries/system/system_calls.h#L387, FWIW), entdeckte ich, dass einige der Semantiken von gcc
sich in den letzten 10-15 Jahren auf ziemlich subtile, aber immer noch gefährliche Weise verändert haben ...: PGCC/x86 inline asm: Wie sagt man gcc, dass der Inline-Assembly-Abschnitt% esp ändert?
Der Code verwendet, um gut mit älteren Versionen von gcc
, wie 2.95 zu arbeiten. Wie auch immer, hier ist der Code:
static inline return_type system_call_service_get(const char *protocol_name, service_parameter_type *service_parameter,
tag_type *identification)
{
return_type return_value;
asm volatile("pushl %2\n"
"pushl %3\n"
"pushl %4\n"
"lcall %5, $0"
: "=a" (return_value),
"=g" (*service_parameter)
: "g" (identification),
"g" (service_parameter),
"g" (protocol_name),
"n" (SYSTEM_CALL_SERVICE_GET << 3));
return return_value;
}
Das Problem mit dem Code oben ist, dass gcc
(4.7 in meinem Fall) dies an folgendem asm-Code kompiliert (AT & T-Syntax):
# 392 "../system/system_calls.h" 1
pushl 68(%esp) # This pointer (%esp + 0x68) is valid when the inline asm is entered.
pushl %eax
pushl 48(%esp) # ...but this one is not (%esp + 0x48), since two dwords have now been pushed onto the stack, so %esp is not what the compiler expects it to be
lcall $456, $0
# Restoration of %esp at this point is done in the called method (i.e. lret $12)
Das Problem: Die Variablen (identification
und protocol_name
) sind im aufrufenden Kontext auf dem Stapel. So gcc
(mit Optimierungen stellte sich heraus, unsicher, wenn es darauf ankommt) wird nur die Werte von dort bekommen und es in den Inline-ASM-Abschnitt übergeben. Aber seit ich Sachen auf den Stapel drängen, werden die Offsets, die gcc
berechnen, im dritten Aufruf um 8 deaktiviert(). :)
Das brauchte eine lange Zeit um herauszufinden, es war nicht alles offensichtlich für mich zuerst.
Der einfachste Weg, um dies zu erreichen, ist natürlich die Verwendung der r
Eingabebedingung, um sicherzustellen, dass der Wert stattdessen in einem Register ist. Aber gibt es einen anderen, besseren Weg? Eine naheliegende Möglichkeit wäre natürlich, die gesamte Systemaufrufschnittstelle neu zu schreiben, um nicht erst einmal Sachen auf den Stapel zu schieben (und stattdessen Register wie zB Linux zu verwenden), aber das ist kein Refactoring, ich fühle mich heute Abend ...
Gibt es eine Möglichkeit, gcc
Inline asm zu sagen, dass "der Stapel flüchtig" ist? Wie geht es euch in der Vergangenheit?
aktualisieren später am selben Abend: Ich habe ein relevantes gcc
ML Gewinde (https://gcc.gnu.org/ml/gcc-help/2011-06/msg00206.html) gefunden, aber es schien nicht zu helfen. Es scheint wie %esp
in der Clobber-Liste sollte machen es Offsets von %ebp
statt tun, aber es funktioniert nicht und ich vermute, die -O2 -fomit-frame-pointer
hat hier eine Wirkung. Ich habe beide Flags aktiviert.
IIRC auf die clobber Liste „cc“ und/oder „Speicher“ und fügte hinzu macht dies. Das Hinzufügen von volatile zu asm() verhindert manchmal eine Überoptimierung des Compilers. – technosaurus
Wie wäre es mit 'Push 8 +% 4'? –
Übrigens: [% rsp in der Clobber-Liste wird stillschweigend ignoriert] (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=52813). –