为什么Linux内核在堆栈溢出时生成segfault?当c或alltran中的临时数组创建溢出时,这会使调试变得很尴尬。当然,运行时肯定有可能产生更有用的错误。
您实际上可以使用信号处理程序来捕获堆栈溢出的条件。
为此,您必须做两件事:
然后,当您使堆栈溢出时,内核将在传递信号之前切换到备用堆栈。进入信号处理程序后,您可以检查导致错误的地址,并确定是堆栈溢出还是常规错误。
"内核"(实际上不是运行代码的内核,是CPU)不知道您的代码是如何引用不应接触的内存的。它只知道您尝试这样做。
代码:
1 2
| char *x = alloca(100);
char y = x[150]; |
当您尝试访问x的边界之外时,
不能真正被CPU评估。
您可能会使用以下相同的地址:
1
| char y = *((char*)(0xdeadbeef)); |
顺便说一句,我不鼓励使用alloca,因为堆栈比堆更受限制(请使用malloc代替)。
堆栈溢出是分段错误。就像您打破了最初分配给您的给定内存范围一样。有限大小的堆栈,您已经超过了它。您可以在Wikipedia
上阅读有关此内容的更多信息。
此外,我过去对项目所做的一件事是将自己的信号处理程序编写到segfault(请参见手册页信号(2))。我通常会捕获信号,然后在控制台上写出"发生致命错误"。我做了其他一些检查点标记和调试的工作。
为了调试段错误,您可以在GDB中运行一个程序。例如,以下C程序将出现段错误:
#segfault.c
#包括
#include
1 2 3 4 5 6 7 8
| int main()
{
printf("Starting\
");
void *foo=malloc(1000);
memcpy(foo, 0, 100); //this line will segfault
exit(0);
} |
如果我这样编译它:
1
| gcc -g -o segfault segfault.c |
,然后像这样运行它:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| $ gdb ./segfault
GNU gdb 6.7.1
Copyright (C) 2007 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type"show copying"
and"show warranty" for details.
This GDB was configured as"i686-pc-linux-gnu"...
Using host libthread_db library"/lib/libthread_db.so.1".
(gdb) run
Starting program: /tmp/segfault
Starting
Program received signal SIGSEGV, Segmentation fault.
0x4ea43cbc in memcpy () from /lib/libc.so.6
(gdb) bt
#0 0x4ea43cbc in memcpy () from /lib/libc.so.6
#1 0x080484cb in main () at segfault.c:8
(gdb) |
我从GDB中发现,第8行存在分段错误。当然,有更复杂的方法来处理堆栈溢出和其他内存错误,但这足够了。
只需使用Valgrind。它会以极高的精确度指出您所有的内存分配错误。
一些注释是有帮助的,但是问题不在于内存分配错误。那是没有错误的代码。在fortran中,运行时会在堆栈上分配临时值,这很麻烦。因此,例如
写(fp)x,y,z
可以触发没有错误的段错误。对Intel Fortran编译器的技术支持说,运行时库无法打印出更有用的消息。但是,如果米格尔(Miguel)是对的,那么他将建议这样做。非常感谢。接下来剩下的问题是我该如何首先找到seg错误的地址,并弄清它是否来自堆栈溢出或其他问题。
对于其他发现此问题的人,有一个编译器标志,该标志将临时变量放在堆上一定大小以上。
堆栈溢出不一定会导致崩溃。它可能会悄无声息地破坏程序的数据,但会继续执行。
我不会使用SIGSEGV处理程序错误,而是要解决原始问题。
如果需要自动帮助,可以使用gcc的-Wstack-protector选项,该选项将在运行时发现一些溢出并中止程序。
valgrind适用于动态内存分配错误,但不适用于堆栈错误。