为什么缓冲区末尾和保存的帧指针之间有8个字节?

mer*_*011 6 c c++ linux stack-overflow gcc

我正在为课程作业进行堆叠式练习,我已完成作业,但有一个方面我不明白.

这是目标计划:

#include <stdio.h>                                             
#include <stdlib.h>                                            
#include <string.h>                                            

int bar(char *arg, char *out)                                  
{                                                              
  strcpy(out, arg);                                            
  return 0;                                                    
}                                                              

void foo(char *argv[])                                         
{                                                              
  char buf[256];                                               
  bar(argv[1], buf);                                           
}                                                              

int main(int argc, char *argv[])                               
{                                                              
  if (argc != 2)                                               
    {                                                          
      fprintf(stderr, "target1: argc != 2\n");                 
      exit(EXIT_FAILURE);                                      
    }                                                          
  foo(argv);                                                   
  return 0;                                                    
}                                                              
Run Code Online (Sandbox Code Playgroud)

以下是在x86运行的虚拟机上Ubuntu 12.04使用已ASLR禁用的编译命令.

gcc -ggdb -m32 -g -std=c99 -D_GNU_SOURCE -fno-stack-protector  -m32  target1.c   -o target1
execstack -s target1
Run Code Online (Sandbox Code Playgroud)

当我在堆栈上查看这个程序的内存时,我看到它buf有地址0xbffffc40.此外,保存的帧指针存储在0xbffffd48,并且返回地址存储在0xbffffd4c.

这些具体地址不相关,但我观察到即使buf只有长度256,距离0xbffffd48 - 0xbffffc40 = 264.象征性地,这个计算是$fp - buf.

为什么堆栈8末端buf和存储的帧指针之间有额外的字节?

这是函数的一些反汇编foo.我已经检查了它,但我没有看到该内存区域的任何明显用法,除非它是隐含的(即某些指令的副作用).

   0x080484ab <+0>:     push   %ebp                    
   0x080484ac <+1>:     mov    %esp,%ebp               
   0x080484ae <+3>:     sub    $0x118,%esp             
   0x080484b4 <+9>:     mov    0x8(%ebp),%eax          
   0x080484b7 <+12>:    add    $0x4,%eax               
   0x080484ba <+15>:    mov    (%eax),%eax             
   0x080484bc <+17>:    lea    -0x108(%ebp),%edx       
   0x080484c2 <+23>:    mov    %edx,0x4(%esp)          
   0x080484c6 <+27>:    mov    %eax,(%esp)             
   0x080484c9 <+30>:    call   0x804848c <bar>         
   0x080484ce <+35>:    leave                          
   0x080484cf <+36>:    ret                            
Run Code Online (Sandbox Code Playgroud)

mer*_*011 3

Basile Starynkevitch 因提及而获奖alignment

事实证明,gcc 4.7.2默认将帧边界与 4 字边界对齐。在 32 位模拟硬件上,即 16 个字节。由于保存的帧指针和保存的指令指针总共只占用 8 个字节,因此编译器在结束后再放入 8 个字节,buf以将堆栈帧的顶部与 16 字节边界对齐。

使用以下附加编译器标志,8 个字节消失,因为 8 个字节足以与 2 字边界对齐。

-mpreferred-stack-boundary=2
Run Code Online (Sandbox Code Playgroud)