2

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?

Alexis Wilke
  • 19,179
  • 10
  • 84
  • 156
Lolo Giordano
  • 21
  • 1
  • 1
  • 2
  • 1
    What is an `entier`? Calling convention in this case is that a return value is returned in `eax`. You should do a Google search on writing an assembly program for whatever platform it is you're running on so you can get the basics. And you have two `ret` statements in sequence separated only by `pop ebp` which I assume is a typographical error? – lurker Apr 20 '19 at 13:26
  • It is an Integer. (Sorry forget to edit it properly) – Lolo Giordano Apr 20 '19 at 13:27
  • If you're in C, then it should be `int`. You haven't said what high level language you are using or what platform you are on. It can make a difference. It looks like C, but I'd rather not assume. Especially since you're using a C keyword (`double`) as a function name which is generally not a good idea. – lurker Apr 20 '19 at 13:28
  • It is not really in C. The main focus of this course was to create a kind of fictive langage with less restrictions than C then to convert this code into a 3 adresses code then into a X86 langage code. But does it make a difference with how the stack works ? – Lolo Giordano Apr 20 '19 at 13:34
  • The language will, to some extent, determine how the stack frame is set up, and your x86 code makes assumptions about the stack frame. Stack frames can be similar or the same between some languages. Google is your friend here. Look up "x86 C stack frame". – lurker Apr 20 '19 at 13:39
  • 1
    The return value goes into `eax`. Whatever is in `eax` when the function returns is the return value. – fuz Apr 20 '19 at 13:41
  • @fuz I mentioned that in my first comment. :) – lurker Apr 20 '19 at 13:49
  • I was also surprised by the 2 rets in the code. But i'm a little bit lost, it may mean the course have errors in it ?(it sucks). In my case i could just put my ebx into eax then pop and ret to get the return value? – Lolo Giordano Apr 20 '19 at 14:01
  • The value you want is in `ebx` so you just move it to `eax`. Your desired return value isn't on the stack, so you wouldn't `pop` it. – lurker Apr 20 '19 at 15:14
  • Excuse me i know i having trouble to understand it but i don't get why i need this : mov [ebp + 12], ebx This instruction is not moving ebx to eax but just in a place where it's suppose to have argument. – Lolo Giordano Apr 20 '19 at 15:20
  • 1
    @LoloGiordano I can't tell you what the random piece of code you found on the internet does. It seems nonsensical and wrong. I advice you to learn (at least initially) from a tutorial or book, not random snippets of code found on the internet. – fuz Apr 20 '19 at 15:31

1 Answers1

7

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.

fuz
  • 88,405
  • 25
  • 200
  • 352
  • 5
    I like the terminology: call-clobbered vs. call-preserved; easier to think about, and you can think about both terms from the caller's perspective (what will a function do to my registers) or from the callee's perspective (how can I use registers). Also avoids implying that anyone needs to do any saving of anything. Just don't touch them, or let no-longer-needed values be clobbered. – Peter Cordes Apr 20 '19 at 15:57
  • "If we wanted to use ebx, we had to save and restore its old value. If we use a callee-saved register, we can avoid this effort." I think you meant to say `caller-saved register` here. – John H Apr 21 '19 at 09:17