linux - 为什么 "lea..and..push"汇编代码经常出现在函数的开头?

标签 linux x86 reverse-engineering 32-bit

我发现当我通过GDB查看一些文件时,很多时候,函数的开头有这三行代码

   0x08048548 <+0>:     lea    ecx,[esp+0x4]
   0x0804854c <+4>:     and    esp,0xfffffff0
   0x0804854f <+7>:     push   DWORD PTR [ecx-0x4]

我通常会忽略它们,因为在创建这三行堆栈帧之后,这就是函数通常启动的方式。

谢谢。

最佳答案

这会将堆栈指针对齐到 16 字节边界,因为有时(对于 SSE)CPU 需要数据的 16 字节对齐。

一个好的编译器会检查调用图(找出什么调用什么),并决定:

  • 函数本身不需要堆栈对齐,也不会调用其他需要堆栈对齐的函数;因此不需要堆栈对齐

  • 所有函数的调用者都使用对齐的堆栈,因此:

    • 该函数只需要进行固定调整即可重新建立预先存在的对齐方式,例如 sub esp, 8 (可以与任何为局部变量保留堆栈空间的代码合并)
    • 真正需要16字节对齐的数据可以给予16字节对齐,而不需要对齐堆栈本身
  • 以上都不能被证明是正确的,因此函数必须假设“最坏情况”并自行强制对齐(例如,您在函数开头看到的指令)

当然,对于一个好的编译器来说,最后一种情况(需要您显示的代码)是极其罕见的。

但是;大多数编译器都不好,因为它们无法看到整个程序(如果程序被分成多个单独编译的目标文件,那么编译器一次只能看到程序的一小部分)。他们无法弄清楚大部分/任何调用图,因此最后一种情况(需要您显示的代码)变得非常常见。要解决这个问题,您需要“链接时间代码生成”,但通常人们不会打扰。

注意:对于 AVX2,您需要 32 字节对齐,对于 AVX512,您需要 64 字节对齐,对于某些事情(为了避免在多线程代码中出现错误共享),您可能需要“缓存行大小对齐”(通常还需要64 字节对齐)。这使得“检查调用图以确定实际需要什么对齐”算法比我所描述的稍微复杂一些。

关于linux - 为什么 "lea..and..push"汇编代码经常出现在函数的开头?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59331096/

相关文章:

c - 使用内联汇编从 C 函数获取返回的字符

gcc - 取消链接 ELF 可执行文件

c - 如何在 C 中创建虚拟以太网设备?

regex - 查找文本文件中至少有两个相同单词的所有行 (Bash)

c - GDB反汇编一个简单的程序

multithreading - 缓存一致性协议(protocol)如何强制执行原子性?

gdb - 在剥离的 ELF 可执行文件中设置断点

java - 如何从 Java 代码生成 UML 图(尤其是序列图)?

c++ - 哪里可以获得boost线程源代码

linux - 在 *nix 中清空目录的最安全方法是什么?