我刚刚开始组装,我发现这些说明和类似的代码遍布:
sub esp , something
mov esp, dword ptr [esp + something]
为什么会这样做?我听说它是关于堆栈帧的初始化。你可以解释一下,或者指出我要找的关键词吗?
答案 0 :(得分:0)
乍一看Intel x86。 'Something'通常是所有本地(堆栈分配)变量的总长度,如果它出现在汇编子例程的开头。由于堆栈向下增长,即朝向较低地址,因此为它们预留了空间。 你确定第二行正是你写的吗? esp已经指向空闲区域,因此在您的例程递归调用自身或调用另一个函数之前,参数可以在本地下方的堆栈上推送。在调整以适应本地变量后,我没有看到加载某些内容的点,除非它被用于允许(下一个)被调用者访问调用者的堆栈帧,就像在Pascal中有嵌套函数时一样。 / p>
答案 1 :(得分:0)
每个C(除了一些inline
函数,而不仅仅是C函数)函数都有它的开始和结束。开头通常看起来像这样:
push ebp
mov ebp, esp
sub esp, x
第一行推送旧堆栈帧。由于在程序运行期间调用了许多函数,因此函数可以在调用另一个函数时将其堆栈帧保存在堆栈中。堆栈帧(通常存储在EBP
寄存器中)是每个局部变量寻址的基础。假设你有这段代码:
int main() {
__volatile int localVariable = 0x10;
printf("%d");
}
变量localVariable
可以静态存储在某个位置(例如,在地址0x410000
上),但如果再次调用main
,则该地址上的值将被覆盖。因此需要像动态分配这样的东西。这就是堆栈框架的用途;保存先前的堆栈帧并相对于堆栈指针的实际位置“分配”局部变量的位置。在这种情况下,localVariable
应始终位于EBP-sizeof(int)
位置。
第二行和第三行实际上是分配“新”内存的那段代码。第三行从ESP
中减去一些值(堆栈增长) - >这就是函数不会覆盖变量的原因;每个功能都有自己的位置。第二行保存旧的堆栈指针,所以当被调用的函数从它的堆栈帧返回时,它会堆栈前一个函数的指针。
功能的标准结尾是
mov esp, ebp
pop ebp
ret
或
离开
RET
第二个示例中的leave
是第一个示例中前两行的替代,因为函数经常返回执行上一个函数。
寻址EBP - something
通常意味着访问被调用(当前)函数的局部变量。
寻址EBP + someting
通常意味着访问传递给函数的参数。
我必须注意,存储在EBP
中的地址实际上并不指向参数,而是返回函数的地址,该函数在调用函数时由call
指令推送。存储在EBP + 4
上的值可以是第一个(旧的做法,例如用于具有可变数量的参数的函数,如printf
),与最后一个变量相同(典型的Java,它处理变量计数参数 - Object... values
- 通过创建新数组并仅传递对它的引用)。