user16806508
user16806508

Reputation: 41

Assembly Segmentation Fault Ret

I'm writing a program that calls a subroutine factorial, which then calculates the factorial of 5. After it's returned the answer will be printed. However after I try to run my program I get a segmentation error. The debugger shows me that the error comes from line 41, a line that merely contains the Ret statement to return from the subroutine. What causes this segmentation fault?

Here's the code:

 1 
 2 .data
 3 .text
 4 
 5 .global main
 6 main:
 7 pushq %rbp        #prologue
 8 mov %rsp, %rbp
 9 
10 push $5               #push value on stack for it to be operated
11 call factorial
12 mov -4(%rbp), %rdi    #move answer into %rdi
13 mov $0, %rax          #no vectors
14 call printf
15 
16 mov %rbp, %rsp    #epilogue
17 popq %rbp
18 
19 mov $0, %rdi      #exit of the program
20 call exit
21 
22 factorial:
23 pushq %rbp      #epilogue
24 mov %rsp, %rbp
25 
26 mov 8(%rbp), %rdi     #move value from line 10 into %rdi
27 mov $1, %rax          # move 1 into %rax, here the result will come
28 
29 factorial_loop_start:
30 cmpb $1, (%rdi)       #compare %rdi to 1 to see if loop ends
31 je factorial_end
32 imul %rdi, %rax       #multiply %rdi with %rax and put the answer in %rax
33 dec %rdi              #decrement %rdi and return to start of loop
34 jmp factorial_loop_start
35 
36 factorial_end:
37 mov %rax, 8(%rbp)     #move the result in %rax to the original stack position of
38                       #the value that needs to be processed
39 mov %rbp, %rsp      #epilogue
40 popq %rbp
41 ret

Upvotes: 0

Views: 734

Answers (1)

Peter Cordes
Peter Cordes

Reputation: 363980

If ret crashes, then either RSP was bad, or the return address it pops into RIP is bad. Look at what RSP points at right before the ret executes, vs. what was there on function entry. Your function overwrites its return address.

Since you're using a debugger already, look at where RDI is actually pointing.

It's weird that you're passing an arg on the stack in 64-bit code, and it looks like you copied the 8-byte offset from the frame pointer from 32-bit code.

In 64-bit code, every push (including the return address pushed by call) is 8 bytes. So you actually loaded your return address from 8(%rbp), I think. (And then overwrite it with mov %rax, 8(%rbp).)

That's why cmpb $1, (%rdi) doesn't fault when you deref %rdi by putting () around it, to get the memory pointed-to by RDI. Which obviously doesn't match how you're using the bare register as the source operand for imul.

Your code would have faulted there if RDI held the integer you wanted it to.


mov -4(%rbp), %rdi is also wrong, loading the top half of the push and overlapping into main's saved-RBP. If you fix the offsets in factorial you could have that function update its stack arg, but that's a weird convoluted custom calling convention. Just have it return a value in %rax like a normal function.

I'm not doing your homework for you by giving you correct code; I'm intentionally just telling you where to look with a debugger for the things that are a problem.

Upvotes: 2

Related Questions