ptrace() - Unix 和 Linux 系統呼叫 - 技術教學
Tutorials Point


  Unix 初學者指南
  Unix Shell 程式設計
  高階 Unix
  Unix 有用參考
  Unix 有用資源
  精選閱讀

版權所有 © 2014 tutorialspoint



  首頁     參考     討論論壇     關於 TP  

ptrace() - Unix 和 Linux 系統呼叫


previous next AddThis Social Bookmark Button

廣告

名稱

ptrace - 程序跟蹤

概要

#include <sys/ptrace.h> 

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

描述

ptrace() 系統呼叫提供了一種方法,允許父程序觀察和控制另一個程序的執行,並檢查和更改其核心映像和暫存器。它主要用於實現斷點除錯和系統呼叫跟蹤。

父程序可以透過呼叫fork(2) 並讓生成的子程序執行 PTRACE_TRACEME,然後(通常)執行exec(3) 來啟動跟蹤。或者,父程序可以使用 PTRACE_ATTACH 開始跟蹤現有程序。

在被跟蹤期間,子程序每次收到訊號時都會停止,即使該訊號被忽略。(例外情況是 SIGKILL,它具有其通常的效果。)父程序將在其下一個wait(2) 中收到通知,並且可以在子程序停止時檢查和修改子程序。然後,父程序使子程序繼續執行,可以選擇忽略已傳遞的訊號(甚至傳遞不同的訊號)。

當父程序完成跟蹤時,它可以使用 PTRACE_KILL 終止子程序,或者透過 PTRACE_DETACH 使其以正常、未跟蹤的模式繼續執行。

request 的值決定要執行的操作

標籤描述
PTRACE_TRACEME
 指示此程序將由其父程序跟蹤。傳遞給此程序的任何訊號(除 SIGKILL 外)都會導致其停止,並透過wait() 通知其父程序。此外,此程序隨後對exec() 的所有呼叫都將導致向其傳送 SIGTRAP,從而使父程序有機會在新的程式開始執行之前獲得控制權。如果父程序沒有預期跟蹤它,則程序可能不應該發出此請求。(pidaddrdata 被忽略。)
上述請求僅由子程序使用;其餘請求僅由父程序使用。在以下請求中,pid 指定要對其進行操作的子程序。對於 PTRACE_KILL 之外的請求,子程序必須已停止。
PTRACE_PEEKTEXT,PTRACE_PEEKDATA
 讀取子程序記憶體中地址 addr 處的一個字,並將該字作為ptrace() 呼叫的結果返回。Linux 沒有單獨的文字和資料地址空間,因此這兩個請求目前等效。(引數 data 被忽略。)
PTRACE_PEEKUSR
 讀取子程序USER區域中偏移量 addr 處的一個字,該區域儲存有關程序的暫存器和其他資訊(參見<linux/user.h> 和 <sys/user.h>)。該字作為ptrace() 呼叫的結果返回。通常,偏移量必須與字對齊,儘管這可能因體系結構而異。參見注釋。(data 被忽略。)
PTRACE_POKETEXT,PTRACE_POKEDATA
 將字 data 複製到子程序記憶體中地址 addr 處。如上所述,這兩個請求目前等效。
PTRACE_POKEUSR
 將字 data 複製到子程序USER區域中偏移量 addr 處。如上所述,偏移量通常必須與字對齊。為了維護核心的完整性,不允許對USER區域進行某些修改。
PTRACE_GETREGS,PTRACE_GETFPREGS
 分別將子程序的通用暫存器或浮點暫存器複製到父程序中的地址 data 處。有關此資料格式的資訊,請參見<linux/user.h>。(addr 被忽略。)
PTRACE_GETSIGINFO(自 Linux 2.3.99-pre6 起)
 檢索導致停止的訊號的相關資訊。將一個siginfo_t 結構(參見sigaction(2))從子程序複製到父程序中的地址 data 處。(addr 被忽略。)
PTRACE_SETREGS,PTRACE_SETFPREGS
 分別將子程序的通用暫存器或浮點暫存器從父程序中的地址 data 處複製。與 PTRACE_POKEUSER 一樣,某些通用暫存器修改可能會被禁止。(addr 被忽略。)
PTRACE_SETSIGINFO(自 Linux 2.3.99-pre6 起)
 設定訊號資訊。將一個siginfo_t 結構從父程序中的地址 data 處複製到子程序。這將僅影響通常傳遞給子程序並被跟蹤器捕獲的訊號。可能難以將這些正常訊號與ptrace() 本身生成的合成訊號區分開來。(addr 被忽略。)
PTRACE_SETOPTIONS(自 Linux 2.4.6 起;有關注意事項,請參見錯誤)
 從父程序中的 data 設定 ptrace 選項。(addr 被忽略。)data 被解釋為選項的位掩碼,這些選項由以下標誌指定
標籤描述
PTRACE_O_TRACESYSGOOD(自 Linux 2.4.6 起)
 在傳遞系統呼叫陷阱時,設定訊號編號中的第 7 位(即,傳遞 (SIGTRAP | 0x80) 這使得跟蹤器很容易區分正常陷阱和由系統呼叫引起的陷阱。(PTRACE_O_TRACESYSGOOD 可能不適用於所有體系結構。)
PTRACE_O_TRACEFORK(自 Linux 2.5.46 起)
 在下一個fork() 呼叫處使用 SIGTRAP | PTRACE_EVENT_FORK << 8 停止子程序,並自動開始跟蹤新分叉的程序,該程序將以 SIGSTOP 開始。新程序的 PID 可以使用 PTRACE_GETEVENTMSG 檢索。
PTRACE_O_TRACEVFORK(自 Linux 2.5.46 起)
 在下一個vfork() 呼叫處使用 SIGTRAP | PTRACE_EVENT_VFORK << 8 停止子程序,並自動開始跟蹤新 vfork 的程序,該程序將以 SIGSTOP 開始。新程序的 PID 可以使用 PTRACE_GETEVENTMSG 檢索。
PTRACE_O_TRACECLONE(自 Linux 2.5.46 起)
 在下一個clone() 呼叫處使用 SIGTRAP | PTRACE_EVENT_CLONE << 8 停止子程序,並自動開始跟蹤新克隆的程序,該程序將以 SIGSTOP 開始。新程序的 PID 可以使用 PTRACE_GETEVENTMSG 檢索。此選項可能無法在所有情況下都捕獲clone() 呼叫。如果子程序使用 CLONE_VFORK 標誌呼叫clone(),則如果設定了 PTRACE_O_TRACEVFORK,則將傳遞 PTRACE_EVENT_VFORK;否則,如果子程序使用設定為 SIGCHLD 的退出訊號呼叫clone(),則如果設定了 PTRACE_O_TRACEFORK,則將傳遞 PTRACE_EVENT_FORK。
PTRACE_O_TRACEEXEC(自 Linux 2.5.46 起)
 在下一個exec() 呼叫處使用 SIGTRAP | PTRACE_EVENT_EXEC << 8 停止子程序。
PTRACE_O_TRACEVFORKDONE(自 Linux 2.5.60 起)
 在下一個vfork() 呼叫完成後使用 SIGTRAP | PTRACE_EVENT_VFORK_DONE << 8 停止子程序。
PTRACE_O_TRACEEXIT(自 Linux 2.5.60 起)
 在退出時使用 SIGTRAP | PTRACE_EVENT_EXIT << 8 停止子程序。子程序的退出狀態可以使用 PTRACE_GETEVENTMSG 檢索。此停止將在程序退出早期完成時進行(此時暫存器仍然可用),允許跟蹤器檢視退出發生的位置,而正常的退出通知在程序完成退出後完成。即使上下文可用,跟蹤器此時也無法阻止退出發生。
PTRACE_GETEVENTMSG(自 Linux 2.5.46 起)
 檢索有關剛剛發生的 ptrace 事件的訊息(作為unsigned long),將其放置在父程序中的地址 data 處。對於 PTRACE_EVENT_EXIT,這是子程序的退出狀態。對於 PTRACE_EVENT_FORK、PTRACE_EVENT_VFORK 和 PTRACE_EVENT_CLONE,這是新程序的 PID。(addr 被忽略。)
PTRACE_CONT
 重新啟動已停止的子程序。如果 data 非零且不是 SIGSTOP,則將其解釋為要傳遞給子程序的訊號;否則,不傳遞訊號。因此,例如,父程序可以控制是否傳遞傳送給子程序的訊號。(addr 被忽略。)
PTRACE_SYSCALL,PTRACE_SINGLESTEP
 如 PTRACE_CONT 一樣重新啟動已停止的子程序,但安排子程序在進入或退出系統呼叫的下一時刻停止,或在執行單個指令後停止,分別。(子程序通常也會在收到訊號時停止。)從父程序的角度來看,子程序似乎已因收到 SIGTRAP 而停止。因此,例如,對於 PTRACE_SYSCALL,想法是在第一次停止時檢查系統呼叫的引數,然後執行另一個 PTRACE_SYSCALL 並檢查第二次停止時系統呼叫的返回值。(addr 被忽略。)
PTRACE_SYSEMU,PTRACE_SYSEMU_SINGLESTEP(自 Linux 2.6.14 起)
 對於 PTRACE_SYSEMU,繼續並在進入下一個系統呼叫時停止,該系統呼叫將不會執行。對於 PTRACE_SYSEMU_SINGLESTEP,執行相同的操作,但如果這不是系統呼叫,則也進行單步執行。此呼叫由 User Mode Linux 等希望模擬子程序所有系統呼叫的程式使用。(addrdata 被忽略;並非所有體系結構都支援。)
PTRACE_KILL
 向子程序傳送 SIGKILL 以終止它。(addrdata 被忽略。)
PTRACE_ATTACH
 附加到 pid 中指定的程序,使其成為當前程序的被跟蹤“子程序”;子程序的行為就像它執行了 PTRACE_TRACEME 一樣。當前程序實際上成為子程序的大多數用途的父程序(例如,它將收到子程序事件的通知,並在ps(1) 輸出中顯示為子程序的父程序),但子程序的getppid(2) 仍將返回原始父程序的 PID。子程序將收到 SIGSTOP,但在此呼叫的完成時不一定已停止;使用wait() 等待子程序停止。(addrdata 被忽略。)
PTRACE_DETACH
 如 PTRACE_CONT 一樣重新啟動已停止的子程序,但首先與程序分離,撤消 PTRACE_ATTACH 的重父程序效果以及 PTRACE_TRACEME 的效果。儘管可能並非有意,但在 Linux 下,無論使用哪種方法啟動跟蹤,都可以以這種方式分離被跟蹤的子程序。(addr 被忽略。)

註釋

儘管ptrace() 的引數根據給出的原型進行解釋,但 GNU libc 目前將ptrace() 宣告為僅具有 request 引數固定的可變引數函式。這意味著可以省略不需要的尾隨引數,儘管這樣做使用了未記錄的gcc(1) 行為。

init(8)(PID 為 1 的程序)可能無法被跟蹤。

記憶體內容和 USER 區域的佈局非常依賴於作業系統和體系結構。提供的偏移量和返回的資料可能與struct user 的定義不完全匹配。

“字”的大小由 OS 變體確定(例如,對於 32 位 Linux,它是 32 位,依此類推)。

追蹤導致跟蹤程序的語義出現一些細微的差異。例如,如果一個程序使用 PTRACE_ATTACH 附加,則其原始父程序將無法再透過 wait() 函式在它停止時收到通知,並且新父程序無法有效地模擬此通知。

此頁面記錄了 ptrace() 呼叫在 Linux 中當前的工作方式。它的行為在其他 Unix 版本上存在明顯的差異。無論如何,ptrace() 的使用高度依賴於作業系統和體系結構。

SunOS 手冊頁將 ptrace() 描述為“獨特且神秘的”,確實如此。Solaris 2 中存在的基於 proc 的除錯介面以更強大和統一的方式實現了 ptrace() 功能的超集。

返回值

成功時,PTRACE_PEEK* 請求返回請求的資料,而其他請求返回零。發生錯誤時,所有請求都返回 -1,並且 errno 被相應地設定。由於成功 PTRACE_PEEK* 請求返回的值可能為 -1,因此呼叫者必須在這些請求之後檢查 errno 以確定是否發生了錯誤。

錯誤

在具有 2.6 核心標頭檔案的宿主機上,PTRACE_SETOPTIONS 的宣告值與 2.4 的不同。這會導致使用此類標頭檔案編譯的應用程式在 2.4 核心上執行時失敗。如果定義了 PTRACE_OLDSETOPTIONS,可以透過將其重新定義為 PTRACE_OLDSETOPTIONS 來解決此問題。

錯誤

標籤描述
EBUSY (僅限 i386)分配或釋放除錯暫存器時出錯。
EFAULT 嘗試讀取或寫入父程序或子程序記憶體中的無效區域,可能是因為該區域未對映或不可訪問。不幸的是,在 Linux 下,此錯誤的不同變體將或多或少任意地返回 EIO 或 EFAULT。
EINVAL 嘗試設定無效選項。
EIO request 無效,或嘗試讀取或寫入父程序或子程序記憶體中的無效區域,或存在字對齊違規,或在重啟請求期間指定了無效訊號。
EPERM 無法跟蹤指定的程序。這可能是因為父程序許可權不足(所需的許可權是 CAP_SYS_PTRACE);出於顯而易見的原因,非 root 程序無法跟蹤它們無法向其傳送訊號的程序或正在執行 set-user-ID/set-group-ID 程式的程序。或者,該程序可能已經在被跟蹤,或者為 init(PID 1)。
ESRCH 指定的程序不存在,或當前未被呼叫者跟蹤,或未停止(對於需要此條件的請求)。

符合標準

SVr4, 4.3BSD

參見



previous next Printer Friendly

廣告


  

廣告



廣告