2016-12-05 50 views
9

當我學習組裝,我用GDB方式如下:如何使用在同一CPU上運行的調試器來讀取CPU寄存器?

gdb ./a.out (a is a compiled C script that only prints hello world) 
break main 
run 
info registers 

爲什麼我可以看到我的程序中使用時,我是我自己使用相同的CPU,打印登記檔案?不應該使用GDB(或操作系統)覆蓋寄存器,只顯示覆蓋的寄存器? 我能想到的唯一答案是,我的CPU是雙核的,其中一個核心正在使用,另一個是爲程序保留的事實。

+2

好概念的問題。在學習彙編時,一定要看看如何調用'call stack'和'instruction pointer'。 _一種使用寄存器的方法,並且仍然知道它們的作用是在更改它們之前將它們推入堆棧。因爲您知道您在堆棧中推送了多少字節,所以您甚至可以在開始更改內容之前推斷堆棧指針的內容。但是,許多CPU都有一條快速指令將所有寄存器複製到緩衝區或從緩衝區複製。主要用於交換進程,因此CPU和寄存器可以共享,但調試器也可以這樣做。 –

回答

14

操作系統保持寄存器爲每個執行線程的狀態。當您在gdb中檢查寄存器時,調試器實際上是要求操作系統從保存的狀態讀取寄存器值。你的程序沒有在那個時候運行,它是一個調試器。

假設有您的系統上沒有其他進程。這裏是發生了什麼的簡化圖:

  1. 調試器發射和獲取CPU
  2. 調試詢問OS加載程序
  3. 調試詢問OS放置斷點
  4. 調試問OS開始執行你的程序。操作系統保存gdb寄存器狀態並將控制權轉交給您的程序。
  5. 你的程序命中了斷點。操作系統接受控制,保存程序的寄存器狀態,重新加載gdb寄存器並將cpu送回gdb。
  6. 調試器要求操作系統從保存的狀態讀取程序的寄存器。

請注意,此機制是多任務操作系統的正常職責的一部分,它不是特定於調試。當OS調度程序決定執行一個不同的程序時,它會保存當前狀態並加載另一個程序。這被稱爲上下文切換,並且每秒可能發生多次,從而給出了程序即使只有一個cpu核心也能同時執行的錯覺。

+1

線程「切換」時狀態被保存到內存中。調試器在你的機器代碼中放置斷點來代替目標指令,運行你的代碼,它會碰到斷點(通常是'int 03h',因爲它是單字節操作碼,所以它可以代替任何指令)和中斷(斷點)處理程序將所有的寄存器保存到內存中,然後將它們用於它自己的事件處理,並將控制切換到調試器,以及發生中斷的信息+保存狀態。編輯:不,你添加了更多的信息.​​.並且更準確,我看到我仍然處於「真實模式」:D – Ped7g

+1

Syscall讓一個進程讀取其他進程的寄存器(和內存)是特定的調試。 – dbrank0

5

早在單任務奧賽斯的舊時代,這可以讓你的程序的執行方式的唯一的東西是中斷。現在,中斷處理程序有你在談論同樣的問題,你的程序計算的東西,用戶按下一個鍵 - 中斷 - 中斷服務程序做了一些工作,但不得修改單個寄存器的過程中。這是主要原因,堆棧是首先發明的。通常一個80x86的DOS中斷服務程序應該是這樣的:

push ax 
push cx 
push dx 
push bx 
push si 
push di 
push bp 
// no need to push sp 
[do actual work, caller registers avaiable on stack if needed] 
pop bp 
pop di 
pop si 
pop bx 
pop dx 
pop cx 
pop ax 
iret 

這是即使如此普遍,一個新的指令對pushapopa(推送/彈出所有)的設立是爲了緩解這個任務。

在當今的CPU與操作系統和應用程序之間的地址空間隔離,CPU提供一些任務狀態系統,並允許操作系統切換任務(中斷可能仍然工作類似於上述概述,但也可以通過任務交換)。所有現代操作系統都使用這種任務狀態系統,其中CPU在進程未被主動執行時保存所有進程的寄存器。就像Jester已經解釋的那樣,gdb只是詢問操作系統有關要調試的進程上的這些值,然後將其打印出來。