Speicherzugriffsfehler in Assembler?
Hey ich habe folgenden rekursiven Fibonacci Code geschrieben. Ich erhalte beim ausführen immer einen Speicherzugriffsfehler
Pastebin-Link: https://pastebin.com/qNWcyBa5
.intel_syntax noprefix
.section .data
n: .quad 10 # define the fibonacci number that should be calculated
.section .text
.global _start
_start:
# call Fibonacci function f(n)
push [n] # parameter: fibonacci number to calculate
call f # call function
add rsp, 8 # remove parameter from stack
# print calculated Fibonacci number on stdout
#call printnumber
# exit process with exit code 0
mov rax, 1
mov rbx, 0
int 0x80
# f: Calculates a Fibonacci number
# f(n) = {n, if n<=1; f(n-1)+f(n-2), else}.
# Parameter: Integer n >= 0, passed on stack
# Returns: Fibonacci number f(n), returned in rax
.type f, @function
f:
push rbp # basepointer auf stack pushen
mov rbp, rsp # basepointer bekommt Wert des stackpointers
add rbp, 8 # 1.Parameter
mov rbx, [rbp] # rbx= 1.Paramter
cmp rbx, 0 # wenn n=0 passiert nichts, jmp zu end
je end
cmp rbx, 1 # wenn n=1 jmp zu baseone
je baseone
dec rbx # n-1
push [rbx] # push n-1
call f # call f(n-1)
dec rbx # n-2
push [rbx] # push n-2
call f # call f(n-2)
baseone: add rdi, 1 # rdi = rdi + 1
end: pop rbp # basepointer wieder vom Stack entfernen
ret
weiß auch nicht warum gf hier so viele Leerzeichen reinmacht. Naja hab das meiste kommentiert. Also wenn jemand einen Tipp hat wäre ich sehr dankbar.
Wer es kompilieren will.
as --gstabs -o Dateiname.o Dateiname.asm
ld -m elf-x86-64 -o Name Dateiname.o
./Name
Lg Tischtennisftw
3 Antworten
push[rbx] # push n-1
Ich könnte mir vorstellen, dass das hier das Problem ist, du pushst ständig etwas in den Stack, könnte sein, dass er irgendwann überläuft.
Wo genau definierst du den Stack?
In unserem Modul MRT (Mikrorechnertechnik) haben wir immer einen Stack-Segment definieren müssen, wo wir auch sagen mussten wie groß der ist (ich weiß nicht mehr genau wie die Syntax war):
.stack 16
16 stand dann für die Größe des Stacks. 16 Byte z.B.
Was mein anderen Einwand angeht ist das so gemeint:
Sagen wir das n=3, dann wird dein Stack vor dem nächsten Call doch so aussehen (bzw. er wäre von obennach unten angeordnet denke ich, ist aber unergeblich):
{bsp1 <- stack pointer
ret1
3}
nach dem zweiten call und kurz vor dem 2 call sehe der Stack dann doch so aus:
{(bsp2) <- stack pointer
ret2
2
bsp
ret1
3}
usw.
Dann wäre der Stack doch irgendwann voll bei hinreichend großem n, oder nicht?
Ja sicherlich, aber sein n ist bei 10, sodaß insgesamt c.a. 2^10 Stackframes angelegt werden sollten. selbst wenn ich großzügig mit 64 Bytes rechne, sind das gerade 64KB. Der normale Stackframe bei Linux sollte bei 8 MB liegen und auch bei Windows ist er 1(?)MB, wenn ich es recht im Kopf habe.
Wenn also kein extrem grober Unfug bei der Stackverwaltung passiert, dann sollte die Tiefe nicht das Problem sein, denn:
Im Normallfall wird erst ein Pfad bis zum Blatt der Rekusion abgelaufen, die Stacktiefe sollte also bei c*n liegen, da ist also einiges an Luft.
Frage, was sagt gdb denn dazu, wo genau der Zugriffsfehler stattfindet? Vielleicht hilft Dir das bei der Analyse.
push [n] # parameter: fibonacci number to calculate
call f # call function
add rsp, 8 # remove parameter from stack
Wäre es nicht sauberer zu pop-en? Auch wenn es konkret ein grow-down stack ist.
Ja das vorgegeben mit dem add rsp, 8. Mit gdb krieg ich das gerade nicht hin, aber trotzdem danke :)
Nach den internen Calls auf f fehlt der ADD RSP,8
Man kommt bei Assembler einfacher mit Pascal- oder stdcall Aufrufkonvention klar: Die Return-Anweisung enthält die Anzahl der Bytes, die vom Stack freigegeben werden.
Außerdem sicherst Du in f nicht den Inhalt von RBX
Naja also ich definiere in eigentlich nicht. Ist das nicht einfach der normale Speicher auf den halt gepusht wird. Deswegen ja auch immer der Stackframe um die Funktion immer wieder aufrufen zu können