堆栈上的本地var和EBP之间有什么关系?

时间:2010-11-12 08:58:28

标签: c linux assembly stack

对于这个简单的代码:

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

4 个答案:

答案 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转储而不是纯十六进制转储。但它很可能是两件事之一:

  1. 正在恢复的堆栈框架
  2. 进行堆叠检查以确保没有任何问题
  3. 开头还可能包含堆栈对齐,强制执行上述一个或两个