gdb除錯core dump檔案之二

绍荣發表於2024-07-29

1 目錄

2 corrupt stack?

3 info registers

4 (gdb)x的使用

5 X86上崩潰

6 除錯執行緒

1 目錄

1.1 目錄指定dir

在GDB中使用dir命令來新增新的原始碼路徑。這個命令允許我指定一個或多個目錄,GDB會在這些目錄中查詢原始碼檔案。

gdb ./your_executable # 啟動可執行檔案
(gdb) dir /path/to/your/source/code # 使用dir命令新增原始碼路徑
(gdb) show directories # 驗證原始碼路徑已被新增:

1.2 目錄替換set substitute-path

如果知道除錯資訊中的舊路徑和新路徑,就可以使用set substitute-path命令將舊路徑替換為新路徑。
info

  • 啟動GDB並載入你的可執行檔案

    • gdb ./your_executable
  • 使用set substitute-path命令替換路徑:

    • (gdb) set substitute-path /old/source/path /new/source/path
  • 列出所有已知的原始檔,驗證它們是否正確指向了新的路徑。

    • (gdb) info sources

1.3 示例

假設我的除錯資訊中包含的舊路徑是/home/user/old_project/src,而你希望替換為新的路徑/home/user/new_project/src,可以按如下步驟操作:

  • 啟動GDB:
    • gdb ./your_executable
  • 設定路徑替換:
    • (gdb) set substitute-path /home/user/old_project/src /home/user/new_project/src
  • 確認路徑已替換:
    • (gdb) info sources

檢視原始檔路徑是否指向了新的路徑。

透過這些方法,就可以在GDB除錯時正確地載入原始碼,即使除錯資訊中的路徑不正確。

2 Backtrace stopped: previous frame identical to this frame (corrupt stack?)

在GDB除錯時,如果遇到錯誤資訊 Backtrace stopped: previous frame identical to this frame (corrupt stack?),通常表明程式的呼叫棧已損壞。這種情況可能由多種原因引起,例如:

  • 堆疊溢位:函式遞迴太深或區域性變數過多,導致超出棧的限制
  • 堆疊損壞:由於緩衝區溢位等問題,導致堆疊上的返回地址或幀指標被意外覆蓋
  • 錯誤的編譯最佳化:某些編譯器最佳化可能會導致除錯資訊不準確。
  • 未初始化或非法指標:使用未初始化的指標或非法訪問記憶體。

2.1 解決方法

  1. 檢查程式碼中的緩衝區溢位

    緩衝區溢位是堆疊損壞的常見原因。確保所有陣列和緩衝區的訪問都在其邊界內。

     #include <stdio.h>
     #include <string.h>
    
     void example_function(char *input) {
         char buffer[10];
         // Potential buffer overflow
         strcpy(buffer, input);
     }
    
     int main() {
         char input[] = "This string is too long and will overflow the buffer";
         example_function(input);
         return 0;
     }
    

    在這個例子中,如果 input 超過 buffer 的大小,將導致緩衝區溢位,可能破壞堆疊。

  • 參考棧大小命令:ulimit -s,一般為8192KB,即8M
  1. 使用除錯工具

    使用工具如 valgrind 或 AddressSanitizer 可以幫助檢測記憶體錯誤。

  • Valgrind:

    valgrind --tool=memcheck ./your_program

  • AddressSanitizer:

    在編譯時新增 -fsanitize=address 選項:

    gcc -fsanitize=address -g -o your_program your_program.c
    ./your_program

  1. 調整編譯最佳化

    編譯時使用較低的最佳化級別,可能會改善除錯資訊的準確性。

     gcc -O0 -g -o your_program your_program.c
    
  2. 檢查遞迴和棧大小

如果函式遞迴深度過大區域性變數佔用太多棧空間,可能導致棧溢位。確保遞迴基準情況和合理的棧空間分配。

void recursive_function(int count) {
    if (count > 0) {
        recursive_function(count - 1);
    }
}

2.2 分析堆疊損壞

如果依然出現堆疊損壞問題,可以使用GDB的其他功能進一步分析:

  • 檢視暫存器和堆疊:

    (gdb) info registers
    (gdb) x/32x $sp

  • 檢查幀指標:

    (gdb) info frame
    (gdb) info locals

使用記憶體除錯工具

valgrind 和 AddressSanitizer 是強大的記憶體除錯工具,可以檢測出記憶體管理問題和未定義行為。

  • Valgrind:

    valgrind --tool=memcheck --leak-check=full ./your_program

  • AddressSanitizer:

編譯時新增 -fsanitize=address 選項:

gcc -fsanitize=address -O0 -g -o your_program your_program.c
./your_program

2.3 結論

堆疊損壞問題通常與記憶體管理錯誤或遞迴深度有關。透過檢查程式碼中的緩衝區溢位,使用記憶體除錯工具,調整編譯最佳化級別,以及檢查遞迴和棧大小,可以有效解決此類問題。使用GDB檢視暫存器和堆疊,以及檢查幀指標和區域性變數,可以幫助進一步分析和診斷問題。

3 info registers

在GDB中,使用info registers命令,可以檢視程式當前狀態下的所有暫存器的值。

暫存器是CPU中的小型儲存單元,用於存放資料地址指令等資訊。info registers命令在除錯時非常有用,可以幫助你進行以下操作:

  1. 檢查程式狀態
  • 檢視暫存器內容:暫存器儲存了CPU在執行當前指令時的各種資訊,例如
    • 程式計數器(PC):
    • 堆疊指標(SP):
    • 基址暫存器(BP):

透過檢視這些暫存器的值,可以瞭解程式的執行狀態,定位程式的執行位置。

(gdb) info registers

這將顯示所有暫存器的當前值,例如:

eax            0x0      0
ebx            0x1      1
ecx            0x2      2
edx            0x3      3
eip            0x401000 0x401000
esp            0xfffdf000 0xfffdf000
ebp            0xfffdf020 0xfffdf020
  1. 除錯程式崩潰
  • 定位崩潰點:當程式崩潰時,檢視暫存器可以幫助確定崩潰發生的位置。程式計數器(EIP或PC)的值通常顯示了崩潰發生的指令地址。

    • PC(Program Counter): 在ARM下的當前崩潰指令所在處,
    • EIP(Extended Instruction Pointer): EIP暫存器是x86架構處理器中的一個重要暫存器,‌用於儲存下一條指令的記憶體地址。‌即當前執行的指令存放的位置
  • 分析呼叫棧:暫存器中的堆疊指標(SP)基址暫存器(BP)的值可以幫助你檢視函式呼叫棧,瞭解程式的呼叫路徑和函式的區域性變數。

  1. 理解程式的執行流程
  • 檢視暫存器變化:在程式執行過程中,暫存器的值會不斷變化。透過檢視暫存器的值,可以跟蹤程式的執行路徑,理解不同部分程式碼如何影響程式的狀態。
  1. 除錯複雜問題
  • 除錯最佳化程式碼:最佳化後的程式碼可能會對暫存器進行復雜的操作。檢視暫存器可以幫助我理解編譯器如何最佳化你的程式碼,從而幫助解決可能出現的複雜問題。
  1. 設定斷點和觀察點
  • 設定條件斷點:可以根據暫存器的值設定條件斷點,例如在特定暫存器值下停止程式執行。

      (gdb) break some_function if eax == 0x1
    
  • 觀察暫存器變化:可以使用watch命令監視暫存器的值,檢視其在程式執行過程中的變化。

      (gdb) watch eax
    

示例,以下是一個在GDB中檢視暫存器的示例:

gdb ./your_program
#設定斷點並執行程式:
(gdb) break main
(gdb) run

檢視暫存器:

(gdb) info registers

輸出可能包括:

eax            0x0      0
ebx            0x0      0
ecx            0x0      0
edx            0x0      0
eip            0x400123 0x400123 <main+3>
esp            0xfffdf000 0xfffdf000
ebp            0xfffdf020 0xfffdf020

在這個示例中,eip暫存器的值(0x400123)表示程式計數器當前指向的地址,這有助於定位程式執行到的位置。

總結

info registers命令提供了對暫存器狀態的全面檢視,在除錯過程中非常重要。透過分析暫存器的內容,可以幫助理解程式的執行狀態、定位崩潰點、跟蹤程式路徑,並解決複雜的除錯問題。

3.1 示例分析1141

在GDB除錯中,檢視暫存器(info registers)有助於瞭解程式的執行狀態。在提供的輸出中,可以看到ARM架構下各個暫存器的當前值。以下是對這些暫存器的一些解釋:

3.1.1 CPU暫存器內容

(gdb) info registers

r0             0xb3c872a8          3016258216
r1             0xffff7e7c          4294934140
r2             0x4c378ce0          1278708960
r3             0x4c370bdc          1278675932
r4             0xb2d00588          2999977352
r5             0xb24fcb18          2991573784
r6             0xf7b2e7b0          4155697072
r7             0xffff7e7c          4294934140
r8             0xbe3c426           199476262
r9             0xbeec7441          3203167297
r10            0xebc8948f          3955790991
r11            0x3fef8e1c          1072664092
r12            0x0                 0
sp             0xb24fc9fc          0xb24fc9fc
lr             0xb5c19735          -1245604043
pc             0xb341f2b8          0xb341f2b8 <memcpy+72>
cpsr           0x200f0010          537853968
fpscr          0x8000001b          -2147483621

3.1.2 ARM暫存器說明

通用暫存器(R0-R12),共13個:

  • r0-r3:通常用於函式引數傳遞和返回值。這裡共4個暫存器
  • r4-r11:通用暫存器,函式呼叫中使用的臨時暫存器。這裡共8個暫存器
  • r12(IP): 一般用於過程間呼叫時的臨時暫存器。最後一個通用暫存器

特殊暫存器:

  • sp(Stack Pointer):棧指標,指向當前棧頂地址。
  • lr(Link Register):連結暫存器,儲存函式呼叫的返回地址。
  • pc(Program Counter):程式計數器,儲存下一條將要執行的指令地址。
  • cpsr(Current Program Status Register):當前程式狀態暫存器,包含條件標誌狀態資訊
  • fpscr(Floating Point Status and Control Register):浮點狀態和控制暫存器。

3.1.3 暫存器值分析

r0             0xb3c872a8          3016258216
r1             0xffff7e7c          4294934140
r2             0x4c378ce0          1278708960
r3             0x4c370bdc          1278675932
r4             0xb2d00588          2999977352
r5             0xb24fcb18          2991573784
r6             0xf7b2e7b0          4155697072
r7             0xffff7e7c          4294934140
r8             0xbe3c426           199476262
r9             0xbeec7441          3203167297
r10            0xebc8948f          3955790991
r11            0x3fef8e1c          1072664092
r12            0x0                 0
sp             0xb24fc9fc          0xb24fc9fc
lr             0xb5c19735          -1245604043
pc             0xb341f2b8          0xb341f2b8 <memcpy+72>
cpsr           0x200f0010          537853968
fpscr          0x8000001b          -2147483621

特別關注的暫存器

  • 程式計數器(PC):

      pc 0xb341f2b8 0xb341f2b8 <memcpy+72>
    

    程式計數器當前指向memcpy函式的偏移量為72的指令。

  • 連結暫存器(LR):

      lr 0xb5c19735 -1245604043
    

    連結暫存器儲存了呼叫函式的返回地址,通常用於返回到呼叫函式。

  • 棧指標(SP):

      sp 0xb24fc9fc 0xb24fc9fc
    

    棧指標,當前指向棧頂。

  • 當前程式狀態暫存器(CPSR):

      cpsr 0x200f0010 537853968
    

    包含條件標誌(例如零、進位、負等)和其他狀態資訊。

  • 浮點狀態和控制暫存器(FPSCR):

      fpscr 0x8000001b -2147483621
    

    浮點操作的狀態和控制資訊。

3.1.4 使用暫存器資訊進行除錯

檢視暫存器的值可以幫助我

  • 分析崩潰位置:pc指向的指令地址(例如,在memcpy中)可能是導致崩潰的原因。
  • 檢查函式呼叫鏈:透過lr,可以找到呼叫鏈的返回地址,進一步分析呼叫路徑。
  • 瞭解棧的狀態:sp指向當前棧頂,可以檢視棧上的資料呼叫幀
  • 分析程式狀態:cpsr中的標誌位可以幫助你理解程式在執行特定指令時的狀態,例如是否發生了溢位零結果等。

示例:除錯崩潰

假設我的程式在呼叫memcpy時崩潰,就可以使用以下步驟進行除錯:

檢視暫存器:

(gdb) info registers

#檢查PC暫存器:
(gdb) x/i $pc       # 這將顯示pc指向的指令。

#檢查棧內容:
(gdb) x/16x $sp     # 這將顯示當前棧頂的內容,有助於理解函式呼叫鏈和區域性變數。

#檢查返回地址:
(gdb) x/i $lr       # 這將顯示lr指向的返回地址指令。

透過這些步驟,可以更深入地分析和除錯程式崩潰或異常行為。

3.1.5 使用反彙編檢視崩潰處memcpy+72

(gdb) disassemble memcpy
Dump of assembler code for function memcpy:
0xb341f270 <+0>:	push	{r0, r4, lr}
0xb341f274 <+4>:	subs	r2, r2, #4
0xb341f278 <+8>:	blt	0xb341f32c <memcpy+188>
0xb341f27c <+12>:	ands	r12, r0, #3
0xb341f280 <+16>:	pld	[r1]
0xb341f284 <+20>:	bne	0xb341f34c <memcpy+220>
0xb341f288 <+24>:	ands	r12, r1, #3
0xb341f28c <+28>:	bne	0xb341f37c <memcpy+268>
0xb341f290 <+32>:	subs	r2, r2, #28
0xb341f294 <+36>:	push	{r5, r6, r7, r8}
0xb341f298 <+40>:	blt	0xb341f2d0 <memcpy+96>
0xb341f29c <+44>:	pld	[r1]
0xb341f2a0 <+48>:	subs	r2, r2, #96	; 0x60
0xb341f2a4 <+52>:	pld	[r1, #28]
0xb341f2a8 <+56>:	blt	0xb341f2b8 <memcpy+72>
0xb341f2ac <+60>:	pld	[r1, #60]	; 0x3c
0xb341f2b0 <+64>:	pld	[r1, #92]	; 0x5c
0xb341f2b4 <+68>:	pld	[r1, #124]	; 0x7c
=> 0xb341f2b8 <+72>:	ldm	r1!, {r3, r4, r5, r6, r7, r8, r12, lr}
0xb341f2bc <+76>:	subs	r2, r2, #32
0xb341f2c0 <+80>:	stmia	r0!, {r3, r4, r5, r6, r7, r8, r12, lr}

在這個這個例子中,<+72> 處的指令是ldm r1!, {r3, r4, r5, r6, r7, r8, r12, lr}。透過檢視這條指令,可以更好地理解程式在執行時的狀態和可能出現的問題。

<memcpy+72> 提供了除錯資訊,表示當前執行的指令在 memcpy 函式內的偏移量為 72 位元組。透過反彙編 memcpy 函式並定位到偏移量 72 處的指令,就可以更好地理解程式的執行狀態,有助於除錯和分析問題。

4 (gdb)x的使用

在GDB中,x命令用於檢查記憶體的內容。這個命令非常靈活,可以根據指定的格式和數量來檢視記憶體。以下是x命令的詳細用法和常見示例:

4.1 基本語法

x/[數量][格式][大小] 地址

  • 數量(count):要顯示的單元數量。

  • 格式(format):顯示記憶體內容的格式。

  • 大小(size):每個單元的大小。

  • 地址(address):要檢視的記憶體地址。

  • 格式說明

  • x:十六進位制

  • d:十進位制

  • u:無符號十進位制

  • o:八進位制

  • t:二進位制

  • f:浮點數

  • a:地址

  • i:指令

  • c:字元

  • s:字串

  • 大小說明

  • b:位元組(8位)

  • h:半字(16位)

  • w:字(32位)

  • g:巨字(64位)

4.2 示例

  1. 檢視記憶體中的整數

    假設想檢視地址0x601000處的記憶體內容,並以十六進位制格式顯示4個字(32位)的值:

     x/4xw 0x601000
    

    輸出示例:

     0x601000: 0x00000001 0x00000002 0x00000003 0x00000004
    
  2. 檢視記憶體中的字元

    假設你想檢視地址0x601000處的記憶體內容,並以字元格式顯示16個位元組:

     x/16cb 0x601000
    

    輸出示例:

     0x601000:  72 'H' 101 'e' 108 'l' 108 'l' 111 'o' 32 ' ' 119 'w' 111 'o'
     0x601008:  114 'r' 108 'l' 100 'd' 33 '!' 0 '\000' 0 '\000' 0 '\000' 0 '\000'
    
  3. 檢視記憶體中的字串

    假設想檢視地址0x601000處的記憶體內容,並以字串格式顯示:

     x/s 0x601000
    

    輸出示例:

     0x601000: "Hello, world!"
    
  4. 檢視記憶體中的浮點數

    假設想檢視地址0x601000處的記憶體內容,並以浮點數格式顯示4個單元:

     x/4fw 0x601000
    

    輸出示例:

     0x601000: 1.0 2.0 3.0 4.0
    
  5. 檢視記憶體中的指令

    假設想檢視地址0x400000處的記憶體內容,並以指令格式顯示10條指令:

     x/10i 0x400000
    

    輸出示例:

     0x400000:  mov    eax,0x1
     0x400005:  mov    ebx,0x2
     0x40000a:  add    eax,ebx
     0x40000c:  ret
     ...
    
  6. 檢視當前棧頂內容

    假設想檢視當前棧頂的內容,可以使用棧指標($sps):

     x/16x $sp
    

    輸出示例:

     0xbffff7f0: 0x00000000 0x08048484 0xbffff808 0x080483f7
     0xbffff800: 0xbffff818 0x00000000 0x00000000 0x00000000
     0xbffff810: 0x00000000 0x00000000 0x00000000 0x00000000
     0xbffff820: 0x00000000 0x00000000 0x00000000 0x00000000
    

4.3 總結

x命令在GDB中非常強大和靈活,可以根據需要檢視記憶體的不同部分和格式。透過熟練掌握x命令的使用,可以更有效地除錯程式,分析記憶體內容和程式行為。

5 X86上崩潰

5.1 堆疊資訊

#0  0xb140478f in RKD::WobjGettorStationary::GetTargetWobj() const () from /home/ae/workspace/code/newARCS/src/packfiles/x86-lib/libarcs-rkd.so
#1  0xb0c39fdd in InterpolationProcess::InterpolateSubTrajPos(RKD::TrajectoryInterface*, double, double, bool) () from /home/ae/workspace/code/newARCS/src/packfiles/x86-lib/libRkdInterface.so
#2  0xb0c3e827 in InterpolationProcess::InterpolatePos(RKD::TrajectoryInterface*, double, double, bool, int&) () from /home/ae/workspace/code/newARCS/src/packfiles/x86-lib/libRkdInterface.so
#3  0xb0c423f1 in InterpolationProcess::Interpolate(int&) () from /home/ae/workspace/code/newARCS/src/packfiles/x86-lib/libRkdInterface.so
#4  0xb67ad82f in RobotInterpolation::ProcessNormalMode (this=0x8452758) at /home/ae/workspace/code/newARCS/src/rtt/mechunit/robot_interpolation.cpp:1575
#5  0xb67a8668 in RobotInterpolation::step (this=0x8452758) at /home/ae/workspace/code/newARCS/src/rtt/mechunit/robot_interpolation.cpp:603
#6  0xb4ef5b97 in ChannelInterpTask::step (this=0x83d3f48) at /home/ae/workspace/code/newARCS/src/rtt/ChannelInterpTask.cpp:29
#7  0xb52cc998 in RTT::Activity::step (this=0x83d8178) at /home/ae/workspace/code/newARCS/src/rtt/Activity.cpp:95
#8  0xb5644afd in RTT::os::thread_function (t=0x83d8180) at /home/ae/workspace/code/newARCS/src/rtt/os/Thread.cpp:135
#9  0xb564ff0b in RTT::os::rtos_posix_thread_wrapper (cookie=0x83cbc78) at /home/ae/workspace/code/newARCS/src/rtt/os/gnulinux/fosi_internal.cpp:101
#10 0xb148d295 in start_thread (arg=0xaecfeb40) at pthread_create.c:333
#11 0xb0e6a1ce in clone () at ../sysdeps/unix/sysv/linux/i386/clone.S:114

5.2 崩潰處的暫存器資訊

(gdb) info registers

eax            0xd2f1a9fc          -755914244
ecx            0x12                18
edx            0xac163360          -1407831200
ebx            0xb0c5f000          -1329205248
esp            0xaecfd304          0xaecfd304
ebp            0xaecfd668          0xaecfd668
esi            0xac17b320          -1407732960
edi            0x4                 4
eip            0xb140478f          0xb140478f <Test() const+15>
eflags         0x10282             [ SF IF RF ]
cs             0x73                115
ss             0x7b                123
ds             0x7b                123
es             0x7b                123
fs             0x0                 0
gs             0x33                51

進一步排查,該指令處的彙編程式碼:x/10i $eip # 檢視從eip暫存器指向的地址開始的10條彙編程式碼

(gdb) x/10i $eip
=> 0xb140478f <_ZNK3RKD20WobjGettorStationary13GetTargetWobjEv+15>:	fldl   0x4c(%eax)
0xb1404792 <_ZNK3RKD20WobjGettorStationary13GetTargetWobjEv+18>:	lea    0x4(%eax),%esi
0xb1404795 <_ZNK3RKD20WobjGettorStationary13GetTargetWobjEv+21>:	mov    %edx,%edi
0xb1404797 <_ZNK3RKD20WobjGettorStationary13GetTargetWobjEv+23>:	rep movsl %ds:(%esi),%es:(%edi)
0xb1404799 <_ZNK3RKD20WobjGettorStationary13GetTargetWobjEv+25>:	fstpl  0x48(%edx)
0xb140479c <_ZNK3RKD20WobjGettorStationary13GetTargetWobjEv+28>:	fldl   0x54(%eax)
0xb140479f <_ZNK3RKD20WobjGettorStationary13GetTargetWobjEv+31>:	fstpl  0x50(%edx)
0xb14047a2 <_ZNK3RKD20WobjGettorStationary13GetTargetWobjEv+34>:	fldl   0x5c(%eax)
0xb14047a5 <_ZNK3RKD20WobjGettorStationary13GetTargetWobjEv+37>:	mov    %edx,%eax
0xb14047a7 <_ZNK3RKD20WobjGettorStationary13GetTargetWobjEv+39>:	fstpl  0x58(%edx)

或者直接用disassemble $eip,可以看到該指標上下文的彙編程式碼

    0xb1404780 <+0>:	push   %edi
    0xb1404781 <+1>:	mov    $0x12,%ecx
    0xb1404786 <+6>:	push   %esi
    0xb1404787 <+7>:	mov    0x10(%esp),%eax
    0xb140478b <+11>:	mov    0xc(%esp),%edx
=>  0xb140478f <+15>:	fldl   0x4c(%eax)
    0xb1404792 <+18>:	lea    0x4(%eax),%esi
    0xb1404795 <+21>:	mov    %edx,%edi
    0xb1404797 <+23>:	rep movsl %ds:(%esi),%es:(%edi)
    0xb1404799 <+25>:	fstpl  0x48(%edx)
    0xb140479c <+28>:	fldl   0x54(%eax)
    0xb140479f <+31>:	fstpl  0x50(%edx)
    0xb14047a2 <+34>:	fldl   0x5c(%eax)
    0xb14047a5 <+37>:	mov    %edx,%eax
    0xb14047a7 <+39>:	fstpl  0x58(%edx)
    0xb14047aa <+42>:	pop    %esi
    0xb14047ab <+43>:	pop    %edi
    0xb14047ac <+44>:	ret    $0x4

在 GDB 除錯時,檢視 info registers 命令的輸出,可以看到所有 CPU 暫存器的當前狀態。每個暫存器儲存的資訊可能有助於你理解程式的執行情況及其崩潰的原因。以下是暫存器輸出的詳細說明:

5.3 暫存器說明

  • eax, ecx, edx, ebx, esi, edi: 通用暫存器,用於儲存函式的引數、返回值以及臨時資料。
  • esp: 棧指標,指向當前棧的頂端。
  • ebp: 基址指標,指向當前棧幀的基址,用於訪問函式的引數和區域性變數。
  • eip: 指令指標,指向下一條將要執行的指令的地址。
  • eflags: 標誌暫存器,包含條件碼標誌、控制標誌、系統標誌等。
  • cs: 程式碼段暫存器,指示程式碼段的起始地址。
  • ss: 棧段暫存器,指示棧段的起始地址。
  • ds, es, fs, gs: 資料段暫存器,用於指示資料段的起始地址。

5.4 具體解釋

  • eax: 0xd2f1a9fc (-755914244) - 通常用於儲存函式返回值或臨時資料。
  • ecx: 0x12 (18) - 通常用於迴圈計數或傳遞函式引數。
  • edx: 0xac163360 (-1407831200) - 用於儲存函式引數或臨時資料。
  • ebx: 0xb0c5f000 (-1329205248) - 通常用於儲存基址或臨時資料。
  • esp: 0xaecfd304 (0xaecfd304) - 當前棧指標,指向棧頂。
  • ebp: 0xaecfd668 (0xaecfd668) - 當前棧幀基址,用於訪問函式的引數和區域性變數。
  • esi: 0xac17b320 (-1407732960) - 通常用於儲存源地址。
  • edi: 0x4 (4) - 通常用於儲存目標地址。
  • eip: 0xb140478f (0xb140478f <RKD::WobjGettorStationary::GetTargetWobj() const+15>) - 當前指令指標,指向下一條將要執行的指令`。
  • eflags: 0x10282 [ SF IF RF ] - 標誌暫存器,包含條件碼和其他控制標誌。
    • SF (Sign Flag) - 標誌結果為負值。
    • IF (Interrupt Enable Flag) - 允許中斷。
    • RF (Resume Flag) - 控制除錯斷點的處理。

5.5 分析步驟

  • 檢視指令指標 (eip):

    當前指令指標 (eip) 指向 0xb140478f,這是函式 RKD::WobjGettorStationary::GetTargetWobj() const 的某個位置(+15 位元組)。這可能是導致 SIGSEGV 的位置。

  • 檢視棧指標 (esp) 和基址指標 (ebp):

    esp 指向 0xaecfd304,ebp 指向 0xaecfd668。可以透過檢視棧上的資料來了解崩潰前的呼叫情況和引數

  • 檢視通用暫存器:

    可以檢查通用暫存器的值是否在預期範圍內,特別是 eax, ecx, edx, ebx, esi, 和 edi,以確定它們是否包含非法地址或未初始化的值。

  • 檢視呼叫堆疊:

  • 使用 bt(backtrace)命令檢視呼叫堆疊,找出導致崩潰的呼叫路徑。

  • 檢查原始碼: 使用 list 命令檢視崩潰位置的原始碼,結合暫存器和堆疊資訊,找出問題所在。

    (gdb) list *0xb140478f

進一步除錯
根據以上資訊,可以進一步使用 GDB 的其他命令進行除錯:

  • 檢查記憶體內容:

    (gdb) x/10i $eip # 檢視指令指標周圍的指令
    (gdb) x/10x $esp # 檢視棧指標周圍的記憶體

  • 列印變數和表示式:

    (gdb) print variable_name
    (gdb) info locals # 列印區域性變數
    (gdb) info args # 列印函式引數

透過這些步驟,可以更詳細地分析崩潰原因,找出非法記憶體訪問的根本問題

6 除錯執行緒

崩潰資訊如下

Thread 17 "ABC" received signal SIGSEGV, Segmentation fault.

具體含義

  • Thread 17: 指出這是程式的第 17 個執行緒。多執行緒程式中會有多個執行緒同時執行,這個執行緒編號有助於我確定出錯的具體執行緒。
  • ABC: 執行緒名稱。如果程式為執行緒命名,這裡會顯示執行緒的名稱,有助於識別執行緒的功能或角色。
  • received signal SIGSEGV: 程式收到了 SIGSEGV 訊號,這是一個表示非法記憶體訪問的訊號。
  • segmentation fault: 即分段錯誤,表示程式嘗試訪問未分配的記憶體或進行非法的記憶體訪問。

可能的原因

  • 訪問空指標: 試圖解引用一個空指標(NULL 指標)。
  • 訪問未初始化的指標: 使用了尚未初始化的指標。
  • 緩衝區溢位: 超出陣列或緩衝區的邊界進行訪問。
  • 無效指標: 使用了一個已經被釋放的指標。
  • 越界訪問: 超出分配記憶體區域的邊界進行訪問。

除錯步驟

  • 檢視崩潰位置: 使用 bt 命令(backtrace)檢視呼叫堆疊,找出崩潰的具體位置。

      (gdb) bt
    
  • 檢視當前執行緒資訊: 確認當前執行緒的上下文和暫存器資訊。

      (gdb) info threads
      (gdb) thread apply all bt  thread apply [thread-id-list | all] args
    
  • 切換到出錯執行緒: 使用 thread 命令切換到特定執行緒。

      (gdb) thread 17
      (gdb) thread apply 17 bt
    
  • 檢視程式碼位置: 使用 list 命令檢視出錯位置的原始碼。

      (gdb) list
    
  • 檢視變數值: 使用 print 命令檢視關鍵變數的值,以確定是否存在非法值或無效指標。

      (gdb) print variable_name
    
  • 檢查記憶體地址: 檢視訪問的記憶體地址是否合法。

      (gdb) info registers
      (gdb) x/10x $sp  # 檢視棧上的記憶體
    

相關文章