## 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

- basepointer.c
- basepointer.asm (cl /Fa basepointer.c)

### 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

- ### basepointer.c

      void f(int i)
      {
          int j, k ;
          j=i ;
          return ;
      }

      void g(int i)
      {
          int j, k ;
          f(j) ;
          k=i ;
          return ;
      }

      int main(int argc, char * argv[]) {
         g(2) ;
      }

- ### basepointer.asm

      	TITLE	basepointer.c
      	.386P
      include listing.inc
      if @Version gt 510
      .model FLAT
      else
      _TEXT	SEGMENT PARA USE32 PUBLIC 'CODE'
      _TEXT	ENDS
      _DATA	SEGMENT DWORD USE32 PUBLIC 'DATA'
      _DATA	ENDS
      CONST	SEGMENT DWORD USE32 PUBLIC 'CONST'
      CONST	ENDS
      _BSS	SEGMENT DWORD USE32 PUBLIC 'BSS'
      _BSS	ENDS
      _TLS	SEGMENT DWORD USE32 PUBLIC 'TLS'
      _TLS	ENDS
      FLAT	GROUP _DATA, CONST, _BSS
      	ASSUME	CS: FLAT, DS: FLAT, SS: FLAT
      endif
      PUBLIC	_f
      _TEXT	SEGMENT
      _i$ = 8
      _j$ = -4
      _f	PROC NEAR
      ; File basepointer.c
      ; Line 4
      	push	ebp
      	mov	ebp, esp
      	sub	esp, 8
      ; Line 6
      	mov	eax, DWORD PTR _i$[ebp]
      	mov	DWORD PTR _j$[ebp], eax
      ; Line 8
      	mov	esp, ebp
      	pop	ebp
      	ret	0
      _f	ENDP
      _TEXT	ENDS
      PUBLIC	_g
      _TEXT	SEGMENT
      _i$ = 8
      _j$ = -4
      _k$ = -8
      _g	PROC NEAR
      ; Line 11
      	push	ebp
      	mov	ebp, esp
      	sub	esp, 8
      ; Line 13
      	mov	eax, DWORD PTR _j$[ebp]
      	push	eax
      	call	_f
      	add	esp, 4
      ; Line 14
      	mov	ecx, DWORD PTR _i$[ebp]
      	mov	DWORD PTR _k$[ebp], ecx
      ; Line 16
      	mov	esp, ebp
      	pop	ebp
      	ret	0
      _g	ENDP
      _TEXT	ENDS
      PUBLIC	_main
      _TEXT	SEGMENT
      _main	PROC NEAR
      ; Line 19
      	push	ebp
      	mov	ebp, esp
      ; Line 20
      	push	2
      	call	_g
      	add	esp, 4
      ; Line 21
      	pop	ebp
      	ret	0
      _main	ENDP
      _TEXT	ENDS
      END