和esp,0xfffffff0

时间:2014-07-05 17:23:59

标签: assembly x86

我不完全理解下面的注释内容。我在SO和gcc手册中阅读了一些帖子,并了解到它是用于堆栈地址对齐但却无法理解它是如何做到的。代码如下所示:

(gdb) disas main
Dump of assembler code for function main:
   0x08048414 <+0>: push   ebp
   0x08048415 <+1>: mov    ebp,esp
   0x08048417 <+3>: and    esp,0xfffffff0 ; why??
   0x0804841a <+6>: sub    esp,0x10
   0x0804841d <+9>: mov    DWORD PTR [esp],0x8048510
   0x08048424 <+16>:    call   0x8048320 <puts@plt>
   0x08048429 <+21>:    mov    DWORD PTR [esp],0x8048520
   0x08048430 <+28>:    call   0x8048330 <system@plt>
   0x08048435 <+33>:    leave
   0x08048436 <+34>:    ret
End of assembler dump.

代码是在linux上使用gcc(版本4.6.3)生成的。感谢。

3 个答案:

答案 0 :(得分:9)

and esp, 0xfffffff0在堆栈指针和常量之间执行按位AND,并将结果存储回堆栈指针。

选择常数使其低四位为零。因此,AND运算会在结果中将这些位设置为零,并使esp的其他位保持不变。这样可以将堆栈指针向下舍入到最接近的16的倍数。

答案 1 :(得分:5)

main开头设置商店时,它似乎是某些代码的一部分。

函数start:将基帧指针保存在堆栈上(稍后需要leave指令):

   0x08048414 <+0>: push   ebp

现在我们将堆栈指针与16字节绑定对齐,因为编译器(无论出于何种原因)需要它。这可能是它总是需要16字节对齐的帧,或者局部变量需要16字节对齐(可能有人使用uint128_t或者他们使用的是使用gcc vector extensions的类型。基本上,由于结果总是小于或等于当前堆栈指针,并且堆栈向下增长,它只是丢弃字节,直到达到16字节对齐点。

   0x08048415 <+1>: mov    ebp,esp
   0x08048417 <+3>: and    esp,0xfffffff0

接下来,我们从堆栈指针中减去16,创建16个字节的局部变量空间:

   0x0804841a <+6>: sub    esp,0x10

puts((const char*)0x8048510);

   0x0804841d <+9>: mov    DWORD PTR [esp],0x8048510
   0x08048424 <+16>:    call   0x8048320 <puts@plt>

system((const char*)0x8048520);

   0x08048429 <+21>:    mov    DWORD PTR [esp],0x8048520
   0x08048430 <+28>:    call   0x8048330 <system@plt>

退出该功能(请参阅another answer了解leave的内容):

   0x08048435 <+33>:    leave
   0x08048436 <+34>:    ret

&#34>丢弃字节的示例&#34;:在main开始时说esp = 0x123C。第一行代码:

   0x08048414 <+0>: push   ebp
   0x08048415 <+1>: mov    ebp,esp

导致此内存映射:

0x123C: (start of stack frame of calling function)
0x1238: (old ebp value) <-- esp, ebp

然后:

   0x08048417 <+3>: and    esp,0xfffffff0

将esp的最后4位强制为0,这样做:

0x123C: (start of stack frame of calling function)
0x1238: (old ebp value) <-- ebp
0x1234: (undefined)
0x1230: (undefined) <-- esp

此时程序员无法依赖espebp 之间的一定数量的内存; 因此该存储器被丢弃而不被使用。

最后,程序分配16个字节的堆栈(本地)存储:

接下来,我们从堆栈指针中减去16,创建16个字节的局部变量空间:

   0x0804841a <+6>: sub    esp,0x10

给我们这张地图:

0x123C: (start of stack frame of calling function)
0x1238: (old ebp value) <-- ebp
0x1234: (undefined)
0x1230: (undefined)
0x123C: (undefined local space)
0x1238: (undefined local space)
0x1234: (undefined local space)
0x1230: (undefined local space) <-- esp

此时,程序可以确定esp指向16字节的16字节对齐内存。

答案 2 :(得分:1)

我知道它是很久以前发布的,可能会对其他人有所帮助。

  

1)在现代处理器中,我们知道 GCC 将堆栈默认对齐为 16字节

     

2) 16字节(128位)是因为 SSE2 指令具有 MMX XMM 寄存器和 XMM 寄存器为 128 位。

     

3),因此在进行函数调用时,它会自动对齐 16字节,而在函数外部,它将保持 8字节

     

4)使用 0xfffffff0 的逻辑是将低4位保持为0,这是由于简单的布尔数学表示,在二进制中, 16位具有低4位为零(为什么是4位? 2 ^ 4 = 16 )。