Unterschied bei normaler Funktion und stdcall-Funktion
-
Hallo,
ich habe mich schon seit längerem gefragt, welcher Unterschied zwischen einer normalen Funktion ala:
int foo(param1, param2)und einer stdcall-Funktion ala:
int __stdcall foo(param1, param2)liegt.
Hat dies Auswirkungen auf den Stackaufbau (da ich auch mit asm arbeite)? Und wann ist es angebracht stdcall zu verwenden? Finde häufig folgende Funktion in Quellcoden:extern int __stdcall foo(char*,int);.
Hoffe jemand kann mir dazu mal nähere Informationen auch in Bezug auf Stackaufbau geben kann. Zum Stackaufbau von stdcall-Funktionen habe ich folgendes gefunden:
Rest vom Stack
Param2
Param1
Rücksprungaddresse
saved ebp <-- ebp
lokale Variable 1
lokale Variable 2
...
lokale Variable n
saved ebx
saved esi
saved edi <-- espVielen Dank
mirrowwinger
-
Erster Link zu google "stdcall": http://de.wikipedia.org/wiki/Aufrufkonvention
Da klickt man dann wie bei allen Informatikthemen links auf "Englisch".
http://en.wikipedia.org/wiki/X86_calling_conventionsRest vom Stack
Dein Stack wächst nach unten? Krass.
ebp, ebx, esi, edi mußte jeweils nur pushen, wenn Du sie selber verändern willst.
-
<a href= schrieb:
testStdcall(int, int): push ebp mov ebp, esp mov edx, DWORD PTR [ebp+8] mov eax, DWORD PTR [ebp+12] add eax, edx pop ebp ret 8 testCdecl(int, int): push ebp mov ebp, esp mov edx, DWORD PTR [ebp+8] mov eax, DWORD PTR [ebp+12] add eax, edx pop ebp ret main: push ebp mov ebp, esp sub esp, 16 push 3 push 1 call testStdcall(int, int) mov DWORD PTR [ebp-4], eax push 3 push 1 call testCdecl(int, int) add esp, 8 mov DWORD PTR [ebp-8], eax mov eax, 0 leave retIch hab das ein bischen formatiert.
Der wichtige Unterschied ist, wer den Speicher die Parameter anlegt/aufräumt.
Zuerst werden zwei 4-Byte integer auf den Stack gepusht. Dann wird die Funktion aufgerufen.
Bei stdcall ist das Funktionsende ein "ret 8", d.h. esp wird verschoben. Bei stdcall räumt also der Callee auf.
Bei cdecl ist das Funktionende ein "ret", dafür kommt direkt nach dem Funktioncall ein "add esp, 8". Bei cdecl räumt also der Caller auf.Dadurch dass bei cdecl der Caller aufruft, werden z.B. variable Parameterlisten möglich. Bei stdcall geht das so nicht, da der Callee aufräumen müsste, wenn er nicht weiß wieviele Parameter er bekommt wird das schwer.
Zur Verwendung der Callingconventions: Normal verwendet mal Cdecl (zumindest machen dass die meisten Compiler per default).
Wenn du asm schreibst musst du halt drauf achten dass die Verwendung konsistent ist, wenn eine Funktion als cdecl aufgerufen wird sollte sie nicht den Stack aufräumen und andersrum. Wenn du asm schreibst würde ich mich einfach für eins entscheiden (je nach Anforderungen) und dann konsistent bleiben, dann kommst du schon nicht durcheinander.
-
First Things first:
@DarkShadow44 danke, top Erklärung. Werde sicherlich noch den ein oder anderen Moment brauchen und auch das Beispiel richtig durcharbeiten müssen, um alles richtig zu verstehen. Aber danke für das ausführliche Beispiel.@volkard auch ein Danke, dass du entsprechende Wiki-Einträge für mich bereit gestellt hast. Aber auf dein Kommentar, dass mein Stack nach unten wächst möchte ich kurz eingehen. wie ich geschrieben hatte:
Zum Stackaufbau von stdcall-Funktionen habe ich folgendes gefunden:
hat, glaube ich, den aufmerksamen Leser suggeriert, dass MEIN STACK das wohl nicht sein kann. Ich schätze dein Wissen über den korrekten Aufbau des Speichermanagments. Deswegen möchte dir deswegen hiermit die Quelle auftun, aus der ich diesen Aufbau entnommen habe und überlasse dir MSDN darüber zu belehren, wie der Stack aufgebaut ist.
http://blogs.msdn.com/b/oldnewthing/archive/2004/01/16/59415.aspx
Für mich als Laie hat sich (vieleicht bin ich auch zu naiv) folgender Sinnvoller Aufbau des Stacks ergeben:HOHE SPEICHERADRESSE
Rest vom Stack
Param2
Param1
Rücksprungaddresse
saved ebp <-- ebp
lokale Variable 1
lokale Variable 2
...
lokale Variable n
saved ebx
saved esi
saved edi <-- espNIEDRIGE SPEICHERADDRESSE
Der Stack wachst also immer noch von (optisch dargestellten) HOHEN Speicheradressen zu NIEDRIGEREN. Sicherlich muss man für die korrekte Darstellung noch um 180° drehen, aber ich hab mich nicht sonderlich daran gestört.
mirrowwinger