代码之家  ›  专栏  ›  技术社区  ›  shodanex

在编译时检查堆栈使用情况

  •  29
  • shodanex  · 技术社区  · 17 年前

    在C语言中,有没有一种方法可以知道和输出函数在编译时所需的堆栈大小? 我想知道的是:

    让我们来做一些功能:

    void foo(int a) {
        char c[5];
        char * s;
        //do something
        return;
    }
    

    在编译这个函数时,我想知道当调用它时,它将消耗多少堆栈空间。这对于检测隐藏大缓冲区的结构的堆栈上声明可能很有用。

    我要找的是像这样的印刷品:

    文件foo.c:函数foo stack的用法是 n 字节

    是否有一种方法不查看生成的程序集来了解这一点?或者可以为编译器设置的限制?

    更新:我不想避免给定进程的运行时堆栈溢出,我正在寻找一种在运行时之前查找的方法,如果编译器确定的函数堆栈用法作为编译进程的输出可用。

    换一种说法:是否可以知道函数局部所有对象的大小?我想编译器优化不会是我的朋友,因为一些变量会消失,但一个更高的限制是好的。

    7 回复  |  直到 9 年前
        1
  •  10
  •   community wiki Blaisorblade    17 年前

    Linux内核代码在x86上的4K堆栈上运行。因此他们关心。他们用来检查的是他们编写的一个Perl脚本,您可以在最近的内核tarball中找到它作为scripts/checkstack.pl。它运行在objdump的输出上,使用文档在初始注释中。

    我想很久以前我就已经在用户空间二进制文件中使用过它了,如果您知道一些Perl编程,那么如果它被破坏了,就很容易修复它。

    不管怎样,它基本上是自动查看gcc的输出。内核黑客编写了这样一个工具,这意味着没有静态的方法来处理gcc(或者可能它是最近添加的,但我对此表示怀疑)。

    顺便说一句,使用mingw项目和activeperl中的objdump,或者使用cygwin,您应该也能够在Windows以及其他编译器获得的二进制文件中这样做。

        2
  •  8
  •   Community Mohan Dere    9 年前

    Stackanlyser似乎检查了可执行代码本身以及一些调试信息。 描述的内容 this reply ,这是我要找的,堆栈分析器在我看来是杀伤力太大了。

    类似于Ada存在的东西会很好。从GNAT手册中查看本手册页面:

    22.2静态堆栈使用分析

    使用-fstack用法编译的单元将生成一个额外的文件,指定每个函数使用的最大堆栈量。该文件与扩展名为.su的目标对象文件具有相同的基名称。此文件的每一行由三个字段组成:

    * The name of the function.
    * A number of bytes.
    * One or more qualifiers: static, dynamic, bounded. 
    

    第二个字段对应于函数框架的已知部分的大小。

    限定符static表示函数帧大小是纯静态的。它通常意味着所有局部变量都有一个静态大小。在这种情况下,第二个字段是对函数堆栈利用率的可靠度量。

    限定符dynamic表示函数帧大小不是静态的。它主要发生在一些局部变量具有动态大小的情况下。当这个限定符单独出现时,第二个字段不是函数堆栈分析的可靠度量。当限定为有界时,意味着第二个字段是函数堆栈利用率的可靠最大值。

        3
  •  3
  •   Community Mohan Dere    9 年前

    我不明白为什么静态代码分析不能给出足够好的数字。

    在任何给定函数中查找所有局部变量都很简单,每个变量的大小可以通过C标准(对于内置类型)或通过计算(对于结构和联合等复杂类型)找到。

    当然,答案不能保证100%准确,因为编译器可以进行各种优化,如填充、将变量放入寄存器或完全删除不必要的变量。但它给出的任何答案至少都应该是一个很好的估计。

    我做了一个快速的谷歌搜索,发现 StackAnalyzer 但我猜其他静态代码分析工具也有类似的功能。

    如果你想要一个100%准确的数字,那么你必须查看编译器的输出或者在运行时检查它(就像Ralph在 his reply )

        4
  •  1
  •   1800 INFORMATION    17 年前

    只有编译器才会真正知道,因为是他把你所有的东西放在一起。您必须查看生成的程序集,并查看序言中保留了多少空间,但这并不能真正解释诸如 alloca 在运行时做他们的事情。

        5
  •  1
  •   Will Dean    17 年前

    假设您在一个嵌入式平台上,您可能会发现您的工具链在这方面有一个优势。好的商业嵌入式编译器(例如arm/keil编译器)通常会生成堆栈使用情况的报告。

    当然,中断和递归通常有点超出了它们的范围,但是如果有人在堆栈的某个地方用一个兆字节的缓冲区犯了一些可怕的错误,它会给你一个粗略的概念。

        6
  •  1
  •   Suma    16 年前

    不完全是“编译时”,但我会在构建后的步骤中这样做:

    • 让链接器为您创建一个映射文件
    • 对于映射文件中的每个函数,读取可执行文件的相应部分,并分析函数序言。

    这与StackAnalyzer类似,但要简单得多。我认为分析可执行文件或反汇编是获得编译器输出的最简单方法。虽然编译器在内部知道这些事情,但恐怕您无法从中获得(您可能会要求编译器供应商实现该功能,或者如果使用开放源代码编译器,您可以自己做,也可以让别人帮您做)。

    要实现这一点,您需要:

    • 能够解析映射文件
    • 了解可执行文件的格式
    • 了解函数序言的外观,并能够“解码”它

    这有多容易或困难取决于你的目标平台。(嵌入式)?哪个CPU架构?什么编译器?)

    所有这些都可以在x86/win32中完成,但是如果您从未做过类似的事情,并且必须从头开始创建所有这些内容,则可能需要几天时间才能完成,并开始工作。

        7
  •  -1
  •   xmjx    17 年前

    一般来说不是。理论计算机科学中的停顿问题表明,你甚至无法预测一个通用程序是否会在给定的输入上停顿。计算用于程序运行的堆栈通常会更复杂。所以:不,可能在特殊情况下。

    假设您有一个递归函数,它的递归级别取决于输入,输入的长度可以是任意的,您已经走运了。