1
votes

I am writing a report to summarize stack. If you click on my profile you will see that I have been doing this for a while. Right now, I have some troubles because on GDB it shows me a different thing than on visual studio.

As a result, I am not too sure about my understanding of base pointer and stack pointer, and I am hoping that someone can lead me in the right direction if I am wrong.

For x86 computer, stack is typical growing downward (from higher memory address to lower).

So when a program begins, we called the main function.

  1. In general, at the entry of each function call, a stack is created at the current esp location, and this is what we called "the top of the stack". Is this correct?

  2. When the old ebp gets pushed onto the stack, is it pushed onto where the esp was first pointed to?

  3. Afterward, the esp will move down to point to an empty memory location, is that correct?

  4. Finally, esp is always changing, moving down pointing at the next available memory space. Is that correct?

  5. Does esp move per byte, or per 4 bytes down?

I know there's a lot of questions. But thanks for your time!


Thank you for the response, sir!

@iSciurus

I am confused how everyone define esp pointing at the most recent entry that was pushed onto the stack.

For x86, since the stack grows downward, from your explanation, the esp will first point at the lowest address of the stack. When I look at the the assembly code, we have

   0x080483f4 <+0>: push   %ebp
   0x080483f5 <+1>: mov    %esp,%ebp
   0x080483f7 <+3>: sub    $0x10,%esp

So esp is decremented 16 bytes. So this is the size of the stack of this function call. Local variables come right after return address (ebp-4, ebp-8, etc). So what is the overall purpose of esp here? From what I understand, stack overflow occurs when we try to access an address smaller than that.

The last thing is: when we say the top of the stack, are we referring to the lowest address (for x86).

This is the picture I have in mind (growing downward)

[Parameter n          ]
...
[Parameter 2          ]
[Parameter 1          ]
[Return Address       ]   0x002CF744
[Previous EBP         ]   0x002CF740  (current ebp)
[Local Variables      ]   
-- ESP

Sorry for these long questions. But I really appreciate your help.

1

1 Answers

1
votes
  1. To be correct, a stack frame is created at the current esp location, not the stack itself. The stack is created once at a thread startup, and each thread has its own stack, which is just an area in the process memory space. The stack frame is what actually created at each function entry, and it is a region inside the thread stack.

  2. No, it is pushed onto the address [old_esp - 4] (or [old_rsp - 8] in x64), because esp is the top of the stack and points to the lowest used address. The next DWORD (or QWORD) in the stack is free and ebp is pushed there.

  3. Yes, this is typically done with sub esp, value

  4. No. First, esp is moving down pointing at the lowest used address in the stack, not at the next available space. Second, keep in mind that esp may point to anywhere, not only to the stack: it is ok until you use stack-related instuctions like push/pop.

  5. Esp moves per machine word size: in x86 it moves per 4 bytes, while in x64 it moves per 8 bytes.


The overall purpose of esp is almost always the same: to store the top of the stack. All stack-related instructions like pop/push use esp as their argument. In x86 both ebp and esp are used to store information about the stack frame (the bottom and the top correspondingly). Maybe, you got confused about this redundancy. However, in x64 only rsp is used for stack-based parameters, rbp is a general purpose register.

What about buffer overflows in stack, they often occur when the code tries to write higher than the last element of an array (or struct or whatever). Stack grows downwards, but arrays grow upwards. When we write higher, we can access the return address, the SEH handler as well as internal variables of our caller.

Yes, when we say top, we mean the lowest address. So, most debuggers show the stack in reverse order:

-- ESP
[Local Variables      ]
[Previous EBP         ]   0x002CF740  (current ebp)
[Return Address       ]   0x002CF744
[Parameter 1          ]
[Parameter 2          ]
...
[Parameter n          ]

Here, ESP points to the value "above" all the data and looks more like the "top". Though it is still the lowest used address.