1

I'm learning assembler language. When I started reading about stack, I tried to write code that adds two numbers and debug it. But when try to call function "add" I get

Program received signal SIGSEGV, Segmentation fault.

The main problem is that the program gives this error without even entering the ADD function. That is, the program, even without executing the call add command, terminates urgently.

here is the code:

global _start

section .text
_start:

call main

add:
        push ebp
        mov ebp, esp
        sub esp, 4

        mov eax, [ebp + 8]
        mov ebx, [ebp + 12]
        add eax, ebx
        mov [ebp - 4], eax
        ret

main:
        push ebp
        mov ebp, esp
        sub esp, 12

        mov [ebp - 8], dword 2
        mov [ebp - 12], dword 40

        call add

        ret

here is the debugging process

James Z
  • 12,209
  • 10
  • 24
  • 44
calmcake
  • 11
  • 2
  • 4
    Your code pushes `ebp` onto the stack but then does not pop it off before trying to return. You need to match your pushes and pops such that you push and pop the same amount of data. To fix this, add the standard epilogue `mov esp, ebp; pop ebp` to your functions. – fuz Jan 03 '22 at 14:36
  • For more on what @fuz says, check this https://stackoverflow.com/questions/3638075/explanation-about-push-ebp-and-pop-ebp-instruction-in-assembly – Ali Ben Zarrouk Jan 03 '22 at 14:37
  • 3
    `ret` can be though of as `pop eip`, so, if when `ret` executes, `esp` is not where it was on entry, you'll have trouble. – 500 - Internal Server Error Jan 03 '22 at 16:20
  • @fuz. The main problem is that the program gives this error without even entering the ADD function. That is, the program, even without executing the call add command, terminates urgently. – calmcake Jan 03 '22 at 20:38
  • @calmcake How do you know it doesn't enter the ADD function? – fuz Jan 03 '22 at 20:40
  • `ni` steps *over* calls, not into them. Use `si` if you want to step into the `call add`, and see its `ret` pop a bad value into EIP – Peter Cordes Jan 03 '22 at 21:01
  • @fuz. Well, when I use gdb, and eip reaches this command, there is no jump, but the error pop up. I'm just comparing it with the previous call command, which translated the eip to the main label – calmcake Jan 03 '22 at 21:06
  • 1
    @calmcake The `ni` command skips over function calls. Use `stepi` to single step instructions. – fuz Jan 03 '22 at 21:08
  • @fuz. Sorry, because of my meager knowledge, I didn't know to use si instead of ni – calmcake Jan 03 '22 at 21:10
  • 1
    Thanks to everyone who took the time to solve my problem!!! I'll figure it out – calmcake Jan 03 '22 at 21:11
  • Write back as soon as you have new results! – Sebastian Jan 03 '22 at 21:33

1 Answers1

2

Firstly, you never exit the program, which would cause the program to go to an infinite loop, if it could return from the functions successfully. You see, ret is equivalent to pop eip, which jumps into the address at the top of the stack and continues execution there. Your program however, pushed many additional values onto the stack without ever restoring it, thus, when the ret instruction was executed, the wrong address was popped. You really need to be careful when it comes to the stack in procedures.

I fixed the errors and mentioned, and maybe you could pick up some ideas from my version. But in any case, just remember to restore the stack and exit from the program.


global _start

section .text
_start:

call main
mov ebx, eax   ;; using the sum as the return code
mov eax, 1     ;; SYS_exit
int 0x80       ;; syscall

add:
        push ebp          ;; stack frame
        mov ebp, esp 

        mov eax, [esp+8]  ;; first argument (getting esp+8 because we pushed ebp)
        mov ebx, [esp+12] ;; second argument
    
        add eax, ebx 

        mov esp, ebp 
        pop ebp           ;; stack frame restore
        ret               ;; returning from function

main:
        push ebp 
        mov ebp, esp      ;; set up frame pointer.

        push dword 2      ;; pushing the arguments
        push dword 40

        call add 
    
        mov esp, ebp      ;; tear down stack frame
        pop ebp           ;; restore frame pointer
        ret             ;; returning to _start where we exit
nasm -g -f elf32 -o program.o program.asm ; ld -g -m elf_i386 -o program program.o ; ./program ; echo $?
Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
UnityH
  • 46
  • 1
  • 2
    Having a frame pointer to a fixed place in your stack frame is optional; `main` could just as easily `add esp, 8` to undo its 2 pushes before `ret`. Also, if you're going to waste instructions setting up EBP as a frame pointer, at least use it to access your stack args in the `add` function. Also, EBX is call-preserved so `mov ebx, [ebp+8]` without saving/restoring EBX is using a custom calling convention, clobbering that register. Also, it's common to just write `push 2`; the push-immediate default operand-size is the current mode in all mainstream assemblers. – Peter Cordes Jan 12 '22 at 21:11