Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does x86 architecture use two stack registers (esp ; ebp)?

My question is simple, Why do x86 architecture use two stack registers (esp ; ebp)?

The length of stack frame has been determined during compile time, then I think we can just use one register (for example esp) to access the stack, and maintain the base address which used to be in the ebp register on the stack (or in other memory regions, but that would incur more performance penalty)

Is it possible?

like image 947
lllllllllllll Avatar asked Dec 05 '14 04:12

lllllllllllll


2 Answers

This all depends on the calling convention of course but it generally goes like this.

The stack pointer is able to be moved arbitrarily based on whatever you need to push onto, or pop off from, the stack at any given point in time. This can happen anytime within a function, as you need to temporarily save some data on the stack.

The base pointer is generally set to the same value for any given stack depth, and is used to access passed parameters (on one side) and local variables (on the other side). It's also used to quickly move the stack pointer upon exiting a function.

The reason it's done this way is to simplify the code so that you're not having to reference stack content based on a possibly-changing stack pointer. Using the base pointer considerably eases the task of code generation (you don't have to know what the stack pointer is at any given time, just use the base pointer, which remains the same for the duration of the function).

Without that, code that wanted to push two copies of a local variable to call the next function, would look like this:

    mov    eax, [esp+16]    ; get var1
    push   eax              ; push it
    mov    eax, [esp+20]    ; get var1 again
    push   eax
    call   _somethingElse

Put aside the fact that you wouldn't reload eax in this case, the point I'm trying to make is that the relative location of items from a moving stack pointer can needlessly complicate matters.

For example, here's a function coded in assembly that follows a common calling convention:

_doSomething:
    push   ebp              ; stack current base pointer
    mov    ebp, esp         ; save previous stack pointer
    sub    esp, 48          ; incl 48 bytes local storage

    ; do whatever you want here, including changing
    ;  esp, as long as it ends where it started.

    mov    esp, ebp         ; restore previous stack pointer
    pop    ebp              ; and previous base pointer

    ret                     ; then return

_callIt:
    mov    eax, 7
    push   eax              ; push parameter for function
    call   _doSomething
    add    esp, 4           ; get rid of pushed value
    :

If you follow the code, you can see that ebp inside the function body is a fixed reference point with [ebp] (contents of ebp) being the return address, [ebp+4] being the pushed value of 7, and [ebp-N] being the local storage for _doSomething, where N varies from 1 to 48.

This is the case regardless of how many items are pushed or popped within the body of the function.

like image 193
paxdiablo Avatar answered Sep 27 '22 16:09

paxdiablo


Why does x86 architecture use two stack registers (esp ; ebp)?

According to @gsg's answer to related question "Do any languages / compilers utilize the x86 ENTER instruction with a nonzero nesting level?" the x86 architecture was designed 30 years ago as "Pascal machine".

See The 8086/8088 Primer, by Stephen P. Morse, Chapter 8: High-Level-Language Programming (Pascal) for rationale from one of the chip designers.

As such it incorporates hardware support for nested and recursive subroutines (procedures, functions) as in the Pascal programming language this was an important aspect of the structured programming paradigm supposed to produce code that was easier to read/write/maintain.

The hardware support in the form of special CPU instructions enabled to generate code using less instructions. Less instructions usually also meant faster code.

Is it possible to implement the stack frame variable access on another Turing complete machine without the use of ebp register?

Yes, but with different execution time

like image 30
xmojmr Avatar answered Sep 27 '22 18:09

xmojmr