我一直是一名高级编码员,架构对我来说还很陌生,所以我决定在这里阅读关于汇编的教程:
http://en.wikibooks.org/wiki/X86_Assembly/Print_Version
在教程的最下面,说明了如何转换 Hello World!程序
#include <stdio.h>
int main(void) {
printf("Hello, world!\n");
return 0;
}
给出了等效的汇编代码并生成了以下内容:
.text
LC0:
.ascii "Hello, world!\12\0"
.globl _main
_main:
pushl %ebp
movl %esp, %ebp
subl $8, %esp
andl $-16, %esp
movl $0, %eax
movl %eax, -4(%ebp)
movl -4(%ebp), %eax
call __alloca
call ___main
movl $LC0, (%esp)
call _printf
movl $0, %eax
leave
ret
对于其中一行,
andl $-16, %esp
解释是:
This code "and"s ESP with 0xFFFFFFF0, aligning the stack with the next lowest 16-byte boundary. An examination of Mingw's source code reveals that this may be for SIMD instructions appearing in the "_main" routine, which operate only on aligned addresses. Since our routine doesn't contain SIMD instructions, this line is unnecessary.
我不明白这一点。有人可以解释一下将堆栈与下一个 16 字节边界对齐的含义以及为什么需要这样做吗? andl
是如何做到这一点的?
最佳答案
假设堆栈在 _main
的入口处看起来像这样(堆栈指针的地址只是一个示例):
| existing |
| stack content |
+-----------------+ <--- 0xbfff1230
压入%ebp
,并从%esp
中减去8,为局部变量保留一些空间:
| existing |
| stack content |
+-----------------+ <--- 0xbfff1230
| %ebp |
+-----------------+ <--- 0xbfff122c
: reserved :
: space :
+-----------------+ <--- 0xbfff1224
现在,andl
指令将 %esp
的低 4 位清零,这可能会减少它;在此特定示例中,它具有保留额外 4 个字节的效果:
| existing |
| stack content |
+-----------------+ <--- 0xbfff1230
| %ebp |
+-----------------+ <--- 0xbfff122c
: reserved :
: space :
+ - - - - - - - - + <--- 0xbfff1224
: extra space :
+-----------------+ <--- 0xbfff1220
重点是有一些“SIMD”(单指令,多数据)指令(在 x86 领域也称为“SSE”,表示“流式 SIMD 扩展”)可以对多个字执行并行操作内存,但要求这些多个字是一个 block ,起始地址是 16 字节的倍数。
一般来说,编译器不能假定 %esp
的特定偏移量会产生一个合适的地址(因为 %esp
在函数入口处的状态取决于调用代码)。但是,通过以这种方式有意对齐堆栈指针,编译器知道将 16 字节的任意倍数添加到堆栈指针将产生 16 字节对齐的地址,这对于这些 SIMD 指令的使用是安全的。
关于c - 对齐堆栈是什么意思?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26783564/