对于这个简单的代码:
void main(){
char buf[12];
buf[11]='\xff';
puts(buf);
}
我使用gdb来调试此代码并获取它的堆栈信息:
0xbffff480: 0x40158ff4 0x40158ff4 0xff0494dc 0x40158ff4
0xbffff490: 0x00000000 0x40016ca0 0xbffff4f8 0x40045de3
0xbffff480是“buf”开始的地方,最后两个字是EBP和RET,但是buf和EBP之间到底是什么?显然我没有任何其他的本地变量。我也尝试过,如果我在堆栈上为buf分配8个字节,它只是从EBP继续,但是如果我分配9个或更多字节,它总是介于两者之间。有人可以向我解释一下吗?非常感谢!我在Linux 2.6.9上
主要反汇编:
0x080483c4 <main+0>: push %ebp
0x080483c5 <main+1>: mov %esp,%ebp
0x080483c7 <main+3>: sub $0x28,%esp
0x080483ca <main+6>: and $0xfffffff0,%esp
0x080483cd <main+9>: mov $0x0,%eax
0x080483d2 <main+14>: add $0xf,%eax
0x080483d5 <main+17>: add $0xf,%eax
0x080483d8 <main+20>: shr $0x4,%eax
0x080483db <main+23>: shl $0x4,%eax
0x080483de <main+26>: sub %eax,%esp
0x080483e0 <main+28>: movb $0xff,0xfffffff3(%ebp)
0x080483e4 <main+32>: lea 0xffffffe8(%ebp),%eax
0x080483e7 <main+35>: mov %eax,(%esp)
0x080483ea <main+38>: call 0x80482e4
0x080483ef <main+43>: leave
0x080483f0 <main+44>: ret
答案 0 :(得分:4)
那将是padding。您的编译器在8字节边界上可能是aligning EBP
,因为对齐的内存几乎总是easier and faster可以使用(从处理器的角度来看)。某些数据类型甚至需要正确对齐才能工作。
在缓冲区中仅分配8个字节时,您看不到任何填充,因为在这种情况下,EBP
已经正确对齐。
答案 1 :(得分:1)
通常gcc保持堆栈对齐到16的倍数,以便能够使用SSE指令。阅读main
的反汇编是有益的。
答案 2 :(得分:1)
众所周知,GCC对堆栈使用感到满意。它通常会保留比严格需要更多的堆栈空间。此外,即使没有必要,它也会尝试实现16字节的堆栈对齐。这似乎是第一条指令所发生的事情(保留40个字节,然后%esp对齐到16的倍数)。
然而,你展示的代码包含了奇怪的东西,尤其是偏移9到27的序列:这是一种从%esp中减去16的长,慢,复杂的方法,可以在单个操作码中完成。从那时起从%esp中减去一些字节对于调用外部函数(puts()
)是合乎逻辑的,并且count(16)尊重对齐,但为什么这样做会以这种奇怪的方式呢?
这个序列可能是以某种方式(例如在链接时)修补,以支持堆栈粉碎检测代码或某种分析代码。我无法在自己的系统上重现这一点。您应该指定正在使用的gcc
和libc的版本,确切的编译标记和Linux发行版(因为发布者可能默认激活某些选项)。 “2.6.9”数字是内核版本,它对手头的问题没有任何影响(它只是告诉我们系统很老)。
答案 3 :(得分:0)
您应该包含一个aseembly转储而不是纯十六进制转储。但它很可能是两件事之一:
开头还可能包含堆栈对齐,强制执行上述一个或两个