分段错误没有明显的原因

时间:2013-04-06 17:48:51

标签: linux gcc gdb arm

我有一个对我没有任何意义的分段错误。 第538次调用此函数时,它失败了,但我什么也看不见 参数错误。我可以在gdb中输入相同的表达式 并且很乐意在没有投诉的情况下这样做。我甚至悄悄地使用它 si看了看寄存器,但它仍然没有任何意义。 我该如何解决这个问题呢?

奇怪的是,gdb报告了" bit"争论有 在故障时从0变为1。 我想" bit"在一个寄存器中被重用于其他东西。

我也没有优化就试过这个。有更多的指示 涉及,但结果是一样的。

请注意,我使用gdb来访问与之相同的位置 代码即将访问,没有任何问题。

代码:

void Bits::set(int bit)
{
    if (bit >= _size*16) BUG;
    _dat[bit/16] |= 1 << (bit & 15);
}

gdb run:

Breakpoint 1, Bits::set (this=0x68374, bit=0) at util.cpp:181
181         _dat[bit/16] |= 1 << (bit & 15);
1: x/i $pc
=> 0x3964c <_ZN4Bits3setEi+36>: ldr     r3, [r0, #4]
(gdb) si
0x00039650      181         _dat[bit/16] |= 1 << (bit & 15);
1: x/i $pc
=> 0x39650 <_ZN4Bits3setEi+40>: mov     r1, #1
(gdb)
0x00039654      181         _dat[bit/16] |= 1 << (bit & 15);
1: x/i $pc
=> 0x39654 <_ZN4Bits3setEi+44>: ldrh    r2, [r12, r3]
(gdb)
0x00039658      181         _dat[bit/16] |= 1 << (bit & 15);
1: x/i $pc
=> 0x39658 <_ZN4Bits3setEi+48>: orr     r2, r2, r1, lsl lr
(gdb)
0x0003965c      181         _dat[bit/16] |= 1 << (bit & 15);
1: x/i $pc
=> 0x3965c <_ZN4Bits3setEi+52>: strh    r2, [r12, r3]
(gdb) p _dat
$18 = (short *) 0x4ee74
(gdb) p *_dat
$19 = -3784
(gdb) p $r2
$20 = 61753
(gdb) p $r12
$21 = 0
(gdb) p/x $r3
$22 = 0x4ee74
(gdb) si

Program received signal SIGSEGV, Segmentation fault.
0x0003965c in Bits::set (this=0x68374, bit=1) at util.cpp:181
181         _dat[bit/16] |= 1 << (bit & 15);
1: x/i $pc
=> 0x3965c <_ZN4Bits3setEi+52>: strh    r2, [r12, r3]
(gdb)

顺便说一下,我在这里使用gdbserver。这是目标响应:

50:/mnt/home/rw # ./gdbserver x:12 cx Andersen_Studio.cxc
Process cx created; pid = 226
Listening on port 12
Remote debugging from host 192.168.1.40

pc : [<0003965c>]    lr : [<00000000>]    Tainted: P
sp : 7ffffdb4  ip : 00000000  fp : 7ffffe84
r10: 2ada7884  r9 : 0000c6c8  r8 : 2ada8d28
r7 : 00000002  r6 : 0005c0b8  r5 : 00000000  r4 : 0006dc10
r3 : 0004ee74  r2 : 0000f139  r1 : 00000001  r0 : 00068374
Flags: Nzcv  IRQs on  FIQs on  Mode USER_32  Segment user
Control: C000317F  Table: 017EC000  DAC: 00000015

更多信息:如果我手动执行命令,则不会发生故障:

Breakpoint 1, Bits::set (this=0x6c75c, bit=0) at util.cpp:181
181         _dat[bit/16] |= 1 << (bit & 15);
(gdb) c 538
Will ignore next 537 crossings of breakpoint 1.  Continuing.

Breakpoint 1, Bits::set (this=0x68374, bit=0) at util.cpp:181
181         _dat[bit/16] |= 1 << (bit & 15);
(gdb) p _dat[bit/16] |= 1 << (bit & 15)
$25 = -3783
(gdb) dis 1
(gdb) c
Continuing.

只是引用_dat [0]没有帮助,但是例如输入_dat [0] = 0可以防止出现问题。

(稍后编辑)

如果我们要查看为该功能生成的代码,我决定关闭优化。这是未经优化的代码:

(gdb) disass
Dump of assembler code for function _ZN4Bits3setEi:
   0x0006a1d8 <+0>:     mov     r12, sp
   0x0006a1dc <+4>:     push    {r11, r12, lr, pc}
   0x0006a1e0 <+8>:     sub     r11, r12, #4
   0x0006a1e4 <+12>:    sub     sp, sp, #8
   0x0006a1e8 <+16>:    str     r0, [r11, #-16]
   0x0006a1ec <+20>:    str     r1, [r11, #-20]
=> 0x0006a1f0 <+24>:    ldr     r3, [r11, #-16]
   0x0006a1f4 <+28>:    ldr     r3, [r3]
   0x0006a1f8 <+32>:    lsl     r2, r3, #4
   0x0006a1fc <+36>:    ldr     r3, [r11, #-20]
   0x0006a200 <+40>:    cmp     r3, r2
   0x0006a204 <+44>:    blt     0x6a220 <_ZN4Bits3setEi+72>
   0x0006a208 <+48>:    ldr     r0, [pc, #108]  ; 0x6a27c <_ZN4Bits3setEi+164>
   0x0006a20c <+52>:    ldr     r1, [pc, #108]  ; 0x6a280 <_ZN4Bits3setEi+168>
   0x0006a210 <+56>:    mov     r2, #180        ; 0xb4
   0x0006a214 <+60>:    bl      0xa00c <printf>
   0x0006a218 <+64>:    mov     r0, #1
   0x0006a21c <+68>:    bl      0xa09c <exit>
   0x0006a220 <+72>:    ldr     r1, [r11, #-16]
   0x0006a224 <+76>:    ldr     r2, [r11, #-20]
   0x0006a228 <+80>:    asr     r3, r2, #31
   0x0006a22c <+84>:    lsr     r3, r3, #28
   0x0006a230 <+88>:    add     r3, r2, r3
   0x0006a234 <+92>:    asr     r0, r3, #4
   0x0006a238 <+96>:    mov     r3, r0
   0x0006a23c <+100>:   lsl     r2, r3, #1
   0x0006a240 <+104>:   ldr     r3, [r1, #4]
   0x0006a244 <+108>:   add     r12, r2, r3
   0x0006a248 <+112>:   ldr     r1, [r11, #-16]
   0x0006a24c <+116>:   mov     r3, r0
   0x0006a250 <+120>:   lsl     r2, r3, #1
   0x0006a254 <+124>:   ldr     r3, [r1, #4]
   0x0006a258 <+128>:   add     r1, r2, r3
   0x0006a25c <+132>:   ldr     r3, [r11, #-20]
   0x0006a260 <+136>:   and     r2, r3, #15
   0x0006a264 <+140>:   mov     r3, #1
   0x0006a268 <+144>:   lsl     r3, r3, r2
   0x0006a26c <+148>:   ldrh    r2, [r1]
   0x0006a270 <+152>:   orr     r3, r2, r3
   0x0006a274 <+156>:   strh    r3, [r12]
   0x0006a278 <+160>:   ldmdb   r11, {r11, sp, pc}
   0x0006a27c <+164>:   andeq   r8, r8, r0, lsr r12
   0x0006a280 <+168>:   andeq   r8, r8, r4, asr r12
End of assembler dump.
(gdb)

我尝试插入_dat [0] = 0;在&#34;坏&#34;之前声明,这造成了错误。 我试过_dat [0] ++;这也是出了问题。

3 个答案:

答案 0 :(得分:3)

嗯,一切看起来还不错。你可以从这个位置读取,但你不能写......奇怪。

我想到了以下想法:

  • 永远不要相信gdb会向您展示什么。在gdb中计算表达式时,这与您的代码完全不同。特别是它不会使用您的代码(除非您明确地通过名称调用函数)。 Gdb将使用调试信息,这些信息通常存在差距并且有很大的解释空间,而您的代码只使用您的代码。 :-) gdb中的变量值通常是错误的。这很可能甚至不是gdbs故障。 Gdb不能比编译器生成的调试信息更好。它甚至包含许多(调试信息相关的)编译器错误的启发式方法。

  • 您的操作系统可能会将许多不同的故障情况映射到Segfault。例如,这可以是异步数据中止,在这种情况下,真正的问题可能是之前的数百条指令。

  • 您使用的是安全且非安全的内存吗?通过缓存从非安全模式写入安全内存可能会导致异步数据中止。

  • 这个功能运行537次的事实并不重要。它是否在完全相同的数据/内存位置上运行?我的意思是,set()函数本质上只是一个内存写入。行为(崩溃与否)将始终取决于变量的内容 bit

当我在我的程序中看到对我没有任何意义的奇怪崩溃时,我修改了该功能,因此它的行为略有不同。如果你这样做会发生什么:

void Bits::set(int bit)
{
    if (bit >= _size*16) BUG;
    _dat[bit/16] = 42; // does this crash?
    _dat[0] = 42;      // does this crash?
    _dat[bit/16]++;    // does this crash?
    _dat[bit/16] |= 1 << (bit & 15);
}

(如果您的程序依赖于此,您可能需要保存/恢复所写的位置。)

将程序中的诊断消息打印到某个日志中也有帮助。这通常比使用调试器更好,因为它使用您的代码和实际值而不是使用调试信息。

答案 1 :(得分:1)

我无法弄清楚为什么在这条特定的线路上存在分段错误,因为使用gdb修改内存似乎没有问题。但是,通过在Bits构造函数中打印出“this”,并将其与故障中的“this”进行比较,我发现我必须遭受内存损坏,可能在某处远离此处。因此,我将脱离这一调查范围并继续假设问题出在其他地方。

(后)

我发现了这个问题,这是一个未经初始化的指针(惊讶!),它恰好足够可信,看起来表面看起来好像是正确的。向我指出正确方向的想法是“记录事物”的想法,我刚用printf做过。

答案 2 :(得分:-1)

1)所以你说执行ARM汇编指令strh r2,[r12,r3]会导致访问冲突。

2)问:0x4ee74(包含在r3中)是否有效?

3)问:0000f139(包含在r2中)有效吗?从手册:

  

http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0068b/BABDJCHA.html

     

字和半字转移的地址对齐方式:

     

地址必须是4的倍数。

     

如果您的系统有系统协处理器(cp15),则可以启用   对齐检查。未对齐的传输会导致对齐异常   如果启用了对齐检查。

     

如果您的系统没有系统协处理器(cp15)或对齐   检查被禁用:

     

未对齐的加载会破坏Rd。

     

非对齐保存会破坏内存中的四个字节。内存中损坏的位置是[地址而不是b11]。