C Calling Conventions und Parameterübergabe
-
Würde jemand mal über folgendes kleines Programm schauen und es verbessern? Es funktioniert nicht, aber es liegt an mir, da ich die Parameterübergaben nicht so recht verstehe. Es geht darum die jeweiligen Calling Conventions zu beachten. Es sollen jeweils die in den Kommentaren rechts neben den Assemblerbefehlen stehenden Werte in die gleichnamigen Parameter der jeweiligen Print-Funktion übergeben werden.
// __fastcall: // param #1: ecx, // param #2: edx; // param #3 and up: stack void __fastcall print_fastcall(char c, short s, int i, double f) { printf("%c, %d, %d, %f\n", c, s, i, f); } void asm_fastcall(void) { _asm { mov ecx, 'x' // c = 'x' mov edx, 0x0c // s = 0ch = 12d push 0x2000 // i = 2000h = 8192d push 0x0000 // f = 2.138d call print_fastcall // call print function } } // __cdecl: // param #1 and up: stack void __cdecl print_cdecl(char c, short s, int i, double f) { printf("%c, %d, %d, %f\n", c, s, i, f); } void asm_cdecl(void) { _asm { push 'x' // c = 'x' push 0x0c // s = 0ch = 12d push 0x2000 // i = 2000h = 8192d push 0xabcd // f = 2.138d call print_cdecl // call print function } } // __stdcall: // param #1 and up: stack void __stdcall print_stdcall(char c, short s, int i, double f) { printf("%c, %d, %d, %f\n", c, s, i, f); } void asm_stdcall(void) { _asm { push 'x' // c = 'x' push 0x0c // s = 0ch = 12d push 0x2000 // i = 2000h = 8192d push 0xabcd // f = 2.138d call print_stdcall // call print function } } int main(void) { asm_fastcall(); asm_cdecl(); asm_stdcall(); return 0; }
-
Die Parameteruebergabe auf den Stack passiert von rechts nach links (siehe hier).
Doubles musst Du als zwei separate dwords pushen (oder float nehmen):void __fastcall print_fastcall(char c, short s, int i, double f) { printf("%c, %d, %d, %f\n", c, s, i, f); } void asm_fastcall(void) { double f = 2.138; _asm { mov ecx, 'x' mov edx, 0x0c lea edi, f push dword ptr [edi+4] push dword ptr [edi] push 0x2000 call print_fastcall } }
Bei _cdecl musst Du die Parameter selber wieder vom Stack raeumen.
-
Hab mal ne Frage zu dem "push dword ptr [edi]", wie ist das zu interpretieren? Wieso gerade das Register EDI? verweißt EDI auf locale Variablen? Bzw. macht man das nicht eigentlich immer mit dem EBP?
Gruß Tobi
-
Mit dem Befehl "lea" wird die Adresse der Variablen "f" in das Register "edi" explizit geladen und dient liedlich zur Verdeutlichung dass man bei groesseren Datentypen eher mit Referenzen/Pointer arbeiten wuerde. Das Register selbst ist hier vollkommen zufaellig gewaehlt. Aus historischen Gruenden bevorzugen viele esi/edi zur Adressierung.
Da "f" hier (absolut willkuerlich) eine lokale Variable (Stack-Frame) ist, wuerde sich deren Adresse auch relativ zu "ebp" ergeben (erledigt der Compiler selbst).
Man koennte genausogut schreiben:push dword ptr [f+4] push dword ptr [f]
-
hellihjb könntest du mir noch explizit schreiben wie das Poppen vom Stack nach dem Aufruf der Funktion mit der Convention __cdecl funktioniert. Also das Programm von oben noch mal nehmen? Ich hab es gestern geschafft, den Stack zu poppen und damit mein Programm-Thread unkillbar zu machen. Selbst der Taskmanager war nicht mehr in der Lage den Prozess zu killen. Vermutlich habe ich irgendwelche Rücksprungadresse vom Stack einfach mal weggepoppt. Wäre sehr nett.
-
Du pusht ja 5 Dwords auf den Stack (wobei der Stack-Pointer dekrementiert) und musst die hinterher wieder "runterpoppen" was effektiv nichts anderes ist als den Stack-Pointer wieder entsprechend zu erhoehen:
void __cdecl print_cdecl(char c, short s, int i, double f) { printf("%c, %d, %d, %f\n", c, s, i, f); } void asm_cdecl(void) { double f = 2.138; _asm { push dword ptr [f+4] push dword ptr [f] push 0x2000 // i = 2000h = 8192d push 0x0c // s = 0ch = 12d push 'x' // c = 'x' call print_cdecl // call print function add esp,4*5 } }
-
Ok, danke. Noch was. Auch wenn das jetzt ne doofe Frage ist. Warum ist 'x', 0x0c, 0x2000 ein dword? 'x' ist ein Byte lang, 0x0c ebenfalls. Und 0x2000 ist ein Word lang. Oder pusht er automatisch immer ein Dword auch wenn der Inhalt eigentlich kein Dword ist?
Edit: Und in der Definition ist c ja auch als Char deklariert also auch kein Dword!
-
Wie hier nachzulesen ist:
All arguments are widened to 32 bits when they are passed
-
Ah ok. Wenn ich das jetzt also richtig verstehe, dann passiert folgendes:
esp-offset stack printf -0 hier wollen wir wieder zurück --- -0 ... -4 dword ptr [f+4] castet das intern -4 ... -8 dword ptr [f] nach double -8 ... -12 0x2000 castet das intern nach int -12 ... -16 0x0c castet das intern nach short -16 ... -20 'x' castet das intern nach char
-
Es ist nicht notwendig zu casten.
Die Parameter stehen in gegebener Reihenfolge im Speicher.
Die Variablen und deren Groessen sind dem Compiler durch die Parameterliste bekannt und er liesst diese beim Zugriff einfach an der entsprechenden Speicherstelle aus.
-
Ich hab mal noch eine Frage und zwar zur __thiscall convention.
ECX bekommt die Referenz zum Klasseobject, und wie wird der rest also die Parameter mitgeteilt?typedef void (__thiscall *fpFunc)( int ); fpFunc fpfunc = (fpSetNewValue)(0x12345678); _asm mov ecx, dword ptr ds:[0x87654321] fpNew( 2 );
So gehts nicht. Aber wie dann?
Gruß Tobi
-
Hat sich erledigt.