(ASM) Wie spielen Stackpointer und Datengröße zusammen?
*Markus
- sonstiges
0 *Markus1 Der Martin0 *Markus
Hallo,
Es passt zwar nicht unbedingt hier rein, aber ich bin sicher, dass mich jemand diesbezüglich aufklären kann. Der folgende Code funktioniert:
segment .code
msg1 db "Message 1", 10
len1 equ $-msg1
msg2 db "Message 2", 10
len2 equ $-msg2
segment .text
global _start
_start:
mov eax, msg1
push eax
mov eax, msg2
push eax
mov eax, 4
mov ebx, 1
mov ecx, [esp+4]
mov edx, len1
int 0x80
mov eax, 1
int 0x80
Eines verstehe ich nicht. Wieso muss ich den Stackpointer gerade um vier erhöhen, um an den ersten String zu gelangen? Ich verstehe den Zusammenhang zwischen dem definierten Byte (das ja die Adresse des Strings enthält, wenn ich es richtig verstanden habe) und dem Platzverbrauch (offensichtlich 4 Adressen) auf dem Stack nicht.
Ich bin mal gespannt, wo mein Denkfehler ist.
Nachtrag:
Offensichtlich stand ich wirklich auf dem Schlauch.
Der Platz des kompletten Registers sind 32bit, also 4byte. Ich vergaß wohl, dass der Inhalt des kompletten Registers auf dem Stack abgelegt wird. Somit ist das mit den vier Bytes auch klar.
Hallo Markus,
Es passt zwar nicht unbedingt hier rein, aber ich bin sicher, dass mich jemand diesbezüglich aufklären kann.
ich will's versuchen. ;-)
Lass mich zuerst noch mal laut denken: Im Lunux-Kernel ruft int 0x80 etliche Systemfunktionen auf, in eax wird die Funktionsnummer angegeben. Funktion #4 scheint eine Stringausgabe zu sein, bei der in ebx der Filehandle (hier 1=stdout), in ecx ein Zeiger auf den String und in edx die Länge des Strings übergeben wird.
Also los.
segment .code
msg1 db "Message 1", 10
len1 equ $-msg1
msg2 db "Message 2", 10
len2 equ $-msg2segment .text
global _start_start:
mov eax, msg1
push eax ; lege einen Zeiger auf msg1 auf dem Stack ab
mov eax, msg2
push eax ; lege einen Zeiger auf msg2 auf dem Stack ab
Bis hier war's Vorgeplänkel, das mit dem eigentlichen Funktionsaufruf nichts zu tun hat.
mov eax, 4 ; Funktionsnummer
mov ebx, 1 ; File-Handle (stdout)
mov ecx, [esp+4] ; Zeiger auf String (*)
mov edx, len1 ; Stringlänge
int 0x80 ; Funktionsaufruf
mov eax, 1 ; Funktionsummer für "Prozess beenden"
int 0x80 ; Funkion ausführen
Eines verstehe ich nicht.
Eines verstehe _ich_ nicht: Wozu legst du erst zwei Werte auf den Stack? Das erscheint mir völlig zweckfrei, und in diesem Fall unnötig. Da die beiden Werte nie wieder vom Stack abgeräumt werden, ist das genaugenommen sogar ein schwerwiegender Fehler, der nur deshalb keine Schutzverletzung (o.ä.) verursacht, weil das Programm danach sowieso beendet wird, ohne nochmal auf Werte von Stack (z.B. Rücksprungadressen) zurückzugreifen.
Okay, zurück zur eigentlichen Frage: Nehmen wir mal an, der Stackpointer esp habe zu Beginn dieses Codestücks den Wert 00001000h.
Bei einem push-Befehl wird erst der Stackpointer um die Anzahl der gepushten Bytes vermindert (hier 4, den es wird ein 32bit-Register gepusht), dann der gepushte Wert an die Adresse geschrieben, auf die der Stackpointer nun zeigt.
Nach den beiden push-Befehlen haben wir also folgende Bestandsaufnahme:
esp = 00000FF8h
Stack: 00001000: [Inhalt unbekannt]
00000FFC: Zeiger auf msg1
00000FF8: Zeiger auf msg2
So. Wenn du jetzt 'mov ecx,[esp+4] ausführst, dann lädst du den Wert, an der Speicheradresse esp+4, also im Moment 00000FFCh steht, ins ecx-Register. Das ist "zufällig" genau der Zeiger auf msg1, den du vorher per push-Befehl dort abgelegt hast.
So isoliert erscheint mir dieses Code-Beispiel aber ziemlich sinnlos, vom vorher beschriebenen Stackfehler ganz abgesehen. Hast du das eventuell aus einem größeren Zusammenhang gerissen oder Teile herausgekürzt, ohne genau zu wissen, was du tust?
Ich bin mal gespannt, wo mein Denkfehler ist.
Ich hoffe, du erkennst ihn jetzt.
So long,
Martin
Hallo,
Eines verstehe _ich_ nicht: Wozu legst du erst zwei Werte auf den Stack? Das erscheint mir völlig zweckfrei, und in diesem Fall unnötig. Da die beiden Werte nie wieder vom Stack abgeräumt werden, ist das genaugenommen sogar ein schwerwiegender Fehler, der nur deshalb keine Schutzverletzung (o.ä.) verursacht, weil das Programm danach sowieso beendet wird, ohne nochmal auf Werte von Stack (z.B. Rücksprungadressen) zurückzugreifen.
Das war auch nur ein Testbeispiel. Das Programm hat keinen tieferen Sinn. Bezüglich des Abräumens des Stacks hast du allerdings recht. Würde ich ein "add esp, 8" vor "pop eax, 1" schreiben, so würde das Programm m.M.n. "sauber" ausgeführt werden, falls ich das mit meinen Assembler-Newbiefähigkeiten richtig erkannt habe.
So isoliert erscheint mir dieses Code-Beispiel aber ziemlich sinnlos, vom vorher beschriebenen Stackfehler ganz abgesehen. Hast du das eventuell aus einem größeren Zusammenhang gerissen oder Teile herausgekürzt, ohne genau zu wissen, was du tust?
Nein, ich experimentiere nur ein wenig herum. Für das Schreiben eines Treibers reicht mein Wissen leider noch nicht aus. :)
Ich hoffe, du erkennst ihn jetzt.
Danke, jetzt ist es mir absolut klar.
Hi Markus,
Würde ich ein "add esp, 8" vor "pop eax, 1" schreiben, so würde das Programm m.M.n. "sauber" ausgeführt werden, falls ich das mit meinen Assembler-Newbiefähigkeiten richtig erkannt habe.
richtig, ja.
Nein, ich experimentiere nur ein wenig herum. Für das Schreiben eines Treibers reicht mein Wissen leider noch nicht aus. :)
Übung macht den Meister! *g*
Schönen Tag noch,
Martin