asm x86中的变量声明顺序?

Posted

技术标签:

【中文标题】asm x86中的变量声明顺序?【英文标题】:Order of variable declaration in asm x86? 【发布时间】:2016-05-12 00:33:41 【问题描述】:

这里是一段代码:

int main()


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

如您所见,check 被声明为 AFTER buffer,所以在堆栈中,我们必须在堆栈中有check ABOVE buffer对吧?

但是,当我用 gdb 反汇编(x86)它时,我得到了:

--> check0xbffff4f8

--> buffer0xbffff4b8

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

另外,我必须告诉你,我在另一台计算机上尝试过同样的事情(x86也是,相同的gcc编译选项,但不同的gdb版本和linux distrib),并且顺序不一样......:S

谢谢!

PS:如果你想了解更多细节,请看截图:(左边是电脑1,右边是电脑2)

【问题讨论】:

不,编译器不需要以任何特定顺序存储它们。如果变量适合寄存器或完全优化掉,它们甚至可能根本不在内存中。 变量可以是任意顺序,变量之前、之后或之间可以有任意数量的填充字节。 为清楚起见,填充字节用于保持对齐。 好吧,我对此有疑问...谢谢! 发布文字,而不是文字图片! 【参考方案1】:

gcc 中有 -fstack-protect 用于重新排序堆栈变量,在近 10 年的一些 Linux 操作系统变体中默认开启,尤其是 Ubuntu、Redhat、gentoo。自 gcc 4.8.3 以来也是默认值(“4.9 及更高版本启用 -fstack-protector-strong。”)

关于 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) 遍历,这将使长函数的编译速度变慢,例如“为什么编译超过 100,000 行的 std::vector::push_back 需要很长时间?” - https://***.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 的作者不会在公共文档 https://gcc.gnu.org/onlinedocs/gcc/Instrumentation-Options.html 中记录 -fstack-protect 的工作原理:

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

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

-fstack-protector-strong-fstack-protector 类似,但包括要保护的附加函数——那些具有本地数组定义或引用本地帧地址的函数。

-fstack-protector-explicit-fstack-protector 类似,但只保护那些具有stack_protect 属性的函数。

而我看到的唯一的 array-before-locals 文档是真正的、最好的文档:源代码

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)

【讨论】:

谢谢,我需要一点时间来理解您回答中的所有内容。我认为-fstack-protect 只是将金丝雀添加到堆栈中以防止基于堆栈的溢出。通过检查该金丝雀是否被覆盖。你能确认一下-fno-stack-protector在堆栈上声明的变量顺序是否符合源文件中的声明顺序? -fstack-protector / -fno-stack-protector 可能(并且将会)改变堆栈中变量的顺序。你有测试,只需用-fno-stack-protector 编译它并检查顺序。如果我的回答解决了您的问题,您可以点击“v”字接受按钮接受,也可以等待其他答案。 好的。所以如果我理解得很好,即使我们设置-fno-stack-protection,堆栈上的变量顺序也可能会改变,对吧?我现在无法检查它,但我会尽快检查。谢谢 变量在栈上的顺序会随着-fstack-protector而改变,而-fno-stack-protector可能不会改变。默认是在 gcc 构建(或安装)到系统中时配置的。

以上是关于asm x86中的变量声明顺序?的主要内容,如果未能解决你的问题,请参考以下文章

x86 ASM Linux - 使用 .bss 部分

js 函数 /变量/ 以及函数中形参的预解析的顺序

连续声明的变量的内存地址顺序总是降序吗?

必须声明标量变量错误 ADO.NET

声明“extern struct cpu * cpu asm(”%gs:0“);”是什么意思?

全局变量的顺序改变了 C++/OpenGL 中的性能