Problem mit NASM?
Ich bekomme einen Speicherzugriffsfehler bei einem Teil meiner IOTA Methode. Diese Funktion ermittelt die Länge einer Zahl, die im eax Register später steht. Das funktioniert auch, das Ergebnis ist richtig, nur ich bekomme dann einen nervigen Speicherzugriffsfehler.
Die Methode wird folgendermaßen angesteuert:
push edi
push ebx
push esi
mov esi, 0 mov edi, eax
call len_count
len_count: mov eax, edi mov ebx, 10 idiv ebx call resetcalc
mov edi, eax
cmp eax, 0 je len_iszero inc esi jne len_count
len_iszero: mov eax, esi add eax, 1 pop esi pop ebx pop edi ret
Die Methode steuere ich so an:
mov eax, 15024
call len
In len_iszero ist eax beim Abschluss der Methode 5, logisch, die Zahl ist fünfstellig. len_iszero läuft komplett durch und dann gibt es einen Speicherzugriffsfehler.
Besser zum lesen weil GF so genial ist *NICHT*:
len:;eax Zahl und später Länge
push edi
push ebx
push esi
mov esi, 0
mov edi, eax
call len_count
len_count:
mov eax, edi
mov ebx, 10
idiv ebx
call resetcalc
mov edi, eax
cmp eax, 0
je len_iszero
inc esi
jne len_count
len_iszero:
mov eax, esi
add eax, 1
pop esi
pop ebx
pop edi
ret
2 Antworten
Ich habe zwar keine Ahnung, was die resetcalc Funktion macht, aber ich glaube die Zeile 'call len_count' ist der Übeltäter.
Die Sprungposition 'len_count' muss eigentlich nicht mit 'call' aufgerufen werden, ich glaube diese Zeile 'call len_count' ist überflüssig.
Das führt dazu das die Rücksprungadresse nach der Instruktion 'call len_count' auf den Stack gelegt wird, und scheinbar nie wider heruntergekommen wird.
Das wiederum führt dazu das die Werte in edi, ebx und esi falsch wiederhergestellt werden und wenn die Funktion wider zurückspringen will, die Adresse vom Register edi nimmt.
Nicht in der Routine, aber durch den Aufruf mit 'call len_count' legst du die Rücksprungadresse auf den Stack.
Diese wird normalerweise mit 'ret' wider vom Stack genommen, aber in deinem Code scheinst du am Ende der 'len_count' Routine mit 'je len_iszero' rauszuhüpfen.
Die Rücksprungadresse wird nie vom Stack genommen, und das führt zu dem in meiner Antwort beschriebenen Fehler.
So wie ich das sehe, kannst du die Zeile 'call len_count' entfernen oder durch ein 'jmp len_count' ersetzen.
Also wenn ich call mache, muss ich immer mit ret zurück, weil sonst die Rücksprungsadresse im Stack für immer verbleibt und jmp nutzt nicht den Stack?
Die jmp springt einfach nur zu einer Adresse.
Die call legt den instruktion pointer (eip) auf den Stack und die ret nimmt diesen wider vom Stack.
Hallo,
die Frage ist zwar schon einige Tage alt aber ich denke hier werden sicherlich noch weitere interessierte Leser aufmerksam.
Der Fehler liegt "auf dem Stack":
- Es werden die Register edi, ebx und esi auf den Stack abgelegt.
- Mit "call len_count" wird die Rücksprungadresse auf den Stack gespeichert. Somit wird der Stackpointer auch weitergeschoben und zeigt nicht mehr auf den Inhalt des esi-Registers (der letzte push-Befehl).
- Die pop-Befehle am Ende des Programms holen deshalb nicht die oben gesicherten Register wieder vom Stack, sonder die Rücksprungadresse des call-Befehls worauf der ret-Befehl im Nirwana landet.
So, ich hoffe ich konnte hier einigen Lesern weiterhelfen.
MatzeL aus H.
Ich lege in der len_count Routine doch nichts auf den Stack? resetCalc setzt nur mit XOR eax und ebx zurück. Die Subroutine laufen durch und liefern auch das richtige Ergebnis.
Es war nicht immer len_count, hatte davor eine extra Subroutine, die nichts anderes gemacht hat, als len_count wieder aufzurufen, selber Fehler. Ich brauche ja hier eine Art Schleife, es wird ja solange durch 10 dividiert bis das Ergebnis null ist. Zähler + 1 ist dann die Länge meiner Zahl.