gdb調試原理及qemu中的gdbserver

(一)gdb調試原理html

此部分轉自:https://blog.csdn.net/u012658346/article/details/51159971     https://www.cnblogs.com/xsln/p/ptrace.html網絡

gdb調試的原理是基於ptrace系統調用,ptrace()系統調用提供了一個方法,該方法使一個程序(追蹤者)能夠觀察和控制另一個程序(被追蹤者)的執行,並檢查和改變被追蹤者的內存及寄存器。它主要用於實現斷點調試和追蹤系統調用。函數

當被追蹤時,被追蹤線程在接收信號時會被中止,即便那個信號是被忽略的也是如此(SIGKILL除外)。追蹤程序會在一個調用waitpid(或者其餘類wait系統調用)時收到通知,該調用會返回一個包含被追蹤線程中止的緣由的狀態值。當被追蹤線程中止時,追蹤程序可使用多種ptrace請求來檢查和編輯被追蹤線程。追蹤程序可讓被追蹤線程繼續運行,有選擇地忽略發過來的信號(甚至能夠發送一個徹底不一樣的信號給被追蹤線程)性能

利用ptrace系統調用,可在被調試程序和gdb之間創建追蹤關係。而後全部發送給被調試程序(被追蹤線程)的信號(除SIGKILL)都會被gdb截獲,gdb根據截獲的信號,查看被調試程序相應的內存地址,並控制被調試的程序繼續運行。測試

 

ptrace系統調用原型:編碼

long ptrace(enum __ptrace_request request, pid_t pid,void *addr,void *data); spa

request參數的主要選項:
PTRACE_TRACEME:由子進程調用,表示本進程將被其父進程跟蹤,交付給這個進程的全部信號,即便信號是忽略處理的(除SIGKILL以外),都將使其中止,父進程將經過wait()獲知這一狀況。.net

PTRACE_ATTACH: attach到一個指定的進程,使其成爲當前進程跟蹤的子進程,而子進程的行爲等同於它進行了一次PTRACE_TRACEME操做。可是,須要注意的是,雖然當前進程成爲被跟蹤進程的父進程,可是子進程使用getppid()的到的仍將是其原始父進程的pid。當你在gdb中使用attach命令來跟蹤一個指定進程/線程的時候,gdb就自動成爲改進程的父進程,而被跟蹤的進程則使用了一次PTRACE_TRACEME,gdb也就瓜熟蒂落的接管了這個進程。 命令行

PTRACE_CONT:繼續運行以前中止的子進程。可同時向子進程交付指定的信號。線程

 

gdb三種調試方式:
1)attach並調試一個已經運行的進程:
肯定須要進行調試的進程id,運行gdb,輸入attch pid,如:gdb 12345。gdb將對指定進行執行以下操做:ptrace(PTRACE_ATTACH,pid,0,0), 創建本身與進程號爲pid的進程間的跟蹤關係。即利用PTRACE_ATTACH,使本身變成被調試程序的父進程。用attach創建起來的跟蹤關係,能夠調用ptrace(PTRACE_DETACH,pid,...)來解除。注意attach進程時的權限問題,如一個非root權限的進程是不能attach到一個root進程上的 。
2)運行並調試一個新的進程,利用fork+execve執行被測試的程序,子進程在執行execve以前調用ptrace(PTRACE_TRACEME),創建了與父進程(debugger)的跟蹤關係:
運行gdb,經過命令行參數或file指定目標調試程序,如gdb ./test 
輸入run命令,gdb執行下述操做:
經過fork()系統調用建立一個新進程
在新建立的子進程中調用ptrace(PTRACE_TRACEME,0,0,0)
在子進程中經過execv()系統調用加載用戶指定的可執行文件
3)遠程調試目標主機上新建立的進程
gdb運行在調試機,gdbserver運行在目標機,經過兩者之間定義的數據格式進行通訊

gdb調試基礎--信號

gdb調試的實現都是創建在信號的基礎上的,在使用參數爲PTRACE_TRACEME或PTRACE_ATTACH的ptrace系統調用創建調試關係後,交付給目標程序的任何信號首先都會被gdb截獲。 所以gdb能夠先行對信號進行相應處理,並根據信號的屬性決定是否要將信號交付給目標程序。 

.斷點原理:

  1)    斷點的實現原理,就是在指定的位置插入斷點指令,當被調試的程序運行到斷點的時候,產生SIGTRAP信號。該信號被gdb捕獲並進行斷點命中斷定,當gdb判斷出此次SIGTRAP是斷點命中以後就會轉入等待用戶輸入進行下一步處理,不然繼續。 

  2)    斷點的設置原理: 在程序中設置斷點,就是先將該位置的原來的指令保存,而後向該位置寫入int 3。當執行到int 3的時候,發生軟中斷,內核會給子進程發出SIGTRAP信號,固然這個信號會被轉發給父進程。而後用保存的指令替換int3,等待恢復運行。

  3)    斷點命中斷定:gdb把全部的斷點位置都存放在一個鏈表中,命中斷定即把被調試程序當前中止的位置和鏈表中的斷點位置進行比較,看是斷點產生的信號,仍是無關信號。

  4)    條件斷點的斷定:原理同3),只是恢復斷點處的指令後,再多加一步條件判斷。若表達式爲真,則觸發斷點。因爲須要判斷一次,所以加入條件斷點後,無論有沒有觸發到條件斷點,都會影響性能。在x86平臺,某些硬件支持硬件斷點,在條件斷點處不插入int    3,而是插入一個其餘指令,當程序走到這個地址的時候,不發出int 3信號,而是先去比較一下特定寄存器和某個地址的內容,再決定是否發送int 3。所以,當你的斷點的位置會被程序頻繁地「路過」時,儘可能使用硬件斷點,會對提升性能有幫助

單步跟蹤:

next指令能夠實現單步調試,即每次只執行一行語句。一行語句可能對應多條及其指令,當執行next指令時,gdb會計算下一條語句對應的第一條指令的地址,而後控制目標程序走到該位置中止。 

gdb調試基本命令

 

(二)qemu中的gdbserver

正常狀況下進行遠程調試須要被調試端安裝有gdbserver程序,而qemu中內置了gdbserver模塊,基於此可以使用gdb實現對qemu虛擬機的遠程調試,GDB/GDBSERVER調試模型的原理以下:

在GDB/GDBSERVER調試模型中,GDBSERVER是一個輕量級的GDB調試器,在調試過程當中擔任着調試代理的角色。在調試過程當中,主機和目標機之間使用串口或者網絡做爲通訊的通道。在主機上GDB經過這條通道使用一種基於ASCII的簡單通信協議RSP與在目標機上運行的GDBSERVER進行通信。GDB發送指令,如內存、寄存器讀寫,GDBSERVER則首先與運行被調試程序映像的進程進行綁定,而後等待GDB發來的數據,對包含命令的數據包進行解析以後便進行相關處理,而後將結果返回給主機上的GDB。

RSP協議將GDB/GDBSERVER間通信的內容更看作是數據包,數據包的內容都使用ASCII字符。每個數據包都遵循這樣的格式:$ <調試信息>#<校驗碼>.

如上圖所示,包的內容會以16進制的形式來編碼(enhex),#後面的兩位數字是校驗碼,具體的計算方式是數據包中全部字符求和再用256求餘數。而數據包的內容,也就是RSP協議的載體,將會是gdb接收的命令。接受方在收到數據包以後,對數據包進行校驗,若正確迴應「+」,反之迴應「-」。

RSP 協議中定義的主要命令能夠分爲 3 類:

(1)寄存器/內存讀寫命令

命令 g: 讀全部寄存器的值

命令 G:寫全部寄存器的值

命令 P: 寫某個寄存器

命令 m: 讀某個內存單元

命令 M:寫某個內存單元

(2)程序控制命令

命令?: 報告上一次的信號

命令 s: 單步執行

命令 c: 繼續執行

命令 k: 終止程序

(3)其它命令

命令 O:控制檯輸出(Console Output )

命令 E:出錯迴應(Error response)

當主機使用gdb調試時,gdb和qemu中內置的gdbserver就使用上述模型進行交互,從而對qemu虛擬機進行調試。如gdb調試端發送x/ <n/f/u> <addr>表示讀取addr處的內容,命令經RSP協議封裝成數據包發送至qemu的gdbserver端,gdbserver收到數據包後對其進行校驗,校驗成功後進行解析處理並返回至gdb客戶端。

開啓gdbserver以後,會等待來自gdb的鏈接請求,默認端口爲1234,gdb使用ip和端口與gdbserver鏈接:

鏈接創建後會調用gdb_handlesig()等待stdin傳來的gdb指令,調用gdb_read_byte()解析用戶的輸入,並對數據包進行校驗,若校驗正確調用gdb_handle_packet()進行gdb命令的處理。

以下解析字符爲m時,表示讀取某個內存單元,則調用函數target_memory_rw_debug()進行內存單元的讀取,該函數最後調用cpu_memory_rw_debug()讀取內存內容

當解析字符爲g時,使用gdb_read_register讀取寄存器信息,該函數會調用特定CPU類型的回調函數:

x86下調用以下函數,經過qemu爲虛擬機維護的CPUX86State結構體獲得虛擬機的寄存器信息:

相似地,插入一個斷點時:

在kvm_enabled的狀況下,調用kvm_insert_breakpoint:

該函數進行斷點的插入並最終使用kvm_update_guest_debug向kvm更新客戶機的debug狀態,該函數調用kvm_invoke_set_guest_debug,進一步調用kvm_vcpu_ioctl(cpu, KVM_SET_GUEST_DEBUG,&dbg_data->dbg)執行ioctl至kvm中設置相關異常向量,BP(breakpoint,int3),DB(int 1)(插一句,可經過設置異常位圖中的這兩個位對上述指令進行攔截)

相關文章
相關標籤/搜索