我不完全理解下面的注释内容。我在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)生成的。感谢。
答案 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
此时程序员无法依赖在esp
和ebp
之间的一定数量的内存; 因此该存储器被丢弃而不被使用。
最后,程序分配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 )。