为什么 "lea..and..push" 汇编代码经常出现在函数的开头?
Why do "lea..and..push" assembly codes frequently come up at the beginning of a function?
我发现我在通过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
(可以与任何为局部变量保留堆栈space的代码合并)
- 实际需要16字节对齐的数据可以给16字节对齐而不用自己对齐栈
上面的 none 可以证明是正确的,所以函数必须假设 "worst case" 并强制自己对齐(例如你在函数开始)
当然,对于一个好的编译器来说,最后一种情况(需要您显示的代码)是极其罕见的。
但是;大多数编译器都不好,因为它们无法看到整个程序(如果程序被分成多个单独编译的目标文件,那么编译器一次只能看到程序的一小部分)。他们无法弄清楚调用图的 much/any,因此最后一种情况(需要您显示的代码)变得非常普遍。要解决这个问题,您需要 "link time code generation",但通常人们不会费心。
注意:对于 AVX2,您需要 32 字节对齐,对于 AVX512,您需要 64 字节对齐,对于某些事情(为了避免在大量线程代码中错误共享),您可能需要 "cache line size alignment" (通常也是 64 字节对齐)。这使得 "examine call graph to determine what alignment is actually needed" 算法比我描述的要复杂一些。
我发现我在通过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
(可以与任何为局部变量保留堆栈space的代码合并) - 实际需要16字节对齐的数据可以给16字节对齐而不用自己对齐栈
上面的 - 该函数只需要一个固定的调整来重新建立预先存在的对齐方式,例如
none 可以证明是正确的,所以函数必须假设 "worst case" 并强制自己对齐(例如你在函数开始)
当然,对于一个好的编译器来说,最后一种情况(需要您显示的代码)是极其罕见的。
但是;大多数编译器都不好,因为它们无法看到整个程序(如果程序被分成多个单独编译的目标文件,那么编译器一次只能看到程序的一小部分)。他们无法弄清楚调用图的 much/any,因此最后一种情况(需要您显示的代码)变得非常普遍。要解决这个问题,您需要 "link time code generation",但通常人们不会费心。
注意:对于 AVX2,您需要 32 字节对齐,对于 AVX512,您需要 64 字节对齐,对于某些事情(为了避免在大量线程代码中错误共享),您可能需要 "cache line size alignment" (通常也是 64 字节对齐)。这使得 "examine call graph to determine what alignment is actually needed" 算法比我描述的要复杂一些。