和esp,0xfffffff0

gum*_*hew 16 x86 assembly

我不完全理解下面的注释内容.我在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.
Run Code Online (Sandbox Code Playgroud)

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

nne*_*neo 10

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

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


Mik*_*one 5

看起来它是一些代码的一部分,在开始时设置商店main.

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

   0x08048414 <+0>: push   ebp
Run Code Online (Sandbox Code Playgroud)

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

   0x08048415 <+1>: mov    ebp,esp
   0x08048417 <+3>: and    esp,0xfffffff0
Run Code Online (Sandbox Code Playgroud)

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

   0x0804841a <+6>: sub    esp,0x10
Run Code Online (Sandbox Code Playgroud)

puts((const char*)0x8048510);

   0x0804841d <+9>: mov    DWORD PTR [esp],0x8048510
   0x08048424 <+16>:    call   0x8048320 <puts@plt>
Run Code Online (Sandbox Code Playgroud)

system((const char*)0x8048520);

   0x08048429 <+21>:    mov    DWORD PTR [esp],0x8048520
   0x08048430 <+28>:    call   0x8048330 <system@plt>
Run Code Online (Sandbox Code Playgroud)

退出功能(见另一种答案什么leave呢):

   0x08048435 <+33>:    leave
   0x08048436 <+34>:    ret
Run Code Online (Sandbox Code Playgroud)

"丢弃字节"的例子:在开始时说esp = 0x123C main.第一行代码:

   0x08048414 <+0>: push   ebp
   0x08048415 <+1>: mov    ebp,esp
Run Code Online (Sandbox Code Playgroud)

导致这个内存映射:

0x123C: (start of stack frame of calling function)
0x1238: (old ebp value) <-- esp, ebp
Run Code Online (Sandbox Code Playgroud)

然后:

   0x08048417 <+3>: and    esp,0xfffffff0
Run Code Online (Sandbox Code Playgroud)

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

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

程序员无法依赖于此时esp和之间的一定量的内存ebp ; 因此该存储器被丢弃而不被使用.

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

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

   0x0804841a <+6>: sub    esp,0x10
Run Code Online (Sandbox Code Playgroud)

给我们这张地图:

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
Run Code Online (Sandbox Code Playgroud)

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


Abh*_*D K 5

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

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

2)16字节(128位)是因为SSE2指令有MMXXMM寄存器,并且XMM寄存器是128位的。

3)因此,当进行函数调用时,它会自动对齐到16 byte,在函数之外它仍然保持为8 byte

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