table of contents
other sections
PTRACE(2) | Linux-Programmierhandbuch | PTRACE(2) |
BEZEICHNUNG¶
ptrace - ProzessverfolgungÜBERSICHT¶
#include <sys/ptrace.h>long ptrace(enum __ptrace_request Anfrage, pid_t Pid, void *Adresse, void *Daten);
BESCHREIBUNG¶
Der Systemaufruf ptrace() stellt ein Mittel bereit, wodurch ein Prozess (der »Verfolger«) die Ausführung eines anderen Prozesses (des »verfolgten Prozesses«) beobachten und steuern kann und seinen Speicher sowie die Register untersuchen und ändern kann. Er wird in erster Linie benutzt, um Fehlersuche mittels Haltepunkten zu implementieren und Systemaufrufe zu verfolgen. Ein verfolgter Prozess muss zuerst an den Verfolger angehängt werden. Anhängen und nachfolgende Befehle bestehen pro Thread: In einem Prozess mit mehreren Threads kann jeder Thread individuell an einen (möglicherweise unterschiedlichen) Verfolger angehängt werden oder nicht angehängt und folglich nicht auf Fehler untersucht werden. Daher bedeutet »verfolgter Prozess« immer »(ein) Thread«, niemals »ein Prozess (mit möglicherweise mehreren Threads)«. Ptrace-Befehle werden immer an einen bestimmten verfolgten Prozess gesandt. Der Aufruf hat folgende Form:ptrace(PTRACE_foo, PID, …)
- PTRACE_TRACEME
- zeigt an, dass dieser Prozess durch seinen Elternprozess verfolgt wird. Ein Prozess sollte diese Anfrage wahrscheinlich nicht stellen, falls sein Elternprozess nicht erwartet, ihn zu verfolgen.( PID, Adresse und Daten werden ignoriert.)
- PTRACE_PEEKTEXT, PTRACE_PEEKDATA
- liest ein »word« an der Stelle Adresse im Speicher des verfolgten Prozesses und gibt das »word« als Ergebnis des ptrace()-Aufrufs zurück. Linux hat keine separaten Adressräume für Text und Daten, daher sind diese beiden Anfragen derzeit gleichwertig. (Das Argument Daten wird ignoriert.)
- PTRACE_PEEKUSER
- Liest ein »word« bei Versatz Adresse im BENUTZERbereich des verfolgten Prozesses, der die Register und andere Informationen über den Prozess enthält (siehe <sys/user.h>). Das »word« wird als Ergebnis des ptrace()-Aufrufs zurückgegeben. Typischerweise muss der Versatz am »word« ausgerichtet sein, obwohl dies je nach Architektur variieren kann. Lesen Sie die ANMERKUNGEN. ( Daten wird ignoriert.)
- PTRACE_POKETEXT, PTRACE_POKEDATA
- kopiert das »word« Daten an die Stelle Adresse im Speicher des verfolgenden Prozesses. Wie bei PTRACE_PEEKTEXT und PTRACE_PEEKDATA sind die beiden Abfragen derzeit gleichwertig.
- PTRACE_POKEUSER
- kopiert das »word« Daten an den Versatz Adresse im BENUTZERbereich des verfolgten Prozesses. Wie für PTRACE_PEEKUSER muss der Versatz am »word« ausgerichtet sein. Um die Integrität des Kernels aufrecht zu erhalten, sind einige Änderungen in BENUTZERbereich nicht erlaubt.
- PTRACE_GETREGS, PTRACE_GETFPREGS
- kopiert die Mehrzweck- beziehungsweise Fließpunktregister des verfolgten Prozesses an die Stelle Daten im Verfolger. Lesen Sie <sys/user.h>, um Informationen über das Format dieser Daten zu erhalten. ( Adresse wird ignoriert.) Beachten Sie, dass auf SPARC-Systemen die Bedeutung von Daten und Adresse umgekehrt ist; daher wird Daten ignoriert und die Register werden an die Adresse Adresse kopiert.
- PTRACE_GETSIGINFO (seit Linux 2.3.99-pre6)
- ruft Informationen über das Signal ab, das den Stopp verursachte. Kopiert eine siginfo_t-Struktur (siehe sigaction(2)) vom verfolgten Prozess an die Stelle Daten im Verfolger. ( Adresse wird ignoriert.)
- PTRACE_SETREGS, PTRACE_SETFPREGS
- kopiert die Mehrzweck- beziehungsweise Fließpunktregister des verfolgten Prozesses von der Adresse Daten im Verfolger. Wie für PTRACE_POKEUSER können einige Änderungen am Mehrzweckregister verboten sein. ( Adresse wird ignoriert.) Beachten Sie, dass auf SPARC-Systemen die Bedeutung von Daten und Adresse umgekehrt ist; daher wird Daten ignoriert und die Register werden von der Adresse Adresse kopiert.
- PTRACE_SETSIGINFO (seit Linux 2.3.99-pre6)
- setzt Signalinformationen: kopiert eine siginfo_t-Struktur von der Adresse Daten vom verfolgenden zum verfolgten Prozess. Dies wird nur Signale betreffen, die normalerweise an den verfolgten Prozess zugestellt würden und vom Verfolger abgefangen wurden. Es könnte schwierig werden, diese normalen Signale von künstlichen Signalen zu unterscheiden, die von ptrace() selbst generiert wurden. ( Adresse wird ignoriert.)
- PTRACE_SETOPTIONS (seit Linux 2.4.6; siehe FEHLER für Vorsichtsmaßnahmen)
- setzt Ptrace-Optionen von Daten. (Adresse wird ignoriert.) Daten wird als Bit in der Maske der Optionen interpretiert, die durch die folgenden Schalter angegeben wird:
- PTRACE_O_TRACESYSGOOD (seit Linux 2.4.6)
- Wenn Systemaufrufe abgefangen werden, wird Bit 7 in der Signalnummer gesetzt (d.h. SIGTRAP | 0x80 geschickt). Dies erleichtert es dem Verfolger, den Unterschied zwischen normalen, abgefangenen Signalen und denen, die durch einen Systemaufruf verursacht wurden, mitzuteilen. ( PTRACE_O_TRACESYSGOOD funktioniert möglicherweise nicht auf allen Architekturen.)
- PTRACE_O_TRACEFORK (seit Linux 2.5.46)
- stoppt den verfolgten Prozess beim nächsten Aufruf von
fork(2) und startet die Verfolgung des neuen Prozesszweiges, der
mit einem SIGSTOP starten wird. Ein waitpid(2) durch den
Verfolger wird einen Statuswert wie diesen zurückgeben:
status>>8 == (SIGTRAP | (PTRACE_EVENT_FORK<<8))
- PTRACE_O_TRACEVFORK (seit Linux 2.5.46)
- stoppt den verfolgten Prozess beim nächsten Aufruf von
vfork(2) und startet automatisch die Verfolgung des neuen
»vfork«-Prozesszweiges, der mit einem SIGSTOP starten
wird. Ein waitpid(2) durch den Verfolger wird einen
Statuswert wie diesen zurückgeben:
status>>8 == (SIGTRAP | (PTRACE_EVENT_VFORK<<8))
- PTRACE_O_TRACECLONE (seit Linux 2.5.46)
- stoppt den verfolgten Prozess beim nächsten Aufruf von
clone(2) und startet automatisch die Verfolgung des neu geklonten
Prozesses, der mit einem SIGSTOP starten wird. Ein
waitpid(2) durch den Verfolger wird einen Statuswert wie
diesen zurückgeben:
status>>8 == (SIGTRAP | (PTRACE_EVENT_CLONE<<8))
- Diese Option kann nicht in allen Fällen clone(2)-Aufrufe abfangen. Falls der verfolgte Prozess clone(2) mit dem Schalter CLONE_VFORK aufruft, wird stattdessen PTRACE_EVENT_VFORK geschickt, wenn PTRACE_O_TRACEVFORK gesetzt ist; andernfalls wird PTRACE_EVENT_FORK geschickt, wenn der verfolgte Prozess clone(2) mit dem auf SIGCHLD gesetzten Beendigungssignal aufgerufen wird, falls PTRACE_O_TRACEFORK gesetzt ist.
- PTRACE_O_TRACEEXEC (seit Linux 2.5.46)
- stoppt den verfolgten Prozess beim nächsten
execve(2). Ein waitpid(2) durch den Verfolger wird einen
Statuswert wie diesen zurückgeben:
status>>8 == (SIGTRAP | (PTRACE_EVENT_EXEC<<8))
- PTRACE_O_TRACEVFORKDONE (seit Linux 2.5.60)
- stoppt den verfolgten Prozess bei Vollendung des
nächsten vfork(2). Ein waitpid(2) durch den Verfolger
wird einen Statuswert wie diesen zurückgeben:
status>>8 == (SIGTRAP | (PTRACE_EVENT_VFORK_DONE<<8))
- PTRACE_O_TRACEEXIT (seit Linux 2.5.60)
- stoppt den verfolgten Prozess beim Beenden. Ein
waitpid(2) durch den Verfolger wird einen Statuswert wie
diesen zurückgeben:
status>>8 == (SIGTRAP | (PTRACE_EVENT_EXIT<<8))
- Der verfolgte Prozess wird frühzeitig während des Beendens gestoppt, wenn die Register noch verfügbar sind, was es dem Verfolger ermöglicht, zu sehen, wo das Beenden veranlasst wurde, wohingegen die normale Benachrichtigung über die Beendigung geschickt wird, wenn der Prozess das Beenden abgeschlossen hat. Auch wenn der Kontext verfügbar ist, kann der Verfolger das Beenden an diesem Punkt nicht mehr verhindern.
- PTRACE_GETEVENTMSG (seit Linux 2.5.46)
- eine Nachricht (als unsigned long) über das Ptrace-Ereignis abfragen, das einfach so auftrat und es an die Adresse Daten im Verfolger platzieren. Für PTRACE_EVENT_EXIT ist dies der Exit-Status des verfolgten Prozesses. Für PTRACE_EVENT_FORK, PTRACE_EVENT_VFORK und PTRACE_EVENT_CLONE ist dies die PID des neuen Prozesses. ( Adresse wird ignoriert.)
- PTRACE_CONT
- startet den gestoppten, verfolgten Prozess erneut. Falls Daten nicht Null ist, wird es als Nummer des Signals interpretiert, das an den verfolgten Prozess geschickt wird; andernfalls wird kein Signal geschickt. Dadurch kann der Verfolger zum Beispiel steuern, ob ein Signal an den verfolgten Prozess geschickt wird oder nicht. ( Adresse wird ignoriert.)
- PTRACE_SYSCALL, PTRACE_SINGLESTEP
- startet den gestoppten, verfolgten Prozess wie für PTRACE_CONT, arrangiert aber, dass der verfolgte Prozess beim nächsten Eintritt oder einem Systemaufruf beziehungsweise nach der Ausführung einer einzelnen Anweisung gestoppt wird. (Der verfolgte Prozess wird auch, wie üblich, über den Empfang des Signals gestoppt.) Aus der Sicht des Verfolgers scheint es, als ob der verfolgte Prozess durch Empfang eines SIGTRAP gestoppt wurde. Daher gibt es zum Beispiel für PTRACE_SYSCALL die Idee, beim ersten Stopp die Argumente des Systemaufrufs zu prüfen, dann einen anderen PTRACE_SYSCALL zu schicken und den Rückgabewert des Systemaufrufs am zweiten Stopp zu prüfen. Das Argument Daten wird wie für PTRACE_CONT behandelt. ( Adresse wird ignoriert.)
- PTRACE_SYSEMU, PTRACE_SYSEMU_SINGLESTEP (seit Linux 2.6.14)
- für PTRACE_SYSEMU beim nächsten Eintritt für den Systemaufruf, der nicht ausgeführt wird, fortfahren und stoppen; für PTRACE_SYSEMU_SINGLESTEP das gleiche tun, aber in einem einzigen Schritt, wenn es sich nicht um einen Systemaufruf handelt. Dieser Aufruf wird von Programmen wie »User Mode Linux« verwandt, die die Systemaufrufe des verfolgten Prozesses emulieren wollen. Das Argument Daten wird wie für PTRACE_CONT behandelt. ( Adresse wird ignoriert; nicht auf allen Architekturen unterstützt)
- PTRACE_KILL
- sendet dem verfolgten Prozess ein SIGKILL, um ihn zu beenden. ( Adresse und Daten werden ignoriert.)
- Diese Transaktion ist überholt; benutzen Sie sie nicht! Senden Sie stattdessen ein SIGKILL direkt mittels kill(2) oder tgkill(2). Das Problem bei PTRACE_KILL ist, dass es verlangt, dass sich der verfolgte Prozess in eine Signallieferstopp befindet, andernfalls funktioniert es möglicherweise nicht (d.h. es könnte komplett erfolgreich sein, würde aber den verfolgten Prozess killen). Im Gegensatz dazu hat das direkte Senden von einem SIGKILL keine derartige Beschränkung.
- PTRACE_ATTACH
- hängt an den Prozess, der durch PID angegeben wird, an, lässt ihn zum verfolgten Prozess des aufrufenden Prozesses werden. Dem verfolgten Prozess wird ein SIGSTOP gesandt, er wird aber nicht notwendigerweise durch die Vollendung dieses Aufrufs gestoppt; benutzen Sie waitpid(2), um auf das Stoppen des verfolgten Prozesses zu warten. Lesen Sie den Unterabschnitt »Anhängen und Loslösen«, um zusätzliche Informationen zu erhalten. ( Adresse und Daten werden ignoriert.)
- PTRACE_DETACH
- startet den gestoppten, verfolgten Prozess wie für PTRACE_CONT, löst ihn aber zuerst vom Prozess ab. Unter Linux kann ein verfolgter Prozess auf diese Art abgelöst werden, ohne Rücksicht darauf zu nehmen, welche Methode zum Starten der Verfolgung benutzt wurde.( Adresse wird ignoriert.)
Tod unter Ptrace¶
Wenn ein Prozess (der möglicherweise aus mehreren Threads besteht) ein killendes Signal erhält (eines, dessen Einstellung auf SIG_DFL gesetzt ist und dessen Standardaktion das Killen des Prozesses ist) werden alle Threads beendet. Verfolgte Prozesse melden ihren Tod an ihre(n) Verfolger. Die Benachrichtigung über dieses Ereignis wird über waitpid(2) zugestellt. Beachten Sie, dass das killende Signal zuerst einen Signallieferstopp (auf nur einen verfolgten Prozess) verursachen wird und nur nachdem es durch den Verfolger eingespeist wurde (oder nachdem es an einen nicht verfolgten Thread versandt wurde), wird der Tod von dem Signal auf alle verfolgten Prozesse innerhalb eines Prozesses mit mehreren Threads ausgehen. (Der Begriff »Signallieferstopp« wird nachfolgend erklärt.) SIGKILL erzeugt keinen »Signallieferstopp«. Daher kann der Verfolger es nicht unterdrücken. SIGKILL killt sogar innerhalb von Systemaufrufen (der Systemaufrufbeendigungsstopp wird nicht vorrangig vor dem Tod durch SIGKILL erzeugt). Der reine Effekt besteht darin, dass SIGKILL den Prozess (all seine Threads) immer killt, sogar dann, wenn einige Threads des Prozesses verfolgt werden. Wenn der verfolgte Prozess _exit(2) aufruft, meldet er seinem Verfolger seinen Tod. Andere Threads werden nicht beeinflusst. Wenn irgendein Thread exit_group(2) ausführt, meldet jeder verfolgte Prozess in dessen Gruppe seinen Tod an den Verfolger. Falls die Option PTRACE_O_TRACEEXIT aktiv ist, wird PTRACE_EVENT_EXIT vor dem tatsächlichen Tod auftreten. Dies wird angewandt, um mittels exit(2), exit_group(2) und Todessignalen (ausgenommen SIGKILL) zu beenden und wenn Threads bei execve(2) in einem Prozess mit mehreren Threads zerrissen werden. Der Verfolger kann nicht abschätzen, ob der von Ptrace gestoppte Prozess existiert. Es gibt mehrere Szenarien, in denen der verfolgte Prozess sterben kann, während er gestoppt ist (wie SIGKILL). Daher muss der Verfolger vorbereitet werden, bei allen Ptrace-Transaktionen einen ESRCH-Fehler zu handhaben. Leider wird der gleiche Fehler zurückgegeben, falls der verfolgte Prozess existiert, aber nicht von Ptrace gestoppt wurde (für Befehle, die einen gestoppten, verfolgten Prozess erfordern) oder falls er nicht durch den Prozess verfolgt wird, der den Ptrace-Aufruf abschickte. Der Verfolger muss den Überblick über den »laufend«-/»gestoppt«-Status des verfolgten Prozesses behalten und ESRCH nur dann als »verfolgter Prozess starb unerwartet« interpretieren, falls er weiß, dass der verfolgte Prozess beobachtet wurde, um in einen Ptrace-stopp einzutreten. Beachten Sie, dass es keine Garantie gibt, dass waitpid(WNOHANG) zuverlässig den Todesstatus des verfolgten Prozesses meldet, falls eine Ptrace-Transaktion ESRCH zurückgibt. waitpid(WNOHANG) könnte stattdessen 0 zurückgeben. In anderen Worten kann es sein, dass der verfolgte Prozess »noch nicht vollständig tot« ist, aber bereits Ptrace-Anfragen ablehnt. Der Verfolger kann nicht abschätzen, ob der verfolgte Prozess immer sein Leben durch melden von WIFEXITED(status) oder WIFSIGNALED(status) aushaucht. Es gibt Fälle, in denen dies nicht geschieht. Falls ein Thread, der nicht führender Thread der Thread-Gruppe ist, zum Beispiel ein execve(2) ausführt, verschwindet er; seine PID wird nie wieder gesehen und alle nachfolgenden Ptrace-Stopps werden unter der PID des führenden Threads der Thread-Gruppe gemeldet.Gestoppt-Status¶
Ein verfolgter Prozess kann zwei Status haben: laufend oder gestoppt. Es gibt viele Arten von Status, wenn ein verfolgter Prozess gestoppt wurde und in Ptrace-Diskussionen werden sie oft durcheinandergebracht. Daher ist es wichtig, präzise Begriffe zu verwenden. In dieser Handbuchseite wird jeder Gestoppt-Status, in dem der verfolgte Prozess bereit ist, Ptrace-Befehle vom Verfolger zu akzeptieren, Ptrace-Stopp genannt. Ptrace-Stopps können weiter in Signallieferstopp, Gruppenstopp und so fort unterteilt werden. Diese gestoppten Status werden nachfolgend im Detail beschrieben. Wenn der laufende, verfolgte Prozess in Ptrace-Stopp eintritt, benachrichtigt er seinen Verfolger mittels waitpid(2) (oder einem anderen der »wait«-Systemaufrufe). Meistens geht diese Handbuchseite davon aus, dass der Verfolger wartet mit:PID = waitpid(pid_or_minus_1, &status, __WALL); Mit Ptrace-Stopp angehaltene, verfolgte Prozesse werden als Rückgaben mit PID größer als 0 und »WIFSTOPPED(status) true« gemeldet. Der Schalter __WALL enthält nicht die Schalter WSTOPPED und WEXITED, impliziert aber ihre Funktionalität. Es wird nicht empfohlen, den Schalter WCONTINUED zu setzen, wenn waitpid(2) aufgerufen wird: Der Status »continued« gilt pro Prozess und ihn zu verbrauchen, kann den echten Elternprozess des verfolgten Prozesses verwirren. Die Benutzung des Schalters WNOHANG könnte waitpid(2) veranlassen, 0 zurückzugeben (»nocht keine Warteergebnisse verfügbar«), sogar dann, wenn der Verfolger weiß, dass dort eine Benachrichtigung sein soll. Beispiel:
errno = 0; ptrace(PTRACE_CONT, pid, 0L, 0L); if (errno == ESRCH) { /* verfolgter Prozess ist tot */ r = waitpid(tracee, &status, __WALL | WNOHANG); /* r kann hier immer noch 0 sein! */ }Die folgenden Arten von Ptrace-Stopps existieren: Signallieferstopps, Gruppenstopps, PTRACE_EVENT-Stopps und Systemaufrufstopps. Sie alle werden von waitpid(2) mit »WIFSTOPPED(status) true« gemeldet. Sie könnten durch Untersuchen des Wertes status>>8 unterschieden werden und, falls es eine Unklarheit im Wert gibt, durch Abfragen von PTRACE_GETSIGINFO. (Hinweis: Das Makro WSTOPSIG(status) kann nicht für diese Untersuchung verwandt werden, da es den Wert (status>>8) & 0xff zurückgibt.)
Signallieferstopp¶
Wenn ein Prozess (möglicherweise mit mehreren Threads) ein Signal außer SIGKILL empfängt, wählt der Kernel einen beliebigen Thread aus, der das Signal handhabt. (Falls das Signal mit tgkill(2) erzeugt wurde, kann der Ziel-Thread explizit durch den Aufrufenden ausgewählt werden.) Falls der ausgewählte Thread verfolgt wird, tritt er in einen Signallieferstopp ein. An diesem Punkt wird das Signal noch nicht an den Prozess zugestellt und kann durch den Verfolger unterdrückt werden. Falls der Verfolger das Signal nicht unterdrückt, übergibt er das Signal bei der nächsten Ptrace-Neustartanfrage an den verfolgten Prozess. Dieser zweite Schritt der Signalzustellung wird in dieser Handbuchseite Signaleinspeisung genannt. Beachten Sie, dass, falls das Signal blockiert ist, der Signallieferstopp nicht auftritt, bis die Blockade des Signals aufgehoben wurde, mit der üblichen Ausnahme, dass SIGSTOP nicht blockiert werden kann. Der Signallieferstopp wird vom Verfolger als waitpid(2) beobachtet und kehrt mit » WIFSTOPPED(status) true« mit dem Signal zurück, das von WSTOPSIG(status) zurückgegeben wurde. Falls das Signal SIGTRAP ist, könnte dies eine andere Art eines Ptrace-Stopps sein; Einzelheiten finden Sie in den Abschnitten »Systemaufrufstopps« und »execve« unterhalb. Falls WSTOPSIG(status) ein stoppendes Signal zurückgibt, könnte dies ein Gruppenstopp sein; siehe unten.Signaleinspeisung und -unterdrückung¶
Nachdem der Signallieferstopp durch den Verfolger beobachtet wurde, sollte der Verfolger den verfolgten Prozess mit dem Aufrufptrace(PTRACE_restart, PID, 0, Signal) neu starten, wobei PTRACE_restart einer der neu startenden Ptrace-Anfragen ist. Falls Signal 0 ist, wird das Signal nicht zugestellt. Andernfalls wird das Signal Signal zugestellt. Diese Transaktion wird in dieser Handbuchseite Signaleinspeisung genannt, um sie vom Signallieferstopp zu unterscheiden. Der Signalwert kann sich vom Wert WSTOPSIG(status) unterschieden: Der Verfolger kann veranlassen, dass ein anderes Signal eingespeist wird. Beachten Sie, dass ein unterdrücktes Signal immer noch Systemaufrufe verursacht, um vorzeitig zurückzukehren. In diesem Fall werden Systemaufrufe neu gestartet: Der Verfolger wird den verfolgten Prozess beobachten, um den unterbrochenen Systemaufruf neu auszuführen (oder den Systemaufruf restart_syscall(2) für wenige Systemaufrufe, die unterschiedliche Mechanismen zum erneuten Starten verwenden), falls der Verfolger PTRACE_SYSCALL benutzt. Sogar Systemaufrufe (wie poll(2)), die nach einem Signal nicht mehr neu startbar sind, werden nach dem Unterdrücken des Signals neu gestartet werden. Es existieren jedoch einige Kernelfehler, die zum Fehlschlagen einiger Systemaufrufe mit EINTR führen, sogar, wenn kein beobachtbares Sygnal in den verfolgten Prozess eingespeist wurde. Es wird nicht garantiert, dass beim Neustarten von Ptrace-Befehlen, die in anderen Ptrace-Stopps als Signallieferstopps angestoßen wurden,ein Signal eingespeist wird, nicht einmal, wenn Signal nicht Null ist. Es wird kein Fehler gemeldet; ein Signal ungleich Null könnte einfach ignoriert werden. Ptrace-Benutzer sollten nicht versuchen, auf diese Art »ein neues Signal zu erzeugen«: Benutzen Sie stattdessen tgkill(2). Die Tatsache, dass Signaleinspeisungsanfragen beim erneuten Starten des verfolgten Prozesses nach Ptrace-Stopps, die keine Signallieferstopps sind, ignoriert werden können, ist ein Grund für Verwirrung bei Ptrace-Benutzern. Ein typisches Szenario ist, dass der Verfolger Gruppenstopps beobachtet, sie fälschlicherweise für Signallieferstopps hält und den verfolgen Prozess mit
ptrace(PTRACE_rest, PID, 0, Stoppsignal)
Gruppenstopp¶
Wenn ein Prozess (der möglicherweise aus mehreren Threads besteht) ein Stoppsignal empfängt, werden alle Threads gestoppt. Falls einige Threads verfolgt werden, treten sie in eine Thread-Gruppe ein. Beachten Sie, dass das Stoppsignal zuerst einen Signallieferstopp verursachen wird (nur auf den verfolgten Prozess) und nur, nachdem es durch den Verfolger eingespeist wurde (oder nachdem es an einen Thread geschickt wurde, der nicht verfolgt wird), wird der Gruppenstopp auf alle verfolgten Prozesse innerhalb eines Prozesses aus mehreren Threads eingeleitet. Wie üblich meldet jeder verfolgte Prozess seinen Gruppenstopp separat an den entsprechenden Verfolger. Der Gruppenstopp wird vom Verfolger als waitpid(2) beobachtet und kehrt mit » WIFSTOPPED(status) true« mit dem Stoppsignal zurück, das über WSTOPSIG(status) verfügbar ist. Dasselbe Ergebnis wird von einigen anderen Klassen von Ptrace-Stopps zurückgegeben, daher ist die empfohlene Vorgehensweise, folgenden Aufruf zu tätigen:ptrace(PTRACE_GETSIGINFO, PID, 0, &siginfo) Der Aufruf kann vermieden werden, falls das Signal nicht SIGSTOP, SIGTSTP, SIGTTIN oder SIGTTOU ist. Nur diese vier Signale sind Stoppsignale. Falls der Verfolger etwas anderes sieht, kann es kein Gruppenstopp sein. Andernfalls benötigt der Verfolger den Aufruf PTRACE_GETSIGINFO. Falls PTRACE_GETSIGINFO mit EINVAL fehlschlägt, ist es definitiv ein Gruppenstopp. (Andere Fehlerkodes wie ESRCH (»kein derartiger Prozess«) sind möglich, falls ein SIGKILL den verfolgten Prozess gekillt hat. Ab Kernel 2.6.38 wird der verfolgte Prozess, nachdem der Verfolger den Ptrace-Stopp des verfolgten Prozesses sieht und bis er neu startet oder ihn killt, nicht laufen und keine Benachrichtigungen an den Verfolger senden (außer dem Tod durch SIGKILL), nicht einmal, wenn der Verfolger in einen anderen waitpid(2)-Aufruf gelangt. Das im vorhergehenden Absatz beschriebene Verhalten verursacht ein Problem bei transparenter Behandlung von Stoppsignalen. Falls der Verfolger den verfolgten Prozess nach einem Gruppenstopp neu startet, wird das Stoppsignal effektiv ignoriert —der verfolgte Prozess bleibt nicht gestoppt, er läuft. Falls der Verfolger den verfolgten Prozess neu startet, bevor er in das nächste waitpid(2) eintritt, werden zukünftige SIGCONT-Signale nicht an den Verfolger gemeldet. Dies würde dazu führen, dass die SIGCONT-Signale keine Auswirkungen auf den verfolgten Prozess haben.
PTRACE_EVENT-Stopps¶
Falls der Verfolger PTRACE_O_TRACE_*-Optionen setzt, wird der verfolgte Prozess in PTRACE_EVENT-Stopps genannte Stopps gelangen. PTRACE_EVENT-Stopps werden durch den Verfolger als als waitpid(2) beobachtet, kehren mit » WIFSTOPPED(status) true« zurück und WSTOPSIG(status) gibt SIGTRAP zurück. Es wird ein zusätzliches Bit in das höhere Bit des Status (Datentyp Word) gesetzt: Der Wert status>>8 wird wie folgt sein:(SIGTRAP | PTRACE_EVENT_foo << 8).
- PTRACE_EVENT_VFORK
- stoppt vor dem Zurückkehren von vfork(2) oder clone(2) mit dem Schalter CLONE_VFORK. Wenn der verfolgte Prozess nach diesem Stopp fortgeführt wird, wird er auf das Beenden/Ausführen des Kindprozesses warten, bevor er mit seiner Ausführung fortfährt (in anderen Worten, das übliche Verhalten auf vfork(2)).
- PTRACE_EVENT_FORK
- stoppt vor dem Zurückkehren von fork(2) oder clone(2) mit dem auf SIGCHLD gesetzten Beendigungssignal.
- PTRACE_EVENT_CLONE
- stoppt vor dem Zurückkehren von clone(2).
- PTRACE_EVENT_VFORK_DONE
- stoppt vor dem Zurückkehren von vfork(2) oder clone(2) mit dem Schalter CLONE_VFORK, aber nachdem die Blockade dieses verfolgten Prozesses durch Beenden oder Ausführung aufgehoben wurde.
- PTRACE_EVENT_EXEC
- stoppt vor dem Zurückkehren von execve(2). Ab Linux 3.0, gibt PTRACE_GETEVENTMSG die vorherige Thread-Kennung zurück.
- PTRACE_EVENT_EXIT
- stoppt vor dem Beenden (einschließlich des Todes aus exit_group(2)), dem Signaltod oder endet, verursacht durch execve(2), in einem Prozess aus mehreren Threads. PTRACE_GETEVENTMSG gibt den Exit-Status zurück. Register können untersucht werden (solange nicht »wirklich« beendet wird). Der verfolgte Prozess ist immer noch lebendig; er benötigt zum Fertigstellen des Beendens PTRACE_CONT oder PTRACE_DETACH.
Systemaufrufstopps¶
Falls der verfolgte Prozess durch PTRACE_SYSCALL neu gestartet wurde, gerät der verfolgte Prozess in einen Systemaufrufeintrittsstopp kurz vor dem Eintritt in irgendeinen Systemaufruf. Falls der Verfolger den verfolgten Prozess mit PTRACE_SYSCALL neu startet, gerät der verfolgte Prozess in einen Systemaufrufbeendigungsstopp, wenn der Systemaufruf beendet ist oder falls er durch ein Signal unterbrochen wurde. (Sprich, der Signallieferstopp tritt nie zwischen Systemaufrufeintrittsstopp und Systemaufrufbeendigungsstopp auf; er findet nach dem Systemaufrufbeendigungsstopp statt.) Andere Möglichkeiten sind, dass der verfolgte Prozess in einem PTRACE_EVENT-Stopp stoppen könnte, endet (falls er in _exit(2) oder exit_group(2) eintritt), durch SIGKILL gekillt wird oder leise stirbt (falls er die Thread-Gruppe anführt, kommt das execve(2) in einem anderen Thread vor und der Thread wird nicht vom selben Verfolger verfolgt; diese Situation wird später besprochen). Systemaufrufeintrittsstopp und Systemaufrufbeendigungsstopp werden vom Verfolger als als waitpid(2) beobachtet, kehren mit » WIFSTOPPED(status) true« zurück und WSTOPSIG(status) gibt SIGTRAP zurück. Falls die Option PTRACE_O_TRACESYSGOOD durch den Verfolger gesetzt wurde, wird WSTOPSIG(status) den Wert (SIGTRAP | 0x80) zurückgeben. Systemaufrufstopps können von Signallieferstopps mit SIGTRAP durch Abfrage von PTRACE_GETSIGINFO für die folgenden Fälle unterschieden werden:- si_code <= 0
- SIGTRAP wurde mit einem Ergebnis einer Userspace-Aktion, zum Beispiel einem Systemaufruf (( tgkill(2), kill(2), sigqueue(3), etc.), Ablauf eines POSIX-Zeitnehmers, Statusänderung einer POSIX-Nachrichtenwarteschlange oder Vervollständigung einer asynchronen E/A-Anfrage geliefert.
- si_code == SI_KERNEL (0x80)
- SIGTRAP wurde vom Kernel gesandt.
- si_code == SIGTRAP or si_code == (SIGTRAP|0x80)
- Dies ist ein Systemaufrufstopp.
PTRACE_SINGLESTEP-, PTRACE_SYSEMU-, PTRACE_SYSEMU_SINGLESTEP-Stopps¶
[Einzelheiten dieser Arten von Stopps sind noch nicht dokumentiert.]Benachrichtigende und neustartende Ptrace-Befehle¶
Die meisten Ptrace-Befehle (alle außer PTRACE_ATTACH, PTRACE_TRACEME und PTRACE_KILL) erfordern, dass der verfolgte Prozess in einem Ptrace-Stopp ist, andernfalls scheitern sie mit ESRCH. Wenn der verfolgte Prozess im Ptrace-Stopp ist, kann der Verfolger Daten des verfolgten Prozesses mittels benachrichtigenden Befehlen lesen und schreiben. Diese Befehle belassen den verfolgten Prozess im Status Ptrace-gestoppt:ptrace(PTRACE_PEEKTEXT/PEEKDATA/PEEKUSER, PID, Adresse, 0); ptrace(PTRACE_POKETEXT/POKEDATA/POKEUSER, PID, Adresse, long_val); ptrace(PTRACE_GETREGS/GETFPREGS, PID, 0, &struct); ptrace(PTRACE_SETREGS/SETFPREGS, PID, 0, &struct); ptrace(PTRACE_GETSIGINFO, PID, 0, &siginfo); ptrace(PTRACE_SETSIGINFO, PID, 0, &siginfo); ptrace(PTRACE_GETEVENTMSG, PID, 0, &long_var); ptrace(PTRACE_SETOPTIONS, PID, 0, PTRACE_O_flags);Beachten Sie, dass einige Fehler nicht gemeldet wurden. Das Setzen des Informationssignals ( siginfo) hat zum Beispiel in einigen Ptrace-Stopps möglicherweise keine Auswirkungen, der Aufruf kann jedoch erfolgreich sein (0 zurückgeben und errno nicht setzen); Abfragen von PTRACE_GETEVENTMSG könnte erfolgreich sein und einen zufälligen Wert zurückgeben, falls der aktuelle Ptrace-Stopp nicht dokumentiert ist, um eine aussagekräftige Ereignisnachricht zuückzugeben. Der Aufruf
ptrace(PTRACE_SETOPTIONS, PID, 0, PTRACE_O_flags);
ptrace(Befehl, PID, 0, Signal); wobei Befehl PTRACE_CONT, PTRACE_DETACH, PTRACE_SYSCALL, PTRACE_SINGLESTEP, PTRACE_SYSEMU oder PTRACE_SYSEMU_SINGLESTEP ist. Falls der verfolgte Prozess sich im Signallieferstopp befindet, ist Signal das Signal, das eingespeist wird (falls es ungleich Null ist). Andernfalls kann Signal ignoriert werden. (Wenn ein verfolgter Prozess von einem anderen Ptrace-Stopp als dem Signallieferstopp neu gestartet wird, ist die empfohlene Vorgehensweise, 0 in Signal zu übergeben.)
Anhängen und Loslösen¶
Ein Thread kann an den Verfolger angehängt werden mit dem Aufrufptrace(PTRACE_ATTACH, PID, 0, 0);
ptrace(PTRACE_TRACEME, 0, 0, 0);
raise(SIGSTOP);
ptrace(PTRACE_DETACH, PID, 0, Signal);
execve(2) unter Ptrace¶
Wenn ein Thread in einem Prozess mit mehreren Threads execve(2) aufruft, zerstört der Kernel alle anderen Threads im Prozess und setzt die Thread-Kennung des ausführenden Threads auf die Gruppenkennung (Prozesskennung) zurück. (Oder anders ausgedrückt, wenn ein Prozess mit mehreren Threads ein execve(2) bei Vervollständigung des Aufrufs ausführt, scheint es durch das execve(2) im führenden Thread der Prozessgruppe aufzutreten, unabhängig davon, welcher Thread das execve(2) aufrief.) Dieses Zurücksetzen der Thread-Kennung sieht für Verfolger sehr verwirrend aus:- *
- Alle anderen Threads stoppen im PTRACE_EVENT_EXIT-Stopp, falls die Option PTRACE_O_TRACEEXIT eingeschaltet wurde. Dann melden alle anderen Threads außer dem führenden Thread der Gruppe den Tod, als ob sie über _exit(2) mit dem Exit-Kode 0 beendet worden wären.
- *
- Der ausführende, verfolgte Prozess ändert seine Thread-Kennung, während er in dem execve(2) ist. (Denken Sie daran, unter Ptrace ist die von waitpid(2) zurückgegebene oder in Ptrace-Aufrufe gespeiste »PID«, die Thread-Kennung des verfolgten Prozesses.) Sprich, die Thread-Kennung des verfolgten Prozesses wird zurückgesetzt, so dass sie ihrer Prozesskennung entspricht, die dieselbe ist, wie die Thread-Kennung des führenden Threads der Thread-Gruppe.
- *
- Dann kommt es zu einem PTRACE_EVENT_EXEC-Stopp, falls die Option PTRACE_O_TRACEEXEC eingeschaltet wurde.
- *
- Falls der führende Thread der Gruppe seinen PTRACE_EVENT_EXEC-Stopp mittlerweile gemeldet hat, scheint es für den Verfolger, als ob der tote führende Thread »aus dem Nichts wieder auftaucht«. (Hinweis: Der führende Thread der Gruppe meldet den Tod nicht über WIFEXITED(status) bis es mindestens einen lebenden anderen Thread gibt. Dies eliminiert die Möglichkeit, dass der Verfolger ihn sterben und dann erneut erscheinen sieht.) Falls der führende Thread der Gruppe immer noch lebt, könnte dies für den Verfolger so aussehen, als ob der führende Thread der Gruppe von einem anderen Systemaufruf als dem beigetretenen zurückkehrt oder sogar »von einem Systemaufruf zurückkehrt, obwohl er in keinem Systemaufruf war«. Falls der führende Thread der Gruppe nicht verfolgt wurde (oder von einem anderen Verfolger verfolgt wurde), dann wird es während execve(2) so aussehen, als ob er ein verfolgter Prozess des Verfolgers des ausführenden verfolgten Prozesses geworden wäre.
Thread-Kennung == Thread-Gruppenkennung == Prozesskennung. Beispiel: Zwei Thread rufen zur gleichen Zeit execve(2) auf:
*** wir bekommen einen Systemaufrufeintrittsstopp in Thread 1: ** PID1 execve("/bin/foo", "foo" <nicht abgeschlossen …> *** wir liefern PTRACE_SYSCALL für Thread 1 ** *** wir bekommen einen Systemaufrufeintrittsstopp in Thread 2: ** PID2 execve("/bin/bar", "bar" <nicht abgeschlossen …> *** wir liefern PTRACE_SYSCALL für Thread 2 ** *** wir bekommen PTRACE_EVENT_EXEC für PID0, wir liefern PTRACE_SYSCALL ** *** wir bekommen Systemaufrufbeendigungsstopp für PID0: ** PID0 <… execve wieder aufgenommen> ) = 0Falls die Option PTRACE_O_TRACEEXEC für den ausführenden, verfolgten Prozess nicht in Kraft ist, sendet der Kernel ein zusätzliches SIGTRAP an den verfolgten Prozess, nachdem execve(2) zurückgekehrt ist. Dies ist ein gewöhnliches Signal (ähnlich einem, das durch kill -TRAP erzeugt werden kann), keine Spezialart eines Ptrace-Stopps. Unter Einsatz von PTRACE_GETSIGINFO für dieses Signal gibt si_code auf 0 gesetzt ( SI_USER) zurück. Dieses Signal kann durch die Signalmaske blockiert sein und könnte daher (viel) später gesandt werden. Üblicherweise würde der Verfolger dem Anwender dieses zusätzliche SIGTRAP-Signal nach Execve nicht zeigen wollen und seinen Versand an den verfolgten Prozess unterdrücken (falls SIGTRAP auf SIGTRAP gesetzt ist, killt es das Signal). Es ist jedoch nicht einfach zu bestimmen, welches SIGTRAP zu unterdrücken ist. Die empfohlene Herangehensweise ist, die Option PTRACE_O_TRACEEXEC zu setzen und daher dieses zusätzliche SIGTRAP zu unterdrücken.
Echter Elternprozess¶
Die Ptrace-API (miss)braucht die Standard-UNIX-Eltern-/Kindprozess-Signalgebung über waitpid(2). Diese wird benutzt, um den echten Elternprozess zum Stopp des Empfangs mehrerer Arten von waitpid(2)-Benachrichtigungen zu veranlassen, wenn der Kindprozess durch einen anderen Prozess verfolgt wird. Viele dieser Fehler wurden behoben, aber ab Linux 2.6.38 existieren etliche immer noch; siehe FEHLER oberhalb. Ab Linux 2.6.38 wird davon ausgegangen, dass das folgende korrekt funktioniert:- *
- Beenden/Sterben durch Signal wird zuerst an den Verfolger gemeldet, dann, wenn der Verfolger das waitpid(2)-Ergebnis verbraucht, an den echten Elternprozess (an den echten Elternprozess nur, wenn der ganze Prozess aus mehreren Threads existiert). Falls der Verfolger und der echte Elternprozess derselbe Prozess sind, wird der Bericht nur einmal gesandt.
RÜCKGABEWERT¶
Bei Erfolg geben PTRACE_PEEK*-Anfragen die angefragten Daten zurück, während andere Anfragen Null zurückgeben. Bei einem Fehler geben alle Anfragen -1 zurück und errno wird entsprechend gesetzt. Da der Wert, der von einer erfolgreichen PTRACE_PEEK*-Anfrage zurückgegeben wurde, -1 sein könnte, muss der Aufrufende vor dem Aufruf errno leeren und es dann hinterher untersuchen, um festzustellen, ob ein Fehler aufgetreten ist oder nicht.FEHLER¶
- EBUSY
- (nur i386) Es ist beim Reservieren oder der Freigabe eines Debug-Registers ein Fehler aufgetreten.
- EFAULT
- Es gab einen Versuch in einem ungültigen Bereich im Speicher des Verfolgers oder des verfolgten Prozesses zu lesen oder zu schreiben, wahrscheinlich, weil der Bereich nicht abgebildet war oder kein Zugriff möglich war. Unglücklicherweise geben unter Linux mehrere Variationen dieser Störung mehr oder weniger willkürlich EIO oder EFAULT zurück.
- EINVAL
- Es wurde versucht, eine ungültige Option zu setzen.
- EIO
- Abfrage ist ungültig, es wurde versucht, in einem ungültigen Bereich im Speicher des Verfolgers oder des verfolgten Prozesses zu lesen oder zu schreiben, es gab eine Verletzung der Ausrichtung an der »word«-Größe oder es wurde während des Neustarts der Abfrage ein ungültiges Signal angegeben.
- EPERM
- Der angegebene Prozess kann nicht verfolgt werden. Dies könnte daher rühren, dass der Verfolger über unzureichende Privilegien verfügt (die Fähigkeit CAP_SYS_PTRACE wird benötigt); unprivilegierte Prozesse können keine Prozesse verfolgen, denen sie keine Signale senden können oder die SUID-/SGID-Programme ausführen, was naheliegend ist. Alternativ könnte der Prozess bereits verfolgt werden oder (auf Kerneln vor 2.6.26) init(8) (PID 1) sein.
- ESRCH
- Der angegebene Prozess existiert nicht, wird derzeit nicht vom Aufrufenden verfolgt oder ist nicht gestoppt (bei Anfragen, die einen gestoppten verfolgten Prozess erfordern).
KONFORM ZU¶
SVr4, 4.3BSD.ANMERKUNGEN¶
Obwohl Argumente für ptrace() gemäß dem angegebenen Prototypen interpretiert werden, deklariert Glibc derzeit ptrace() als eine variable Funktion mit nur dem festen Anfrage-Argument. Es wird empfohlen, immer vier Argumente anzugeben, sogar dann, wenn die angeforderte Transaktion sie nicht verwendet. Setzen Sie unbenutzte/ignorierte Argumente auf 0L oder (void *) 0. In Linux-Kerneln vor 2.6.26 kann init(8) den Prozess mit der Prozessnummer 1 nicht verfolgen. Das Layout des Speicherinhalts und des BENUTZERbereichs sind ziemlich Betriebsystem- und Architekturspezifisch. Der mitgelieferte Versatz und die zurückgegebenen Daten passen möglicherweise nicht ganz zu der Definition von struct user. Die Größe eines »word« wird durch die Betriebsystemvariante festgelegt (z.B. ist es für ein 32-Bit-Linux 32 Bit). Diese Seite dokumentiert die Möglichkeit, wie der ptrace()-Aufruf derzeit in Linux arbeitet. Sein Verhalten unterscheidet sich auf anderen UNIX-Geschmacksrichtungen deutlich. Auf jeden Fall ist die Benutzung von ptrace() in hohem Grad abhängig vom Betriebssystem und der Architektur.FEHLER¶
Auf Rechnern mit 2.6 Kernel-Headern ist PTRACE_SETOPTIONS mit einem anderen Wert deklariert, als auf einem für 2.4. Dies führt dazu, dass Anwendungen, die mit 2.6-Kernel-Headern kompiliert wurden, bei der Ausführung auf 2.4er Kerneln scheitern. Dies kann durch Neudefinieren von PTRACE_SETOPTIONS zu PTRACE_OLDSETOPTIONS umgangen werden, wenn dies definiert ist. Gruppenstoppbenachrichtigungen werden an der Verfolger gesandt, aber nicht an den echten Elternprozess. Zuletzt auf 2.6.38.6 bestätigt. Falls ein führender Thread einer Gruppe verfolgt und durch den Aufruf von _exit(2) beendet wird, wird es für ihn zu einem PTRACE_EVENT_EXIT-Stopp kommen (falls angefordert), aber die nachfolgende WIFEXITED-Benachrichtigung wird nicht gesandt, bis alle anderen Threads beendet sind. Wie oben erklärt, wird der Tod des führenden Prozesses der Gruppe gemeldet, falls einer der anderen Threads execve(2) aufruft. Falls der ausgeführte Thread nicht durch den Verfolger verfolgt wird, wird der Verfolger niemals erfahren, dass execve(2) auftrat. Eine mögliche Notlösung ist ein PTRACE_DETACH für den führenden Thread der Gruppe, anstatt ihn in diesem Fall neu zu starten. Zuletzt auf 2.6.38.6 bestätigt. Ein SIGKILL-Signal kann immer noch einen PTRACE_EVENT_EXIT-Stopp vor dem tatsächlichen Signaltod verursachen. Dies könnte in Zukunft geändert werden; SIGKILL ist dazu gedacht, Aufgaben immer sofort zu killen, sogar unter Ptrace. Zuletzt auf 2.6.38.6 bestätigt. Einige Systemaufrufe kehren mit EINTR zurück, falls ein Signal an den verfolgten Prozess gesandt, die Auslieferung aber durch den Verfolger unterdrückt wurde. (Dies ist eine ganz typische Transaktion: Sie wird normalerweise von Fehlersuchprogrammen bei jedem Anhängen durchgeführt, um kein fingiertes SIGSTOP einzuleiten.) Ab Linux 3.2.9 werden die folgenden Systemaufrufe beeinflusst (diese Liste ist wahrscheinlich nicht vollständig): epoll_wait(2) und read(2) von einem inotify(7)-Dateideskriptor. Das übliche Anzeichen für diesen Fehler ist, falls Sie einen ruhenden Prozess mit dem Befehlstrace -p <Prozess-ID>
restart_syscall(<... resuming interrupted call ...>_oder
select(6, [5], NULL, [5], NULL_('_' kennzeichnet die Cursor-Position) mehr als eine Zeile beobachten können, zum Beispiel:
clock_gettime(CLOCK_MONOTONIC, {15370, 690928118}) = 0 epoll_wait(4,_Was hier nicht sichtbar ist, ist, dass der Prozess in epoll_wait(2) blockiert wurde, bevor strace(1) an ihn angehängt hat. Das Anhängen verursachte ein epoll_wait(2), um zum User-Space mit dem Fehler EINTR zurückzukehren. In diesem besonderen Fall reagiert das Programm auf EINTR, indem die aktuelle Zeit geprüft und dann epoll_wait(2) erneut ausgeführt wird. (Programme, die keine derartigen »verirrten« EINTR-Fehler erwarten, können sich bei einem strace(1)-Anhängen in unbeabsichtigter Weise verhalten.)
SIEHE AUCH¶
gdb(1), strace(1), clone(2), execve(2), fork(2), gettid(2), sigaction(2), tgkill(2), vfork(2), waitpid(2), exec(3), capabilities(7), signal(7)KOLOPHON¶
Diese Seite ist Teil der Veröffentlichung 3.42 des Projekts Linux- man-pages. Eine Beschreibung des Projekts und Informationen, wie Fehler gemeldet werden können, finden sich unter http://www.kernel.org/doc/man-pages/.ÜBERSETZUNG¶
Die deutsche Übersetzung dieser Handbuchseite wurde von Patrick Rother <krd@gulu.net> und Chris Leick <c.leick@vollbio.de> erstellt.3. August 2012 | Linux |