2

我对 Assembly 完全陌生,并希望确认在以下陈述中我有误解并需要纠正的地方。

堆栈指针(ESP ) 指的是堆栈的顶部(最低内存地址)。

基指针(EBP)用于在构建堆栈帧时临时存储各种内存地址。它通常保存当前堆栈帧的最高内存地址。

指令指针EIP)指的是内存中文本(code)段中一行代码的内存地址

一旦某些东西被推入堆栈,就不能就地更改它。IE。如果我们PUSH EBP到堆栈,我们正在推送当前值EBP,而不是某种引用或指向它的指针。然后我们不能就地更改该值。

传递给函数的参数通常被移动到地址空间中,该地址空间是堆栈指针的偏移量。IE。[ESP-12].

调用函数时(使用CALL),会发生以下情况:

  1. 返回地址被添加到堆栈中(紧跟当前地址的内存,EIP因此我们知道在被调用函数完成后返回到哪里
  2. 保存的帧指针被添加到堆栈中,通常是调用函数的堆栈帧的堆栈指针
  3. 然后我们将进入被调用函数的序言

谢谢。我正在努力解决这些问题。

4

2 回答 2

9

传递给函数的参数通常被移动到地址空间中,该地址空间是堆栈指针的偏移量。IE。[ESP-12]。

通常在调用之前将参数压入堆栈。

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

这导致以下堆栈内容:

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

由于返回地址(由 推入call)是 4 个字节,所以函数的参数是 at[ESP+4][ESP+8]

如果您的函数添加了堆栈帧,通常您会这样做

myFunct:  push EBP
          mov EBP, ESP

现在堆栈看起来像这样:

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

并且参数位于[EBP+8]and [EBP+12],即使您推送更多值(或为局部变量添加一些位置),因为EBP不再更改:

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

局部变量为 at [EBP-4],[EBP-8][EBP-12]
返回地址为 at[EBP+4]

注意:如您所见,这是可能的

  • 访问ESP(然后你不需要帧指针,但是你需要跟踪你推送了多少数据,以“找到”参数和变量)
  • 或通过EBP(这增加了一些开销)。在许多函数中,根本不需要帧指针,并被编译器优化掉。
于 2017-11-15T13:13:37.857 回答
4

一旦某些东西被推入堆栈,就不能就地更改它。IE。如果我们将 EBP 推入堆栈,我们将推入 EBP 的当前值,而不是某种引用或指向它的指针。然后我们不能就地更改该值。

你当然可以。堆栈是普通的计算机内存,没有什么特别之处,除了 99% 的代码需要有效的(读+写访问)内存地址esp和一些保留空间,因此它可以根据需要向它推送一些本地的东西。

push  ebp    ; store current value in ebp to stack

几乎等同于:

sub   esp,4
mov   [esp],ebp

(但第二个变体也会修改标志,它的原子性稍差)

现在您可以用其他任何内容覆盖它,例如:

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

引用或指向它的指针

好吧,没有办法对寄存器有某种引用或指针ebp,它是 CPU 中的一个寄存器,只有 32 位(32x 0 或 1 值)并且它没有地址,您只能ebp在指令中使用它的名称,这允许在其编码中使用它。

push ebp将这些 32 位(并且没有其他信息)复制到内存中(然后将这些 0/1 值保存在其自己的 32 位 = 4 个字节中)。没有信息从哪里写入内存中的值,何时以及通过什么指令,只存储值位。

于 2017-11-15T15:05:10.127 回答