asm x86中变量声明的顺序?

时间:2016-02-03 00:32:12

标签: c gcc x86 stack stack-overflow

这段代码:

int main()
{

  char buffer[64];
  int check;
...

如您所见,check被声明为 AFTER buffer,因此在堆叠中,我们必须check ABOVE 堆栈中的buffer对吗?

然而,当我用gdb反汇编(x86)时,这就是我得到的:

- > check

0xbffff4f8

- > buffer

0xbffff4b8

我的问题:堆栈中是否存在局部变量的特定顺序?

另外,我必须告诉你,我在另一台计算机上尝试过相同的东西(x86也是,gcc编译选项相同,但gdb版本和linux发行版不同),顺序不一样......:S < / p>

谢谢!

PS:如果您想了解更多详情,请参阅屏幕截图:(计算机1左侧,计算机2右侧)enter image description here

1 个答案:

答案 0 :(得分:3)

gcc中有-fstack-protect重新排序堆栈变量,默认情况下在某些Linux操作系统变体中打开了近10年,特别是Ubuntu,Redhat,gentoo。同样默认为gcc 4.8.3(&#34; 4.9及更高版本启用-fstack-protector-strong。&#34;)

关于gcc默认值的Ubuntu页面:https://wiki.ubuntu.com/ToolChain/CompilerFlags

  

工具链中特定于Ubuntu的默认编译器标志,用于帮助为Ubuntu提供额外的安全功能。 ...   默认标志-fstack-protector ...首先在Ubuntu 6.10中启用。

关于堆栈保护的Ubuntu页面https://wiki.ubuntu.com/GccSsp

  

gcc 4.1现在附带SSP,这是一种很好的技术,可以减轻许多缓冲区溢出的可利用性。 ... SSP提供了一种技术来阻止这类漏洞的可利用性:(1)重新排序堆栈变量 ...... RedHat和gentoo默认使用SSP多年

这种重新排序需要对函数的所有局部变量进行多次O(n^2)遍历,这将使长函数的编译速度变慢,例如&#34;为什么编译超过100,000行的std :: vector :: push_back需要很久了?&#34; - https://stackoverflow.com/a/14034393/196561

  

启用-fstack-protect时强制执行延迟分配(有时需要重新排序所有堆栈变量)。 .. cfgexpand.c

969 /* A subroutine of expand_one_var.  VAR is a variable that will be
970    allocated to the local stack frame.  Return true if we wish to
971    add VAR to STACK_VARS so that it will be coalesced with other
972    variables.  Return false to allocate VAR immediately.
973 
974    This function is used to reduce the number of variables considered
975    for coalescing, which reduces the size of the quadratic problem.  */
976 
977 static bool
978 defer_stack_allocation (tree var, bool toplevel)
980   /* If stack protection is enabled, *all* stack variables must be deferred,
981      so that we can re-order the strings to the top of the frame.  */

因此,gcc将重新排序所有堆栈变量,字符串将位于帧的顶部。 尝试使用-fno-stack-protector选项停用。

像往常一样,gcc的作者没有记录-fstack-protect在公开文档https://gcc.gnu.org/onlinedocs/gcc/Instrumentation-Options.html中的工作原理:

  

-fstack-protector   发出额外的代码来检查缓冲区溢出,例如堆栈粉碎攻击。这是通过向具有易受攻击对象的函数添加保护变量来完成的。这包括调用alloca的函数,以及缓冲区大于8字节的函数。输入功能时会初始化防护装置,然后在功能退出时进行检查。如果防护检查失败,则会打印一条错误消息并退出程序。

     

-fstack-protector-all   与-fstack-protector类似,只是所有功能都受到保护。

     

-fstack-protector-strong   与-fstack-protector类似,但包括要保护的其他函数 - 具有本地数组定义或具有本地帧地址引用的函数。

     

-fstack-protector-explicit   与-fstack-protector类似,但仅保护具有stack_protect属性的函数。

我看到的本地数组之前的唯一文档是真实的,最好的文档:源代码

https://gcc.gnu.org/viewcvs/gcc/branches/gcc-4_6-branch/gcc/cfgexpand.c?revision=175029&view=markup#l1526 - expand_used_vars()

1533          if (has_protected_decls)
1534            {
1535              /* Phase 1 contains only character arrays.  */
1536              expand_stack_vars (stack_protect_decl_phase_1);
1537    
1538              /* Phase 2 contains other kinds of arrays.  */
1539              if (flag_stack_protect == 2)
1540                expand_stack_vars (stack_protect_decl_phase_2);
1541            }
1542    
1543          expand_stack_vars (NULL);

阶段1和阶段2变量由stack_protect_decl_phase() https://gcc.gnu.org/viewcvs/gcc/branches/gcc-4_6-branch/gcc/cfgexpand.c?revision=175029&view=markup#l1235

分隔
1235    /* Return nonzero if DECL should be segregated into the "vulnerable" upper
1236       part of the local stack frame.  Remember if we ever return nonzero for
1237       any variable in this function.  The return value is the phase number in
1238       which the variable should be allocated.  */
1239    
1240    static int
1241    stack_protect_decl_phase (tree decl)
 ...
1243      unsigned int bits = stack_protect_classify_type (TREE_TYPE (decl));
 ...
1249      if (flag_stack_protect == 2)
1250        {
1251          if ((bits & (SPCT_HAS_SMALL_CHAR_ARRAY | SPCT_HAS_LARGE_CHAR_ARRAY))
1252              && !(bits & SPCT_HAS_AGGREGATE))
1253            ret = 1;
1254          else if (bits & SPCT_HAS_ARRAY)
1255            ret = 2;
1256        }

stack_protect_classify_type只返回HAS_ARRAYHAS_*_CHAR_ARRAY仅用于char数组(char, unsigned char and signed char