Assembly Call Stack - Terminology Questions

2019-02-27 01:29发布

问题:

I'm completely new to Assembly and looking to confirm where, in the following statements, I have a misunderstanding and need to be corrected.

The stack pointer (ESP) refers to the top (lowest memory address) of the stack.

The base Pointer (EBP) is used for temporarily storing various memory addresses when building a stack frame. It normally holds the highest memory address of the current stack frame.

The instruction pointer (EIP) refers to the memory address of a line of code in the text (code) segment of memory

Once something has been pushed to the stack, it can't be changed in-place. ie. If we PUSH EBP to the stack, we are pushing the current value of EBP, not some kind of reference or pointer to it. We can't then change that value in-place.

Arguments being passed into a function are generally moved into an address space which is an offset of the stack pointer. ie. [ESP-12].

When a function is called (using CALL), the following occurs:

  1. The return address is added to the stack (the memory of the address immediately following the current EIP so we know where to return to after the called function has completed
  2. The saved frame pointer is added to the stack, which is normally the stack pointer of the stack frame of the calling function
  3. We would then move into the prologue of the called function

Thanks. I'm trying to get my head around this stuff.

回答1:

Arguments being passed into a function are generally moved into an address space which is an offset of the stack pointer. ie. [ESP-12].

Often arguments are pushed on the stack, before the call.

push paramA  ; ( some 32bit value, register, whatever )
push paramB
call myFunct

This leads to the following stack content:

---------------
|    paramA   |
---------------
|    paramB   |
---------------
| return addr |   <-- ESP
--------------- 

Since the return address (pushed by the call ) is 4 bytes, the parameters for the function are at [ESP+4] and [ESP+8].

If your function adds a stack frame, usually you do

myFunct:  push EBP
          mov EBP, ESP

Now the stack looks like this:

---------------
|    paramA   |
---------------
|    paramB   |
---------------
| return addr |   
---------------     
|   saved EBP |   <-- EBP, ESP
--------------- 

and the parameters are at [EBP+8] and [EBP+12], even if you push more values (or add some place for local variables) since EBP is not changing anymore:

myFunct:  push EBP
          mov EBP, ESP
          sub ESP, 12      ; make room for 3 32bit local variables

          mov eax, [EBP+8] ; access one of the parameters
          mov [EBP-4], eax ; save it in local variable #1

rel |  rel |
to  |  to  |
ESP |  EBP |
----|------|--------------
+24 | +12  |    paramA   |
    |      |--------------
+20 | +8   |    paramB   |
    |      |--------------
+16 | +4   | return addr |  
    |      |--------------
+12 |      |   saved EBP |  <-- EBP   (is fixed here for now)
    |      |--------------- 
+8  | -4   |    local#1  |
    |      |--------------- 
+4  | -8   |    local#2  |
    |      | --------------- 
0   | -12  |    local#3  |  <--- ESP  (keeps growing, by pushing, calling etc)
           --------------- 

The local variables are at [EBP-4], [EBP-8], [EBP-12] etc.
The return Address is at [EBP+4]

Note: As you can see, it is possible

  • to access by ESP (then you do not need a frame-pointer, but then you need to keep track of how much data you pushed, to "find" the parameters and variables )
  • or by EBP (which ofc adds some overhead). In many functions, the frame pointer is not needed at all, and optimized away by compilers.


回答2:

Once something has been pushed to the stack, it can't be changed in-place. ie. If we PUSH EBP to the stack, we are pushing the current value of EBP, not some kind of reference or pointer to it. We can't then change that value in-place.

Sure you can. Stack is ordinary computer memory, there's nothing special about it, except that 99% of code expects valid (read+write access) memory address in esp and some reserved space, so it can push some local things to it, as needed.

push  ebp    ; store current value in ebp to stack

Is almost equivalent to:

sub   esp,4
mov   [esp],ebp

(but the second variant will also modify flags and it is somewhat less atomic)

Now you can overwrite it with anything else, like:

mov [esp],eax ; overwrite the old_ebp value with current_eax value

reference or pointer to it

Well, there's no way to have some kind of reference or pointer to the ebp register, it's a register in CPU, only 32 bits (32x 0 or 1 value) and it has no address, you can work with it only by its name ebp in instructions, which allow usage of it in their encoding.

Upon push ebp those 32 bits (and no other info) are copied into memory (which is then holding those 0/1 values copied in its own 32 bits = 4 bytes). There's no information where the value in memory was written from, when and by what instruction, only value bits are stored.