2013-04-06 81 views
0

我有一個分段錯誤,對我沒有任何意義。 第538次調用這個函數時,它失敗了,但是我看不到任何參數的錯誤 。我可以輸入相同的表達式gdb ,它會很樂意做到這一點沒有抱怨。我甚至利用 si匆匆看過它,看着寄存器,但它仍然沒有任何意義。 我該如何着手解決這個問題?沒有明顯原因的分段錯誤

一個奇怪的是,gdb報告「bit」參數 在故障時從0更改爲1。 我想「位」是在一個註冊表中被重用的東西。

我也試過這個沒有優化。涉及更多指令 ,但結果相同。

請注意,我使用gdb訪問代碼即將訪問的相同位置,但沒有任何問題。

代碼:

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

GDB運行:

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;在「壞」聲明之前,並且導致錯誤。 我試過_dat [0] ++;那也是錯誤的。

+0

所以你說執行ARM彙編指令'strh r2,[r12,r3]'會導致訪問衝突。問:是0x4ee74(包含在r3中)有效嗎? – paulsm4 2013-04-06 17:55:19

+0

0x4ee74有效。請參閱gdb響應$ 19。 – rich 2013-04-06 17:59:46

+0

'_size'的價值是什麼?我沒有看到任何明顯的錯誤。你在使用glibc嗎?可能值得導出'MALLOC_CHECK_ = 3'來讓glibc檢查malloc相關的問題。 – jszakmeister 2013-04-06 18:46:13

回答

-1

1)所以你說的是執行ARM彙編指令strh r2,[r12,r3]導致訪問衝突。

2)問:是0x4ee74(包含在r3中)有效嗎?

3)Q:是0000f139(包含在r2中)有效嗎?從手冊:

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

地址對齊的字和半字傳輸:

地址必須是4

的倍數。如果你的系統有一個系統協處理器( cp15),您可以啓用 對齊檢查。如果啓用了對齊檢查,則非對齊傳輸會導致對齊異常 。

如果您的系統沒有系統協處理器(cp15),或對準 檢查功能:

非對齊負載破壞路。

未對齊的保存會破壞內存中的四個字節。內存中損壞的地址是[address AND NOT b11]。

+0

由於前兩個指令的加載不會失敗,因此地址似乎有效,但內存被寫保護。 – 2013-04-06 18:04:02

+1

地址是4的倍數.r2的內容應該是不在乎的。我們只是存儲它。 – rich 2013-04-06 18:04:55

+1

如果內存被寫保護,gdb是否不會寫入它的麻煩?以及它如何突然得到寫保護?請注意,它是非零的,這意味着這個相同的函數已經在前面的537次調用中的一部分中訪問過這個位置。 – rich 2013-04-06 18:07:10

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

我不明白爲什麼在這個特定的行有一個分段錯誤,因爲使用gdb修改內存似乎沒有問題。但是,通過在Bits構造函數中打印出「this」,並將其與「this」相比較,我發現我必須遭受內存破壞,可能遠離這裏。因此,我打算脫離這一調查路線,繼續假設問題在別處。

(後下)

我發現這個問題,這是一個未初始化的指針(驚喜!)到正好是足夠可信的,它表面上看起來好像是正確的位。指向正確方向的思想是「記錄事物」的想法,我剛剛用printf做了這個。

+0

也許'valgrind'會幫助你找到... – 2013-04-07 07:19:17

+1

而且還用'-Wall -Wextra'編譯(使用最近的GCC編譯器,例如4.8)可能也有幫助。 – 2013-04-07 07:38:51