我不完全理解下面的注释内容.我在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的倍数的效果.
看起来它是一些代码的一部分,在开始时设置商店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.
我知道它是很久以前发布的,它可能对其他人有帮助。
1)在现代处理器中,我们知道GCC默认将堆栈对齐为16字节 对齐。
2)16字节(128位)是因为SSE2指令有MMX和XMM寄存器,并且XMM寄存器是128位的。
3)因此,当进行函数调用时,它会自动对齐到16 byte,在函数之外它仍然保持为8 byte。
4)使用0xfffffff0的逻辑是将低 4 位保持为 0 ,这是因为简单的布尔数学表示,在二进制中,16 的倍数的低 4 位为零 (为什么是四位? 2^4 = 16)。