2012-03-25 4 views
4

Ich spielte etwas herum, um die Aufrufkonventionen besser zu verstehen und wie der Stack gehandhabt wird, aber ich kann nicht herausfinden, warum main beim Einrichten des Stacks drei zusätzliche Doppelwörter zuweist (um <main+0>). Es ist weder auf 8 Bytes noch auf 16 Bytes ausgerichtet, weshalb ich nicht weiß, warum. Wie ich es sehe, benötigt main 12 Byte für die beiden Parameter func und den Rückgabewert.Stack-Zuweisung, warum der zusätzliche Platz?

Was fehlt mir?

Das Programm ist C-Code kompiliert mit "gcc -ggdb" auf einer x86-Architektur.

Edit: Ich entfernte die -O-Flag von gcc, und es machte keinen Unterschied für die Ausgabe.

(gdb) disas main 
Dump of assembler code for function main: 
    0x080483d1 <+0>: sub esp,0x18 
    0x080483d4 <+3>: mov DWORD PTR [esp+0x4],0x7 
    0x080483dc <+11>: mov DWORD PTR [esp],0x3 
    0x080483e3 <+18>: call 0x80483b4 <func> 
    0x080483e8 <+23>: mov DWORD PTR [esp+0x14],eax 
    0x080483ec <+27>: add esp,0x18 
    0x080483ef <+30>: ret  
End of assembler dump. 

Edit: Natürlich sollte ich den C-Code geschrieben haben:

int func(int a, int b) { 
    int c = 9; 
    return a + b + c; 
} 

void main() { 
    int x; 
    x = func(3, 7); 
} 

Die Plattform ist i686 Arch Linux.

+1

Es könnte hilfreich sein, den C-Code –

+0

zu posten Die Plattform wäre auch nützlich, da Sie nach Aufrufkonventionen fragen. Mac OS X erfordert beispielsweise, dass der Stapel auf 16-Byte-Grenzen ausgerichtet bleibt. –

+0

Es ist am besten anzunehmen, dass beim Deaktivieren von Optimierungen am Ende unoptimierter Code angezeigt wird. –

Antwort

2

Es ist Ausrichtung.Ich nahm aus irgendeinem Grund an, dass esp von Anfang an ausgerichtet sein würde, was es eindeutig nicht ist.

gcc richtet Stack Frames auf 16 Bytes pro Standard, was passiert ist.

+0

Der Stapel wird um 16 * vor einem 'Aufruf'-Befehl ausgerichtet, so dass die Argumente 16B-ausgerichtet sind. Wenn der Stack vor dem Zugriff auf "main" keine bekannte Ausrichtung hatte, würde gcc Code ausgeben, der 'und esp, -16' verwendet, um ihn auszurichten. –

4

Die Parameter einer Funktion (einschließlich, aber nicht beschränkt auf main) befinden sich bereits auf dem Stapel, wenn Sie die Funktion aufrufen. Der Platz, den Sie in der Funktion zuweisen, ist für lokale Variablen. Bei Funktionen mit einfachen Rückgabetypen wie int befindet sich der Rückgabewert normalerweise in einem Register (eax, mit einem typischen 32-Bit-Compiler auf x86).

Wenn zum Beispiel war main etwas wie folgt aus:

int main(int argc, char **argv) { 
    char a[35]; 

    return 0; 
} 

... wir würden erwarten, dass mindestens 35 Bytes auf dem Stack zugeordnet, um zu sehen, wie wir Haupt um Platz zu schaffen für a eingetragen. Unter der Annahme einer 32-Bit-Implementierung wird diese normalerweise auf das nächste Vielfache von 4 aufgerundet (in diesem Fall 36), um die 32-Bit-Ausrichtung des Stapels aufrechtzuerhalten. Wir würden nicht erwarten, dass für den Rückgabewert Platz zugewiesen wird. argc und argv wären auf dem Stapel, aber sie würden bereits auf dem Stapel sein, bevor main eingegeben wurde, also main würde nichts tun müssen, um Platz für sie zuzuordnen.

Im Fall oben, nach a für Raum Zuteilung würde a typicaly bei [esp-36] beginnen, argv bei [esp-44] wäre und argc bei [esp-48] wäre (oder die zwei rückgängig gemacht werden könnte - je nachdem, ob Argumente links geschoben rechts oder rechts nach links). Falls Sie sich wundern, warum ich [esp-40] übersprungen habe, wäre das die Absenderadresse.

Edit: Hier ist ein Diagramm des Stapels auf Eintritt in die Funktion, und nach dem Stapelrahmen einrichten:

enter image description here

Edit 2: Basierend auf Ihrer aktualisierten Frage, was Sie haben, ist leicht Kreisverkehr , aber nicht besonders schwer zu verstehen. Bei der Eingabe in main wird Speicherplatz nicht nur für die lokalen Variablen main zugewiesen, sondern auch für die Parameter, die Sie an die Funktion übergeben, die Sie von main aufrufen.

Das ist für mindestens einen Teil des zusätzlichen Speicherplatzes verantwortlich (obwohl nicht unbedingt alles).

+0

Aber main() hat nur drei lokale Ints, 12 Bytes. Warum also 24 Bytes? – spektre

+1

@spretre: Der Rest ist Padding, um den Stack auf 16 Bytes auszurichten. Versuchen Sie, 'func()' zu ändern, um weniger/mehr Argumente zu verwenden. Sie werden bemerken, dass 'main()' 's Stapelbereich sich in 16-Byte-Erhöhungen ändert (mit mehr Argumenten sehen Sie schließlich' sub esp, 0x28', dann mit mehr Argumenten wird es zu 'sub esp, 0x38' wechseln , ...). – ninjalj

+0

@ninjalj Alignment war das erste, an das ich dachte, aber es summierte sich nicht, da ich annahm, dass 'esp' bereits beim Eingeben von' main() 'ausgerichtet war. Obwohl ich jetzt darüber nachdenke, wäre eine Ausrichtung niemals notwendig, wenn es so wäre. Warum ist es dann nicht von Anfang an ausgerichtet? Sicherlich benutzt der Code, der vor der Eingabe von 'main()' läuft, den Stack, oder liegt dieser Code strikt in einem anderen Kontext? – spektre

Verwandte Themen