C 語言偵錯程式是如何工作的?

2021-07-10 08:36:39 字數 4908 閱讀 8651

當你用 gdb 的時候,可以看到它完全控制了應用程式程序。當你在程式執行的時候用ctrl + c,程式的執行就能夠終止,而gdb能展示它的當前位址、堆疊跟蹤資訊之類的內容。

但是它是怎麼辦到的呢?

開始,讓我們先研究它怎樣才會不工作。它不能通過閱讀和分析程式的二進位制資訊來模擬程式的執行。它其實能做,而那應該能起作用(valgrind記憶體偵錯程式就是這樣工作的),但是這樣的話會很慢。valgrind會讓程式慢1000倍,但是gdb不會。它的工作機制與qemu虛擬機器一樣。

所以到底是怎麼回事?黑魔法?……不,如果那樣的話就太簡單了。

另一種猜想?……?破解!是的,這裡正是這樣的。作業系統核心也提供了一些幫助。

首先,關於linux的程序機制需要了解一件事:父程序可以獲得子程序的附加資訊,也能夠ptrace它們。並且你可以猜到的是,偵錯程式是被除錯的程序的父程序(或者它會變成父程序,在linux中程序可以將乙個程序變為自己子程序:-))

linux ptrace api 允許乙個(偵錯程式)程序來獲取低等級的其他(被除錯的)程序的資訊。特別的,這個偵錯程式可以:

ptrace的實現不在本文討論的範圍內,所以我不想進一步討論,只是簡單地解釋它是如何工作的(我不是核心專家,如果我說錯了請一定指出來,並原諒我過分簡化:-))

ptrace 是linux核心的一部分,所以它能夠獲取程序所有核心級資訊:

這個解釋超出了特定的linux本地除錯,但是對於大部分其他環境是合理的。要了解gdb在不同目標平台請求的內容,你可以看一下它在目標棧裡面的操作。

在這個目標介面裡,你可以看到所有c除錯需要的高階操作:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

structtarget_ops

普通的gdb呼叫這些函式,然後目標相關的元件再實現它們。(概念上)這是乙個棧,或者乙個金字塔:棧頂的是非常通用的,比如:

那個遠端目標很有趣,因為它通過乙個連線協議(tcp/ip、串列埠)把兩台「電腦」間的執行棧分離開來。

那個遠端的部分可以是執行在另一台linux機器上的gdbserver。但是它也可以是乙個硬體除錯埠的介面(jtag) 或者乙個虛擬的機器管理程式(比如 qemu),並能夠代替核心和ptrace的功能。那個遠端根偵錯程式會查詢管理程式的結構,或者直接地查詢處理器硬體暫存器來代替對os核心結構的查詢。

我們能看到ptrace的api提供了這裡所有底層機制被要求實現的偵錯程式:

但是這就是乙個偵錯程式的全部工作嗎?不,這只是那些非常低階的部分……它還會處理符號。這是,鏈結源程式和二進位制檔案。被忽視可能也是最重要的的一件事:斷點!我會首先解釋一下斷點是如何工作的,因為這部分內容非常有趣且需要技巧,然後回到符號處理。

就像我們之前看到的那樣,斷點不是ptraceapi的一部分。但是我們可以改動記憶體並獲取被除錯的程式訊號。你看不到其中的相關之處?這是因為斷點的實現比較需要技巧並且還要一點hack!讓我們來檢驗一下如何在乙個指定的位址設定乙個斷點。

1、這個偵錯程式讀取(ptrace追蹤)存在位址裡的二進位制指令,並儲存在它自己的資料結構中。

2、它在這個位置寫入乙個不合法的指令。不管這個指令是啥,只要它是不合法的。

3、當被除錯的程式執行到這個不合法的指令時(或者更準確地說,處理器將記憶體中的內容設定好時)它不會繼續執行(因為它是不合法的)。

4、在現代多工系統中,乙個不合法的指令不會使整個系統崩潰掉,但是會通過引發乙個中斷(或錯誤)把控制權交回給系統核心。

5、這個中斷被linux翻譯成乙個sigtrap訊號,然後被傳送到處理器……或者發給它的父程序,就像偵錯程式希望的那樣。

6、偵錯程式獲得訊號並檢視被除錯的程式指令指標的值(換言之,是陷入 trap發生的地方)。如果這個ip位址是在斷點列表中,那麼就是乙個偵錯程式的斷點(否則就是乙個程序中的錯誤,只需要傳過訊號並讓它崩潰)。

7、現在,那個被除錯的程式已經停在了斷點,偵錯程式可以讓使用者來做任何他/她想要做的事,等待時機合適繼續執行。

8、為了要繼續執行,這個偵錯程式需要 1、寫入正確的指令來回到被除錯的程式的記憶體; 2、單步執行(繼續執行單個cpu指令,伴隨著ptrace 單步執行); 3、把非法指令寫回去(使得這個執行過程下一次可以再次停止) ;4、讓這個執行正常執行

現在,讓我們回到訊號和除錯資訊處理。我沒有詳細地學習這部分,所以只是大體地說一說。

首先,我們是否可以不使用除錯資訊和訊號位址來除錯呢?答案是可以。因為正如我們看到過的那樣,所有的低階指令是對cpu暫存器和記憶體位址來操作的,不是源程式層面的資訊。因此,這個到源程式的鏈結只是為了方便使用者。沒有除錯資訊的時候,你看程式的方式就像是處理器(和核心)看到的一樣:二進位制(彙編)指令和記憶體位元組。gdb不需要進一步的資訊來把二進位制資訊翻譯成cpu指令:

(gdb) x/10x $pc # hexadecimal representation

0x402c60: 0x56415741 0x54415541 0x55f48949 0x4853fd89

0x402c70: 0x03a8ec81 0x8b480000 0x8b48643e 0x00282504

0x402c80: 0x89480000 0x03982484

(gdb) x/10i $pc # instruction representation

=> 0x402c60: push %r15

0x402c62: push %r14

0x402c64: push %r13

0x402c66: push %r12

0x402c68: mov %rsi,%r12

0x402c6b: push %rbp

0x402c6c: mov %edi,%ebp

0x402c6e: push %rbx

0x402c6f: sub $0x3a8,%rsp

0x402c76: mov (%rsi),%rdi

現在,如果我們加上除錯資訊,gdb能夠把符號名稱和位址配對:

(gdb) $pc

$1 = (void (*)()) 0x402c60

你可以通過nm -a $file來獲取elf二進位制的符號列表:

nm -a /usr/lib/debug/usr/bin/ls.debug | grep " main"

0000000000402c60 t main

gdb還會能夠展示堆疊跟蹤資訊(稍後會詳細說),但是只有感興趣的那部分:

(gdb) where

#0 write ()

#1 0x0000003d492769e3 in _io_new_file_write ()

#2 0x0000003d49277e4c in new_do_write ()

#3 _io_new_do_write ()

#4 0x0000003d49278223 in _io_new_file_overflow ()

#5 0x00000000004085bb in print_current_files ()

#6 0x000000000040431b in main ()

我們現在有了pc位址和相應的函式,就是這樣。在乙個函式中,你將需要對著彙編來除錯!

現在讓我們加入除錯資訊:就是dwarf規範下的gcc -g選項。我不是特別熟悉這個規範,但我知道它提供的:

$ dwarfdump /usr/lib/debug/usr/bin/ls.debug | grep 402ce4

0x00402ce4 [1289, 0] ns

$ addr2line -e /usr/lib/debug/usr/bin/ls.debug 0x00402ce4

/usr/src/debug/coreutils-8.21/src/ls.c:1289

試一試dwarfdump來檢視二進位制檔案裡嵌入的資訊。addr2line也能用到這些資訊:

很多源**層的除錯命令會依賴於這些資訊,比如next命令,這會在下一行的位址設定乙個斷點,那個print命令會依賴於變數的型別來輸出(charintfloat,而不是二進位制或十六進製制)。

我們已經見過偵錯程式內部的好多方面了,所以我只會最後說幾點:

還可以玩gdb gdb,或者更好的(好多了)gdb --pid $(pid of gdb),因為把兩個偵錯程式放到同乙個終端裡是瘋狂的:-)。還可以除錯系統:

qemu-system-i386 -gdb tcp::1234

gdb --pid $(pidof qemu-system-i386)

gdb /boot/vmlinuz --exec "target remote localhost:1234"

但我會在另一篇文章裡提到!

Linux下C語言的偵錯程式 Gdb

除錯是所有程式設計師都會面臨的問題.如何提高程式設計師的除錯效率,更好更快地定位程式中的問題從而加快程式開發的進度,是大家共同面對的問題.就如讀者熟知的windwos下的一些除錯工具,如vc自帶的設定斷點,單步跟蹤等,都受到了廣大使用者的讚賞.那麼,在liunx下有什麼好的除錯工具呢?gdb是一款g...

python的偵錯程式 Python 偵錯程式入門

python 生態系統包含豐富的工具和庫,可以讓開發人員更加舒適。例如,我們之前已經介紹了如何使用互動式 shell 增強 python。本文重點介紹另一種可以節省時間並提高 python 技能的工具 python 偵錯程式。python 偵錯程式 python 標準庫提供了乙個名為 pdb 的偵錯...

偵錯程式的原理

偵錯程式和被除錯程式是通過中斷系統來實現的.不過在windows下,這些工作都不需要你來做了.windows封裝了一套完整的除錯介面.你只要建立乙個偵錯程式,用它開啟 或建立 乙個被除錯程序,然後根據除錯的目標中所包含的除錯資訊找出源 與機器碼之間的對映關係.在你要中斷的地方加 乙個int3指令 並...