Parameterübergabe an Unterprogramme per Stack

01.09.2008
von Mario Rasser

Hierbei handelt es sich um eine Vortrag meines Studienkollegens Dipl. Inf. (FH) Frank Grimm zum Thema Parameterübergabe an Unterprogramme per Stack und eine Darstellung der Funktionsweise mit Beispielen.

Voraussetzung

  1. Es gibt ein Register, das auf eine Speicherstelle zeigt, die sich im sogenannten Stack befindet, es wird unter x86 mit esp bezeichnet.
  2. Der Stack in ein Stück Speicher, das sich im Speicherraum eines Prozesses befindet und dient zum Sichern von Werten, zur Wertübergabe an Unterprogramme und zum Anlegen von lokalen Variablen.
  3. Es gibt zwei Befehle, mit denen ein Operant auf dem Stack gesichert bzw. vom Stack geholt werden kann: push und pop.
  4. Jedes push/pop verändert den Stackzeiger (den Inhalt von esp).
  5. Auf x86 wächst der Stack von den hohen Adressen zu niedrigen Adressen auf den Heap zu.
  6. Mit den Befehlen push und pop kann nur nach dem “First In – Last Out”-Prinzip auf Werte im Stack zugriffen werden.
  7. Da esp eine Adresse auf dem Stack beinhaltet, kann aber per Zeigerarithmetik auch beliebig auf den Stack zugegriffen werden.

1. Parameter auf Stack pushen

Der letzte Parameter wird zu erst auf den Stack geschafft, die Parameter werden also von rechts nach links gesichert (gepusht) und können daher im Unterprogramm wieder von links nach rechts gelesen (pop) werden.

2. call zum Unterprogramm

Wie immer, zeigt der Befehlszeiger auf den Speicherplatz mit dem n&äuml;chsten Befehl im Hauptprogramm, dieser Wert des Befehlszeigers wird automatisch vom Befehl call auf den Stack gepusht.

3. Stack-Frame aufbauen und Unterprogramm-Befehle ausfuehren

  1. Wert des ebp-Registers (allgemeines Register) auf Stack sichern (pushen)
  2. Aktuelles esp-Register (Stackzeiger) in ebp sichern (per mov)

4. Verwendung des Stack-Frames

Falls lokale Variablen angelegt werden, wird das Stack-Register (esp) um die Grösse der gesamten lokalen Variablen erniedrigt (subtrahiert) (Stack wächst auf x86 von oben nach unten auf den Heap zu). Der Stackzeiger verweisst nun auf eine Speicheradresse, die völlig ungenutzt ist, damit nachfolgende Unterprogramm-Rufe im aktuellen Unterprogramm die aktuellen lokalen Variablen nicht verfälschen.

Der Zwischenraum auf Stack (ebp – esp) wird für lokale Variablen verwendet und die Parameter werden auf den Stack gepusht (der Stack-Zeiger verändert sich).

5. Stack-Frame-Abbau und Verlassen des Unterprogramms

  1. Beim Verlassen werden alle Stack-Veraenderungen rueckgaengig gemacht, was bedeutet, dass der Stackzeiger (esp) auf den Zustand vor dem Call zurückgesetzt (mit leave) wird. Das heisst: Beim Verlassen, wird der Befehlszeiger (ip) mit dem Wert geladen, auf den der Stackzeiger beim UP-Aufruf zeigte.
  2. Das Register ebp wird wieder mit dem Wert vor dem Sichern der Stack-Adresse auf ebp gefuellt (mit ret).

6. Der Rückgabewert eines Unterprogramms

Der Rückgabewert einer Funktion wird im allgemeinen Register eax an das Hauptprogramm zurueckgegeben.

7. Zusammenfassung

Beim Eintritt in das Unterprogramm zeigt der Stackpointer auf die Rücksprungadresse, das ist der nächste Befehl im Hauptprogramm und vor dem aktuellen Stackwert liegen die Parameter; im Unterprogramm kann der Stack veraendert werden, wird beim Verlassen aber wieder zurueckgesetzt

Beispiel

void foo()
{
int i = 99;
}
 
void bar(int i)
{
int j = i;
}
 
int add(int i, int j)
{
return i+j;
}
 
int main()
{
int i = 99;
foo();
bar(i);
i = add(1, 3);
}

Umsetzung in GNU(-x86)-Assember (AT&T-Notation, d.h.: Befehl Quelle Ziel):
(Assembercode erzeugt mit gcc -S stack.c)

foo:
 
pushl %ebp
movl %esp,%ebp
leave
ret
 
bar:
 
pushl %ebp
movl %esp,%ebp
subl $24,%esp
movl 8(%ebp),%eax
movl %eax,-4(%ebp)
leave
ret
 
add:
 
pushl %ebp
movl %esp,%ebp
movl 8(%ebp),%eax
movl 12(%ebp),%ecx
leal (%ecx,%eax),%edx
movl %edx,%eax
leave
ret
 
main:
 
pushl %ebp
movl %esp,%ebp
subl $24,%esp
movl $99,-4(%ebp)
call foo
addl $-12,%esp
movl -4(%ebp),%eax
pushl %eax
call bar
addl $16,%esp
addl $-8,%esp
pushl $3
pushl $1
call add
addl $16,%esp
movl %eax,%eax
movl %eax,-4(%ebp)
leave
ret

Copyrigth Dipl. Inf. (FH) Frank Grimm

(Archiviert aus unseren alten Tipps und Tricks Sektion.)

Kommentare sind geschlossen.

© 2003-2017 Fa. ipunct - IT-Lösungen auf den Punkt gebracht