【问题标题】:What exception is raised in C by GCC -fstack-check optionGCC -fstack-check 选项在 C 中引发了什么异常
【发布时间】:2014-10-14 21:40:09
【问题描述】:

根据 gcc 文档

-fstack-check

生成代码以验证您没有超出堆栈的边界。请注意,此开关实际上不会导致进行检查;操作系统必须这样做。该开关会导致生成代码以确保操作系统看到正在扩展的堆栈。

我的假设是这个额外的代码会产生异常让操作系统知道。 使用 C 语言时,我需要知道额外代码生成了什么异常。

Google 也没有太大帮助。关闭我知道它会在 Ada 语言 (Reference) 的情况下生成 Storage_Error 异常。

我正在开发一种需要捕获此异常的小型操作系统/调度程序。我正在使用 C/C++。

我的 GCC 版本 3.4.4

【问题讨论】:

    标签: c++ c exception gcc


    【解决方案1】:

    它不会直接产生任何异常。它会生成代码,当堆栈扩大一页以上时,会生成对新分配区域中的每一页的读写访问。这就是它所做的全部。示例:

    extern void bar(char *);
    void foo(void)
    {
        char buf[4096 * 8];
        bar(buf);
    }
    

    编译(使用 gcc 4.9,在 x86-64 上,-O2)到:

    foo:
            pushq   %rbp
            movq    $-32768, %r11
            movq    %rsp, %rbp
            subq    $4128, %rsp
            addq    %rsp, %r11
    .LPSRL0:
            cmpq    %r11, %rsp
            je      .LPSRE0
            subq    $4096, %rsp
            orq     $0, (%rsp)
            jmp     .LPSRL0
    .LPSRE0:
            addq    $4128, %rsp
            leaq    -32768(%rbp), %rdi
            call    bar
            leave
            ret
    

    orq $0, (%rsp)(%rsp) 处的内存内容没有影响,但 CPU 将其视为对该地址的读写访问。 (我不知道为什么 GCC 在循环期间将 %rsp 偏移了 4128 个字节,或者为什么它认为帧指针是必要的。) 理论上,如果堆栈变得太大,操作系统可以注意到这些访问并做一些适当的事情。对于 POSIX 兼容的操作系统,这将是一个 SIGSEGV 信号的传递。

    您可能想知道操作系统如何注意到这样的事情。硬件允许操作系统将地址空间页面指定为完全不可访问;任何在这些页面中读取或写入内存的尝试都会触发硬件故障,操作系统可以按照它认为合适的方式处理该故障(同样,对于符合 POSIX 的操作系统,提供SIGSEGV)。这可用于在为堆栈保留的空间末尾立即放置一个“保护区”。这就是为什么每页访问一次就足够了。

    -fstack-check 旨在保护您免受“保护区域”非常小的情况(可能只有一页)的影响,因此在堆栈上分配一个大缓冲区会移动堆栈指针 过去该区域并进入可访问 RAM 的另一个区域。如果程序碰巧从不接触保护区内的内存,您不会立即崩溃,但您会在其他区域的任何地方乱涂乱画,从而导致延迟操作故障。

    【讨论】:

    • +1。太可惜了,POSIX 没有提供安全的方法来处理堆栈溢出情况:(
    • @BillyONeal:或者,就此而言,没有定义的机制可以让递归代码避免进行可能破坏堆栈的调用。当然,如果看一下 C 标准的实际要求,就很难编写一个具有标准要求行为的有用程序,因为标准甚至不需要能够处理八深嵌套的堆栈。恕我直言,这是一个相当奇怪的遗漏。
    • @supercat:我们似乎陷入了特定于平台的黑客攻击。 C 标准不定义某些东西是有道理的,但 POSIX 很容易有一个 SIGSTACK 或类似的。至少在 NT 上你可以安全地处理EXCEPTION_STACK_OVERFLOW
    • @BillyONeal:由于各种原因,尝试从堆栈溢出中恢复很容易出现问题。另一方面,我认为标准指定实现必须提供一个方法int __stack_near_limit(int n, int t) 没有问题,该方法必须存在,并且如果系统可能能够支持n,则应在实际返回负值嵌套调用使用 t 字节的本地存储,如果在这种情况下可能会崩溃,则为正值,如果它真的不知道,则为 0。
    • @BillyONeal:如果零总是被认为是合法的(尽管没有帮助)返回值,那么任何平台都应该在提供该方法的一致实现时遇到任何困难。当然,关于可移植性,我认为更需要将 C 分为“基于字节的整数 C”和“不寻常的整数 C”,并努力定义具有可用语义的整数类型,至少对于基于字节的系统.我觉得很可笑,因为uint32_t a=0xFFFFFFFFu; 没有干净的便携式方法来计算 a*a 而不会冒未定义行为的风险。
    猜你喜欢
    • 2017-05-22
    • 2012-01-20
    • 2019-07-06
    • 2011-02-07
    • 2010-09-26
    • 1970-01-01
    • 2014-11-12
    • 2014-03-03
    • 2011-10-10
    相关资源
    最近更新 更多