gpt4 book ai didi

assembly - 需要一些关于汇编语言中 LEAVE 指令的解释

转载 作者:行者123 更新时间:2023-12-02 20:04:56 26 4
gpt4 key购买 nike

我没有清楚地理解 LEAVE 函数,它是这两条指令的浓缩:

MOV ESP, EBP
POP EBP

因此,MOV ESP, EBP 将 ESP 向下移动到 EBP 的级别(堆栈的开头)。

然后POP EBP,将ESP指向的值移动并影响到EBP,同时将ESP向下移动一步。

但我真的不明白,这两个操作如何与离开函数的事实联系起来(这是 LEAVE 的目的)。

您能帮我澄清一下吗?

最佳答案

在 32 位和 16 位时代,常见的序言是例程开始时的指令序列

push ebp
mov ebp, esp

sub esp, <local_var_size>

push <clobbered_reg1>
push <clobbered_reg2>
...

这里没有什么是随意的,指令的顺序很重要,我们最终得到

|parN | <-- EBP + 04 + n*4                 par1..parN = Routine parameters
... ... ra = Return address
|par2 | <-- EBP + 0ch o ebp = Original (caller) EBP
|par1 | <-- EBP + 08h lvar1..lavarM = Local variables
|ra | <-- EBP + 04h creg1..cregK = Clobbered registers
|o ebp| <-- EBP
|lvar1| <-- EBP - 04h
|lvar2| <-- EBP - 08h
... ...
|lvarM| <-- EBP - m*4
|creg1|
|creg2|
...
|cregK| <-- ESP

看看如何使用 ebp 中的合适指针轻松访问所有数据。 (参数作为连续的正偏移量大于或等于 8,局部变量作为负偏移量小于或等于 4)以及该模型对于更多数量的局部变量或参数的扩展效果如何。
为此ebp称为帧指针。

尾声必须消除这一切。
一种可能的变体是

pop <clobbered_regK>
...
pop <clobbered_reg1>

add esp, <local_var_size>

pop ebp
ret n*4

然而,这涉及重复<local_var_size> - 很容易忘记保持两个版本同步。
我们可以利用ebp这一事实值为 esp在分配本地变量之前,因此通过恢复该值,我们可以有效地取消分配它们。

pop <clobbered_regK>
...
pop <clobbered_reg1>

mov esp, ebp

pop ebp
ret n*4

但是从最后开始的第三条和第二条指令是leave指令确实如此。所以:

pop <clobbered_regK>
...
pop <clobbered_reg1>

leave
ret n*4

是等效的序言。

<小时/>

enter 是一条非常慢的指令( https://agner.org/optimize ),因此编译器从不使用它,但 leave可用于优化代码空间,对性能影响很小(可以通过节省代码大小来平衡)。 GCC 使用leavepop ebp其本身是不够的,大多数 -mtune=设置。

在当前的 Intel CPU(例如 Skylake)上,leave总共花费 3 个 uop,而 mov esp, ebp 则需要 2 个 uop/pop ebp 。在真实的测试用例中,通过调用(从重复循环)一个实际的小函数来解释堆栈同步微指令中可能存在的差异,该函数将 EBP 设置为帧指针并分配一些堆栈空间,然后将其拆除,测量的硬件性能计数器left 函数每次调用比 mov/pop 函数多占用 1 个前端 uop。但由于某种未知的原因,leave 函数运行得稍微快一些,即使两者都对齐了 32。(@petercordes 运行了这个测试。)

关于assembly - 需要一些关于汇编语言中 LEAVE 指令的解释,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41907672/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com