输入函数时的SIGSEGV

时间:2012-05-08 13:17:01

标签: c++ linux gdb segmentation-fault

刚进入函数时会出现什么分段错误?

输入的功能如下:

21:  void eesu3(Matrix & iQ)
22:  {

其中Matrixstruct。当使用GDB运行时,回溯产生:

(gdb) backtrace 
#0  eesu3 (iQ=...) at /home/.../eesu3.cc:22
#1  ...

GDB没有说明iQ是什么。 ...就在那里。 什么可能导致这个?

海湾合作委员会:(Ubuntu / Linaro 4.6.3-1ubuntu5)4.6.3

使用-O3 -g

构建的程序

来电者喜欢:

Matrix q;
// do some stuff with q
eesu3(q);

这里没什么特别的

我用valgrind重新编写程序:

valgrind --tool=memcheck --leak-check=yes --show-reachable=yes --num-callers=20 --track-fds=yes <prgname>

输出:

==2240== Warning: client switching stacks?  SP change: 0x7fef7ef68 --> 0x7fe5e3000
==2240==          to suppress, use: --max-stackframe=10076008 or greater
==2240== Invalid write of size 8
==2240==    at 0x14C765B: eesu3( Matrix &) (eesu3.cc:22)
...
==2240==  Address 0x7fe5e3fd8 is on thread 1's stack
==2240== 
==2240== Can't extend stack to 0x7fe5e2420 during signal delivery for thread 1:
==2240==   no stack segment
==2240== 
==2240== Process terminating with default action of signal 11 (SIGSEGV)
==2240==  Access not within mapped region at address 0x7FE5E2420
==2240==    at 0x14C765B: eesu3( Matrix&) (eesu3.cc:22)
==2240==  If you believe this happened as a result of a stack
==2240==  overflow in your program's main thread (unlikely but
==2240==  possible), you can try to increase the size of the
==2240==  main thread stack using the --main-stacksize= flag.
==2240==  The main thread stack size used in this run was 8388608.

看起来像是一个损坏的堆栈。

    Dump of assembler code for function eesu3( Matrix & ):
   0x00000000014c7640 <+0>: push   %rbp
   0x00000000014c7641 <+1>: mov    %rsp,%rbp
   0x00000000014c7644 <+4>: push   %r15
   0x00000000014c7646 <+6>: push   %r14
   0x00000000014c7648 <+8>: push   %r13
   0x00000000014c764a <+10>:    push   %r12
   0x00000000014c764c <+12>:    push   %rbx
   0x00000000014c764d <+13>:    and    $0xfffffffffffff000,%rsp
   0x00000000014c7654 <+20>:    sub    $0x99b000,%rsp
=> 0x00000000014c765b <+27>:    mov    %rdi,0xfd8(%rsp)

好的,要说清楚:Matrix的数据存在于堆上。它基本上包含一个指向数据的指针。结构很小,32个字节。 (刚检查过)

现在,我使用不同的优化选项重建了程序:

-O0:错误未显示。

-O1:错误显示。

-O3:错误显示。

- 更新

-O3 -fno-inline -fno-inline-functions:错误未显示。

这解释了它。函数内联太多会导致堆栈使用过多。

问题是由于堆栈溢出

4 个答案:

答案 0 :(得分:15)

  

刚进入函数时会出现什么分段错误?

最常见的原因是堆栈耗尽。在崩溃点做(gdb) disas。如果崩溃的指令是在%rsp递减之后第一次读取或写入堆栈位置,那么堆栈耗尽几乎肯定是原因。

解决方案通常涉及创建具有较大堆栈的线程,将一些大型变量从堆栈移动到堆堆,或两者兼而有之。

另一个可能的原因:如果Matrix包含非常大的数组,则无法将其置于堆栈中:内核将堆栈超出当前范围超过128K(左右),我不记得确切的价值)。如果Matrix大于该限制,则无法将其置于堆栈中。

更新

   0x00000000014c7654 <+20>:    sub    $0x99b000,%rsp
=> 0x00000000014c765b <+27>:    mov    %rdi,0xfd8(%rsp)

这次拆卸证实了诊断。

此外,您在堆栈上保留0x99b000字节(大约10MB)。在eesu3例程中,您必须在堆栈中找到一些巨大的对象。不要那样做。

  

你的意思是“内核不会将超出当前的堆栈扩展到超过”

当您通过例如扩展堆栈(减量%rsp)时1MB,然后尝试触摸该堆栈位置,内存将无法访问(内核按需增长堆栈)。这将生成硬件陷阱,并将控制权转移到内核。当内核决定做什么时,它会查看

  1. 当前%rsp
  2. 应用程序尝试访问的内存位置
  3. 当前线程的堆栈限制
  4. 如果错误地址低于当前%rsp,但在128K(或其他类似幅度的常量)内,则内核只是扩展堆栈(假设此类扩展不会超出堆栈限制)。

    如果错误地址低于当前%rsp以下128K(如此处所示),则会得到SIGSEGV

    这对大多数程序都很有效:即使它们在递归过程中使用了大量堆栈,它们通常也会以小块扩展堆栈。但是,试图在单个例程中保留所有堆栈的等效程序会崩溃。

    无论如何,在崩溃点做(gdb) info locals,看看当地人可能需要10MB的堆栈。然后将它们移动到堆中。

    更新2:

      

    没有当地人

    啊,该程序可能没有足够的进入eesu3本地人。

      

    使用-O0构建时,错误消失。 GCC错误?

    这可能是一个GCC错误,但更可能只是GCC将许多其他例程内联到eesu3,并且每个内联例程都需要自己的N KB堆栈。如果您使用eesu3生成包含-fno-inline的来源,问题是否会消失?

    不幸的是,对此类行为进行分类并找出适当的解决方法或修复GCC需要编译器专业知识。您可以先使用-fdump-tree-all进行编译,然后查看生成的<source>.*t.*文件。这些包含在编译过程的各个阶段的GCC内部表示的文本转储。您可能能够充分了解它以取得进一步进展。

答案 1 :(得分:5)

这是一个堆栈溢出。

eesu3尝试在堆栈上分配一些非常大的东西,可以在汇编代码中看到:

sub    $0x99b000,%rsp

这意味着消耗了超过10MB的堆栈空间。

问题可能在eesu3或它调用的函数中,编译器选择内联。

我的猜测是问题出在函数eesu3调用中,但是在你测试的情况下不是(调试函数?)
我想这是因为没有优化就不会发生这种情况 - 通过优化,函数被内联到eesu3中,所以eesu3使用了大量的堆栈。没有它,该函数不是内联函数,因此只有在实际调用它时才会出现问题。

答案 2 :(得分:0)

如果是Matrix,请检查您尝试访问的索引。也许您正在访问超出Matrix对象维度的元素?

答案 3 :(得分:0)

您可能在函数

中初始化了一些变量
void eesu3(Matrix & iQ)

尽管调试器可能会逐步执行变量声明,但它们可能是在作用域的开头(即您的函数)初始化的。如果你像这样声明一个非常大的缓冲区:

char * buffer[268435456];

你可能会出现堆栈溢出。分配一些像

这样的内存可能会更好
void * pvBuffer = malloc(268435456);

你宣布了一个大缓冲区吗?哪个太大而无法叠加?这可能意味着不同的体系结构会导致缓冲区(64位和32位操作系统)的最大可能大小不同?不同的内核?正如你所说,程序在一台机器上运行正常但在另一台机器上运行不正确。