rit*_*ter 10 c++ linux gdb segmentation-fault
只是输入一个函数会导致分段错误的原因是什么?
输入的功能如下:
21: void eesu3(Matrix & iQ)
22: {
Run Code Online (Sandbox Code Playgroud)
这里Matrix是一个struct.使用GDB运行时,回溯会产生:
(gdb) backtrace
#0 eesu3 (iQ=...) at /home/.../eesu3.cc:22
#1 ...
Run Code Online (Sandbox Code Playgroud)
GDB没有透露具体是什么iQ.在...字面上有.什么可能导致这个?
海湾合作委员会:(Ubuntu/Linaro 4.6.3-1ubuntu5)4.6.3
程序内置 -O3 -g
来电者喜欢:
Matrix q;
// do some stuff with q
eesu3(q);
Run Code Online (Sandbox Code Playgroud)
这里没什么特别的
我用valgrind重新编写程序:
valgrind --tool=memcheck --leak-check=yes --show-reachable=yes --num-callers=20 --track-fds=yes <prgname>
Run Code Online (Sandbox Code Playgroud)
输出:
==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.
Run Code Online (Sandbox Code Playgroud)
看起来像是一个损坏的堆栈.
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)
Run Code Online (Sandbox Code Playgroud)
好的,要说清楚:Matrix的数据存在于堆上.它基本上包含一个指向数据的指针.结构很小,32个字节.(刚检查过)
现在,我使用不同的优化选项重建了程序:
-O0:错误没有显示.
-O1:错误确实显示.
-O3:错误确实显示.
--update
-O3 -fno-inline -fno-inline-functions:错误没有显示.
这解释了它.函数内联太多会导致堆栈使用过多.
问题是由于堆栈溢出
Emp*_*ian 15
只是输入一个函数会导致分段错误的原因是什么?
最常见的原因是堆栈耗尽.不要(gdb) disas在崩溃点.如果崩溃的指令是在%rsp递减之后第一次读取或写入堆栈位置,那么堆栈耗尽几乎肯定是原因.
解决方案通常涉及创建具有较大堆栈的线程,将一些大型变量从堆栈移动到堆堆,或两者兼而有之.
另一个可能的原因:如果Matrix包含非常大的数组,则无法将其放在堆栈上:内核不会将超出当前的堆栈扩展超过128K(或者说,我不记得确切的值).如果Matrix大于该限制,则无法将其置于堆栈中.
更新:
0x00000000014c7654 <+20>: sub $0x99b000,%rsp
=> 0x00000000014c765b <+27>: mov %rdi,0xfd8(%rsp)
Run Code Online (Sandbox Code Playgroud)
这种拆卸证实了诊断.
此外,您在堆栈上保留0x99b000字节(这几乎是10MB).在eesu3例行程序中,您必须尝试在堆栈中找到一些巨大的对象.不要那样做.
你的意思是"内核不会超过当前的堆栈超过"
当您将堆栈(递减%rsp)延长例如1MB,然后尝试触摸该堆栈位置时,内存将无法访问(内核按需增长堆栈).这将生成硬件陷阱,并将控制权转移到内核.当内核决定做什么时,它会查看
%rsp如果故障地址低于当前值%rsp,但在128K(或其他类似幅度的常量)内,则内核只是扩展堆栈(假设此类扩展不会超过堆栈限制).
如果错误地址低于当前的128K %rsp(这里似乎是这种情况),你得到SIGSEGV.
对于大多数程序来说,这一切都很好用:即使它们在递归过程中使用了大量堆栈,它们通常也会以小块的形式扩展堆栈.但是,试图在单个例程中保留所有堆栈的等效程序将会崩溃.
无论如何,(gdb) info locals在崩溃点做,看看当地人可能需要10MB的堆栈.然后将它们移动到堆.
更新2:
没有当地人
啊,该计划可能还没有eesu3达到当地人的水平.
当使用-O0构建时,错误消失.GCC错误?
它可能是一个GCC错误,但更可能只是GCC正在将许多其他例程内联到其中eesu3,并且每个内联例程都需要自己的N KB堆栈.请问这个问题,如果你建立一个包含源消失eesu3用-fno-inline?
不幸的是,这种行为的分类和找出适当的解决方法,或修复GCC,需要编译器专业知识.您可以从编译-fdump-tree-all和查看生成的<source>.*t.*文件开始.这些包含在编译过程的各个阶段的GCC内部表示的文本转储.您可能能够充分了解它以取得进一步进展.
这是堆栈溢出.
eesu3 尝试在堆栈上分配非常大的东西,可以在其汇编代码中看到:
sub $0x99b000,%rsp
Run Code Online (Sandbox Code Playgroud)
这意味着消耗了超过10MB的堆栈空间.
问题可能在于eesu3它调用的函数或函数,编译器选择内联.
我的猜测是问题在于函数eesu3调用,但是在你测试的情况下不是(一个调试函数?)
我想这是因为没有优化就不会发生 - 通过优化,函数被内联到eesu3,所以eesu3使用了很多堆.没有它,该函数不是内联函数,所以只有在实际调用时才会出现问题.
| 归档时间: |
|
| 查看次数: |
3859 次 |
| 最近记录: |