RE: Arbeitsspeicher
Wurde so vereinbart.
Als Stack wird einfach ein freier Speicherbreich genommen - Anfangs und Endadresse sind dann Stackanfangsadresse und Stackendadresse.
Durch die SEGMENT Deklarierungen sagst du dem Assembler, dass er die Daten in unterschiedlichen maximal auf einmal ansprechbaren Speicherbereichen (Segmenten) platzieren soll.
Zu der Trennung:
In 32-Bit Asm kann man nun alles durcheinander schreiben:
der Assembler bringt dann .data Einträge zusammen in einem Block(genauso mit .code)
und platziert die Blöcke so, wie er lustig ist (Codebereich vor Daten oder auch andersrum).
Es gibt keinerlei Trennung durch Markierungen - alles wird linear in den Speicher geladen. D.h wenn du am Ende deines Asm Codes keinen RET/JMP "ENDE" hast und danach Daten folgen, gibt es lustige Fehlermeldungen, wenn die CPU versucht, Daten als Anweisungen zu interpretieren.
also:
Wenn du in Windows ein Programm startest, dann ist in EBP die "Stackbasis" und ESP verweist auf den aktuellen "oberen" Wert. Die Daten, die zu diesem Zeitpunkt auf dem Stack liegen, gehören dem Aufrufer (also Windows). Nach konvention wird man also vor der Nutzung des Stacks erstmal die alten Werte sichern und den Stackspeicher reservieren (also PUSH EBP; MOV EBP,ESP; ADD ESP,Größe; usw).
aber was ist mit
?
Sofern man nur eigene Funktionen nutzt, kann man sich als Asmprogrammierer noch irgendwas überlegen - als Compilerentwickler oder Nutzer von Fremdfunktionen muss man allerdings an eine Konvention halten. Eine gebräuchliche Konvention ist also, den
Stackzustand nach dem Funktionsaufruf genauso zurückzugeben, wie er vor dem Aufruf war. D.h wenn man lokale Variablen hat, spricht man sie in Asm für gewöhnlich mit [EBP-Position] an (genauso übersetzt es auch meistens der Compiler). Wenn man den EBP Wert vor der Codeausführung nicht sichert, hat man 2 Optionen - diesen Überschreiben (dann erhält der Aufrufer einen anderen Wert als er vor dem Funktionsaufruf hatte - und findet seine lokalen Variablen nicht mehr) oder man verändert EBP Wert nicht. Dann kann man aber in dieser Funktion lokale Variablen nicht per [EBP-Position] ansprechen, weil der EBP Wert ja nicht bekannt ist und je nach dem, von welcher anderen Funktion diese Funktion aufgerufen wurde, unterschiedlich.
Also sicher man den alten EBP, kopiert den ESP darauf (damit bildet EBP also die Basisstackadresse in der Funktion) und kann lokale Variablen reservieren oder andere Sachen machen. Zum Schluss stellt man den alten Zustand wieder her.
siehe das Wort Konvention.Wieso wächst der von unten nach oben?Stack "wächst" von unten nach oben - von größeren Adressen zu kleineren. Das ist aber nur eine Konvention. D.h der Stack hat nicht dieselbe Adresse wie der Code/Daten, sondern ist ein anderer Speicherbreich.
Wurde so vereinbart.
in deinem Bild hat Stack wieder die Adresse 0001, die ja schon vom Code belegt ist.Und was genau meinst du mit "der Stack hat nicht dieselbe Adresse wie der Code/Daten??Haben die etwa ne spezielle Adresse? Oder meintest du Adressbereich??
Denn so wie du das im Beispiel gezeigt hast, sind Code/Daten und Stack genauso hintereinander angeordnet.
Als Stack wird einfach ein freier Speicherbreich genommen - Anfangs und Endadresse sind dann Stackanfangsadresse und Stackendadresse.
Du brinst jetzt zwei unterschiedliche Speicherzugriffsverfahren durcheinander. Segmentierung so wie es im Helloworld steht ist eine DOS Krücke, um mehr Specher anzusprechen. Denn in 16-Bit Registern kann man "as is" erstmal auch nur Werte bis 65535 haben. Also hat man noch Segmentregister eingefügt - aus dem Wert im Segmentregister und dem im 16-Bit "normalregister" setzt sich dann die tatsächliche Adresse - man kann also nicht nur 65k Speicher ansprechen, sondern irgendwas mit 1MBDer Assembler trennt Code und Daten durch seperate Segmente voneinander, richtig?
Also sprich: Mein Programm besteht aus 2 Segmenten. Wo aber stehen infos zu diesen Segmenten (Anfangs/End - Adresse)?
Die Segmente sind ja wohl nicht einfach durch ein paar "leere Adressen" voneinander getrennt, oder?
Und das selbe ist dann mit dem Stack, oder? Der ist dann einfach ein drittes Segment, oder?
Durch die SEGMENT Deklarierungen sagst du dem Assembler, dass er die Daten in unterschiedlichen maximal auf einmal ansprechbaren Speicherbereichen (Segmenten) platzieren soll.
Zu der Trennung:
In 32-Bit Asm kann man nun alles durcheinander schreiben:
Code:
.data
x dd 0
.code
mov eax,x
.data
hello db "helloworld",0
.code
add eax, offset hello
call ausgabe
und platziert die Blöcke so, wie er lustig ist (Codebereich vor Daten oder auch andersrum).
Es gibt keinerlei Trennung durch Markierungen - alles wird linear in den Speicher geladen. D.h wenn du am Ende deines Asm Codes keinen RET/JMP "ENDE" hast und danach Daten folgen, gibt es lustige Fehlermeldungen, wenn die CPU versucht, Daten als Anweisungen zu interpretieren.
also:
nein.So stelle ich mir das vor:
Beispielprogramm:
Code:0001: code 0002: code 0003: code 0004: code 0005: leere Adresse <---- hier ist der Zwischenraum der Segmente Code - Daten
Es kommt auf die Konvention an, an die sich der Compiler/Asmprogrammierer hält.Es ist nur so, dass bestimmte CPU Anweisungen (push, pop,call,ret) automatisch die Werte aus PTR [ESP] auslesen und den ESP Register modifizieren.Steht die Stackanfangsadresse im ESP oder im EBP ?
Wenn du in Windows ein Programm startest, dann ist in EBP die "Stackbasis" und ESP verweist auf den aktuellen "oberen" Wert. Die Daten, die zu diesem Zeitpunkt auf dem Stack liegen, gehören dem Aufrufer (also Windows). Nach konvention wird man also vor der Nutzung des Stacks erstmal die alten Werte sichern und den Stackspeicher reservieren (also PUSH EBP; MOV EBP,ESP; ADD ESP,Größe; usw).
Wenn man seinen Code nur in der Mainfunktion unterbringt, bringt es tatsächlich nichts.Zu deinem Codebeispiel der Funktion habe ich auch noch eine Frage:
Verstehe ich nicht...push ebp ;alten Wert sichern
Wenn EBP beim Programmstart eine Adresse (Anfangsadresse des Stacks) beinhaltet, dann bewirkt dieser Befehl doch nur, dass diese Adresse als Wert in eine neue Adresse auf den Stack gelegt wird, oder? Aber was bringt das??
aber was ist mit
Code:
int main()
{
l337_output("hello");
return 0
}
void l337_output(String str)
{
for (int i=0;i<str.len();i++)
{
if (str[i]=='e') str[i]='3';
if (str[i]=='o') str[i]='0';
}
printf("%s",str);
}
Sofern man nur eigene Funktionen nutzt, kann man sich als Asmprogrammierer noch irgendwas überlegen - als Compilerentwickler oder Nutzer von Fremdfunktionen muss man allerdings an eine Konvention halten. Eine gebräuchliche Konvention ist also, den
Stackzustand nach dem Funktionsaufruf genauso zurückzugeben, wie er vor dem Aufruf war. D.h wenn man lokale Variablen hat, spricht man sie in Asm für gewöhnlich mit [EBP-Position] an (genauso übersetzt es auch meistens der Compiler). Wenn man den EBP Wert vor der Codeausführung nicht sichert, hat man 2 Optionen - diesen Überschreiben (dann erhält der Aufrufer einen anderen Wert als er vor dem Funktionsaufruf hatte - und findet seine lokalen Variablen nicht mehr) oder man verändert EBP Wert nicht. Dann kann man aber in dieser Funktion lokale Variablen nicht per [EBP-Position] ansprechen, weil der EBP Wert ja nicht bekannt ist und je nach dem, von welcher anderen Funktion diese Funktion aufgerufen wurde, unterschiedlich.
Also sicher man den alten EBP, kopiert den ESP darauf (damit bildet EBP also die Basisstackadresse in der Funktion) und kann lokale Variablen reservieren oder andere Sachen machen. Zum Schluss stellt man den alten Zustand wieder her.