1

Right now I am trying to learn the assembly language on x86 systems. Therefore I am readying the book "Programming from the Ground Up". (Available for free at http://download.savannah.gnu.org/releases/pgubook/)

On page 53, the way the computer's stack works is explained:

The computer’s stack lives at the very top addresses of memory. You can push values onto the top of the stack through an instruction called pushl. [...] Well, we say it’s the top, but the "top" of the stack is actually the bottom of the stack’s memory. [...] In memory the stack starts at the top of memory and grows downward due to architectural considerations. Therefore, when we refer to the "top of the stack" remember it’s at the bottom of the stack’s memory.

That part I get. Let's say the stack's memory starts at address 0 and ends at address 11 (inclusively). That means there are currently three words (4 bytes a piece) on the stack. According to my understanding, the word that is on "top" of the stack currently occupies the addresses 8, 9, 10, and 11. (Since one word has 4 bytes and therefore occupies four storage locations in main memory). However, the book now says the following:

The stack register, %esp, always contains a pointer to the current top of the stack.

Okay, in my example the %esp register would hold the address 8. It points to the word that is currently on top of the stack. But...

Every time we push something onto the stack with pushl, %esp gets subtracted by 4 so that it points to the new top of the stack (remember, each word is four bytes long, and the stack grows downward).

What? Isn't it exactly the other way around? If I push another 4-byte-sized machine word onto the stack, this word will occupy the main memory addresses 12 to 15. Like they said: The stack grows downward. Now the %esp register points to the word that is currently on top of the stack. It starts at address 12. Before we pushed another word onto the stack, the address that was stored in %esp was 8. So %esp has clearly been added 4, not subtracted. Where do they get the subtraction from? What did I miss? I am very confused...

Help is very appreciated ;)

4

3 回答 3

3

如果我将另一个 4 字节大小的机器字压入堆栈,这个字将占用主内存地址 12 到 15。就像他们说的:堆栈向下增长。

向下意味着朝向较低地址,因此将另一个值压入堆栈意味着减去 4 并将该值写入新位置。所以 %esp 变成 4。

  +--------+
8 |12345678| <- top of stack before push
  +--------+
4 |11223344| <- top of stack
  +--------+
0 |00000000|
  +--------+
于 2013-10-26T13:21:42.923 回答
0

通常,“内存地址的顶部”是指最高地址。例如,您的堆栈可能从0x00105000; 如果您添加一个单词,您将移动esp0x00104ffc(即您在内存地址中向下增长)。参见例如这里的一个漂亮的图表。

于 2013-10-26T13:21:46.817 回答
0

如果您输入您的函数并且堆栈位于 0x100。通常堆栈从高地址向低地址向下增长,朝向零。因此,如果您随后推送一个 4 字节项目,则堆栈“顶部”现在位于 0xFC。推另一个,0xF8,依此类推。

阻碍这里的另一件事是ebp vs esp。

通常,处理器有一个堆栈指针,一个特殊的或有时是通用的寄存器,它具有与之相关的特定指令,push,pop,也许还有堆栈指针相关的寻址加载和存储。此外,编译器使用不同于堆栈指针的通用(或特殊用途)寄存器用于堆栈帧的情况并不少见。为什么?使阅读和调试编译器生成的代码更容易一些。

例如,在函数进入时,您可以将堆栈指针复制到另一个寄存器中,并且在该寄存器不移动的函数期间,这允许您对该寄存器进行相对寻址以查找局部变量以及函数参数(如果此编译器和处理器倾向于在堆栈上传递参数)。因此,在整个函数中,您可以访问具有相同偏移量的任何一个局部变量。如果您没有使用堆栈帧,那么如果出于优化原因(保护堆栈内存)堆栈指针在函数内是动态的,那么从堆栈指针到局部变量和基于堆栈的函数参数的相对偏移量也将是动态的更难阅读和调试。(同时节省堆栈空间并返回一个寄存器以供其他用途,这两种优化)。

在任何情况下,调用函数的函数都需要在调用嵌套函数时将堆栈指针放在“堆栈顶部”,以便该函数可以像任何其他函数一样享受堆栈,并假定它可以使用/销毁超出堆栈结束用于自己的目的。

于 2013-10-26T13:22:02.123 回答