Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Returning a value in x86 assembly language

Tags:

x86

assembly

I'm currently learning x86 Assembly language (I'm at the start of the course) and I'm having some problems understanding how the stack works in one particular case.

Let's say I have this code:

double(entier n) { return n + n; }

I've tried to convert it into x86 code and I ended up with this :

push ebp #save old pointer stack
mov ebp, esp #put new pointer stack
mov ebx, dword[ebp + 8] #get argument n and put it in ebx
add ebx, dword[ebp + 8] #add n to ebx 

But then I was totally blocked and couldn't find how to return the value of ebx. I found a solution on internet that was the following:

mov [ebp + 12], ebx
pop ebp
ret
pop ebp
ret

I don't understand how it works. Isn't ebp+12 the value of the 2nd argument? (In my case there's none). The pop is used to move the esp pointer but why do we need 2 pop and 2 return in that case ? Is it only to remove the value that have been used during the function declaration?

like image 373
Lolo Giordano Avatar asked Oct 16 '25 16:10

Lolo Giordano


1 Answers

Since it appears that you are throughly confused by this, let me just show you how to do it:

double: push ebp           ; establish...
        mov ebp, esp       ; ...stack frame

        mov eax, [ebp + 8] ; load argument from stack into eax
        add eax, eax       ; add it to itself

        leave              ; tear down the stack frame
        ret                ; return to the caller

Note that I chose eax instead of ebx for the register. This is for two reasons:

  • eax is a caller-saved register (meaning that the caller must take care to preserve its value if desired) whereas ebx is a callee-saved register (meaning that the callee, i.e. double must preserve its value). If we wanted to use ebx, we had to save and restore its old value. If we use a caller-saved register like eax instead, we can avoid this effort.
  • eax is the register where by convention the return value is found. The caller will take the value of eax as the return value.

    In almost all calling conventions for x86, the return value is whatever value is found in eax on return. Thus, by placing the result of the addition in eax, we don't have to do any extra work to set up the return value.


For future questions along these lines, I advise you to consult the output of a C compiler with optimisations turned on. The C compiler is very good at generating assembly and rarely makes any mistakes. On UNIX-like systems such as Linux, you can use the -S option to generate assembly code. For gcc I advice you to type

gcc -m32 -O3 -masm=intel -fno-omit-frame-pointer -S -o- source.c

to have assembly code for source.c printed to the terminal in Intel syntax, which is the style of assembly you seem to be using.

See also How to remove "noise" from GCC/clang assembly output? for more details.

like image 114
fuz Avatar answered Oct 19 '25 11:10

fuz



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!