Wie finden vararg-Funktionen die Anzahl der Argumente im Maschinencode?

7

Wie können variadische Funktionen wie printf die Anzahl der Argumente herausfinden, die sie bekommen haben?

Die Menge der Argumente wird offensichtlich nicht als (versteckter) Parameter übergeben (siehe a Rufen Sie hier printf in asm auf.

Was ist der Trick?

    
masterxilo 11.03.2011, 12:08
quelle

4 Antworten

10

Der Trick ist, dass Sie ihnen irgendwie anders erzählen. Für printf müssen Sie eine Format-Zeichenfolge angeben, die sogar Typinformationen enthält (die jedoch möglicherweise nicht korrekt sind). Die Art und Weise, diese Informationen zu liefern, ist hauptsächlich benutzervertraglich und oft fehleranfällig.

Wie beim Aufruf von Conventions: Normalerweise werden die Argumente von links nach rechts auf den Stack geschoben und dann die Backjump-Adresse. Die aufrufende Routine löscht den Stapel. Es besteht also keine technische Notwendigkeit, dass die aufgerufene Routine die Anzahl der Parameter kennt.

EDIT: In C ++ 0x gibt es einen sicheren Weg (sogar typsicher!), um variadische Funktionen aufzurufen!

    
coldfix 11.03.2011, 12:14
quelle
9

Implizit aus der Formatzeichenfolge. Beachten Sie, dass stdarg.h keine Makros enthält, um die Gesamtzahl der übergebenen Argumente abzurufen. Dies ist auch einer der Gründe, warum die C-Aufrufkonvention verlangt, dass der Aufrufer den Stack bereinigt, obwohl dies die Codegröße erhöht.

    
Ruud Koot 11.03.2011 12:32
quelle
7

Aus diesem Grund werden Argumente in der C-Aufrufkonvention in umgekehrter Reihenfolge übergeben, z. B .:

Wenn Sie anrufen:

%Vor%

Der Stapel endet wie folgt:

%Vor%

Argumente werden indirekt unter Verwendung ihres Offsets vom Rahmenzeiger (der Rahmenzeiger kann von intelligenten Compilern weggelassen werden, die wissen, wie man Dinge vom Stapelzeiger berechnet). Das erste Argument befindet sich immer an einer bekannten Adresse in diesem Schema. Die Funktion greift auf so viele Argumente zu, wie es ihren ersten Argumenten sagt.

Versuchen Sie Folgendes:

%Vor%

Dadurch wird ein Teil des Stapels gelöscht.

    
ninjalj 14.03.2011 00:29
quelle
3
  • Der AMD64 System V ABI (Linux, Mac OS X) übergibt den Zahlenvektor (SEE / AVX) varargs in rax , im Gegensatz zu IA-32. Siehe auch: Warum wird% eax vor einem Aufruf von printf auf Null gesetzt?

    TODO warum ist das erforderlich? Ich denke, es ist nur aus Performance-Gründen, nicht unnötige SSE-Register in der "Register Save Area" zu speichern, die in "3.5.7 Variable Argument Lists" erwähnt wird.

  • Auf der C-Ebene gibt es auch andere Techniken als das Parsen der Formatzeichenfolge, wie von anderen erwähnt. Sie könnten auch:

    • Übergeben Sie ein Sentinel (void *)0 , um das letzte Argument wie execl anzugeben.

      Sie möchten das Funktionsattribut sentinel verwenden, um GCC bei der Kompilierung zu unterstützen: C Warnung Fehlender Sentinel im Funktionsaufruf

    • Übergeben Sie es als extra Integer Argument mit der Anzahl der Varargs

    • Verwenden Sie das Attribut format function, um GCC bei der Durchsetzung von Formatstrings bekannter Typen wie printf oder strftime

    • zu unterstützen
quelle

Tags und Links