什么是`和esp,0xfffffff0`?

ker*_*nux 8 assembly gdb disassembly

当我main()在gdb中反汇编它时,它会返回这个结果:

 0x0804854c <+0>:   push   ebp
   0x0804854d <+1>: mov    ebp,esp
   0x0804854f <+3>: and    esp,0xfffffff0
   0x08048552 <+6>: sub    esp,0x10
Run Code Online (Sandbox Code Playgroud)

并在代码中我检查esp寄存器如下:

x/32xw $esp
Run Code Online (Sandbox Code Playgroud)

这使得:

0xbffffb70: 0xb7ea1515  0xb7ff0590  0x080485eb  0xb7fd2ff4
0xbffffb80: 0x080485e0  0x00000000  0xbffffc08  0xb7e88e46
0xbffffb90: 0x00000002  0xbffffc34  0xbffffc40  0xb7fe0860
0xbffffba0: 0xb7ff6821  0xffffffff  0xb7ffeff4  0x080482bc
0xbffffbb0: 0x00000001  0xbffffbf0  0xb7fefc16  0xb7fffac0
0xbffffbc0: 0xb7fe0b58  0xb7fd2ff4  0x00000000  0x00000000
0xbffffbd0: 0xbffffc08  0xd51689cb  0xfbfdbfdb  0x00000000
0xbffffbe0: 0x00000000  0x00000000  0x00000002  0x08048400
Run Code Online (Sandbox Code Playgroud)

我想知道and esp,0xfffffff0这些结果之间的关系是什么.

Pau*_*l R 10

and esp,0xfffffff0强制执行16字节堆栈对齐,这是一个常见的ABI要求.它通过屏蔽(设置为零)堆栈指针的最低4位来完成此操作,这相当于向下舍入到最接近的16的倍数.


Ble*_*ose 8

它创建一个所谓的堆栈帧并将其与可以除以16的地址对齐:

  1. 从堆栈中调用programm保存堆栈帧指针:
    push ebp.
  2. 为程序创建新的stackframe指针,该指针被调用:
    mov ebp, esp
  3. 通过将最低4位设置为0,将堆栈对齐到可以除以16的地址:
    and esp, -16
  4. 在堆栈中创建16字节空间,例如局部变量和东西:
    sub esp, 0x10

对齐为什么?
cpu总是一次读取16个字节的数据(取决于cpu类型).但它只从一个可以除以16的地址读取:0x0,0x10,0x20,......依此类推,因为地址总线中没有使用最低的4位.他们"失踪".当你从一个地址读取多个字节时,cpu可能必须读两次,因为你的地址是指向dword或类似的那个,就在一个地址的末尾,可以除以16和你的dword通过将堆栈与可除以16的地址相对应,可以降低发生这种情况的风险.

您可以在发布的示例中看到.ESP的值在左侧,并与可分为16的地址对齐.由于结束,很容易看到0:

0xbffffb70: 
0xbffffb80: 
0xbffffb90: 
0xbffffba0: 
0xbffffbb0: 
0xbffffbc0: 
0xbffffbd0: 
0xbffffbe0:
Run Code Online (Sandbox Code Playgroud)


Cod*_*dor 0

显然,通过屏蔽它来and esp,0xfffffff0删除最低的半字节。esp