Base Pointer


Overview

The details of procedure calling in i386 creates linked list of stack frames threaded through the Base Pointer register ebp. In fact, three invairants hold simultaneously thanks to this linked list:
  1. ebp currently points to the last closed stack frame, with the previous edp appended: hence a linked list of stack frames;
  2. ebp points to the base of the currently open stack frame, where the first element of the open stack frame is the base of the most recently closed stack frame;
  3. thinking of ebp as a pointer to the base of the last close stack frame, since the bottom item of a closed stack frame is the previous eip, the previous eip is recoverable.

Manifest

The Details

As a linked list of stack frames, the structure could be thought of as:

   struct _STACK_FRAME {
        void * prev ; // a double word for i386
        char frame[] ; // indeterminate length
   } ;
We could be a bit more specific and write into the specification that the base of the frame contains the return address,
  struct _CLOSED_STACK_FRAME {
        void * prev ;
        void * return_address ;
        char frame [] ;
  }
An open stack frame, that is the current, has esp pointing to its current base, and ebp is the root of the list of closed stack frames.

This does not capture it all, however, because ebp has a currently living value as the base of the current stack, or perhaps better, the current heap of local variables. I find this dual role confusing unless it is cleanly described.

Hence a more accurate description is a linked list of stack-heap pairs, with the thread running physically through the center:

  struct _STACK_HEAP_PAIR {
        char current_heap ;
        void * prev_stack_heap_pair ;
        char prev_frame [] ;
 } ;
where we note that the first item of previous_is the return address and implicitly the value of the previous stack pointer.

As a picture:


       +-------+    +-------+
       |  esp  |    |  ebp  +
       +-------     +-------+
           |           |
           +---+       |
               |       |
               v       |
    +-------------     |
    | stack frame D .. |
    +-------------     |
                       |
                       v
    +---------------+------+-------------+  
    | stack frame C | prev |    heap D   |
    +---------------+------+-------------+
                       |
                       v
    +---------------+------+-------------+  
    | stack frame B | prev |    heap C   |
    +---------------+------+-------------+
                       |
                       v
    +---------------+------+-------------+  
    | stack frame A | prev |    heap B   |
    +---------------+------+-------------+
    
    
The labels applied to the heaps and stack frames associate them logically, whereas the drawing suggests their physical association. For instance, stack from D and heap D are currently active. The most recently closed frame and head are those labeled C.

It is also true that these pairs are arranged descending and contiguous in memory. For instance, the left edge of stack frame B goes right up against the right edge of heap B. This fact is surprisingly useless. Neither the depth of the frame nor the height of the heap are ever recorded - they are "compiled in" as either the size of the parameter list of the call or the size of the local variable block.

In other words, the boundary between a stack and its heap is not visible in register settings or standard global variables, nor need it be.

Author

Burton Rosenberg
11 September 1998


Supporting Documents