[英]How does ptrace work in Linux?
The ptrace
system call allows the parent process to inspect the attached child. ptrace
系统调用允许父进程检查附加的子进程。 For example, in Linux, strace
(which is implemented with the ptrace
system call) can inspect the system calls invoked by the child process. 例如,在Linux中,
strace
(使用ptrace
系统调用实现)可以检查子进程调用的系统调用。
When the attached child process invokes a system call, the ptracing parent process can be notified. 当附加的子进程调用系统调用时,可以通知ptracing父进程。 But how exactly does that happen?
但究竟是怎么发生的呢? I want to know the technical details behind this mechanism.
我想知道这个机制背后的技术细节。
Thank you in advance. 先感谢您。
When the attached child process invokes a system call, the ptracing parent process can be notified.
当附加的子进程调用系统调用时,可以通知ptracing父进程。 But how exactly does that happen?
但究竟是怎么发生的呢?
Parent process calls ptrace
with PTRACE_ATTACH
, and his child calls ptrace
with PTRACE_TRACEME
option. 父进程使用
PTRACE_ATTACH
调用ptrace
,他的子进程使用PTRACE_TRACEME
选项调用ptrace
。 This pair will connect two processes by filling some fields inside their task_struct
( kernel/ptrace.c: sys_ptrace , child will have PT_PTRACED
flag in ptrace
field of struct task_struct
, and pid of ptracer process as parent and in ptrace_entry
list - __ptrace_link
; parent will record child's pid in ptraced
list). 这对将通过填充
task_struct
一些字段来连接两个进程( kernel / ptrace.c:sys_ptrace ,child将在struct task_struct
ptrace
字段中具有PT_PTRACED
标志,ptracer进程的pid作为父级和ptrace_entry
列表中的__ptrace_link
; parent将在ptraced
列表中记录孩子的pid)。
Then strace will call ptrace
with PTRACE_SYSCALL
flag to register itself as syscall debugger, setting thread_flag TIF_SYSCALL_TRACE
in child process's struct thread_info
(by something like set_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
). 然后strace将使用
PTRACE_SYSCALL
标志调用ptrace
将其自身注册为syscall调试器,在子进程的struct thread_info
设置thread_flag TIF_SYSCALL_TRACE
(通过类似set_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
)。 arch/x86/include/asm/thread_info.h
: arch/x86/include/asm/thread_info.h
:
67 /*
68 * thread information flags
69 * - these are process state flags that various assembly files
70 * may need to access ...*/
75 #define TIF_SYSCALL_TRACE 0 /* syscall trace active */
99 #define _TIF_SYSCALL_TRACE (1 << TIF_SYSCALL_TRACE)
On every syscall entry or exit, architecture-specific syscall entry code will check this _TIF_SYSCALL_TRACE
flag (directly in assembler implementation of syscall, for example x86 arch/x86/kernel/entry_32.S
: jnz syscall_trace_entry
in ENTRY(system_call)
and similar code in syscall_exit_work
), and if it is set, ptracer will be notified with signal (SIGTRAP) and child will be temporary stopped. 在每个系统调用条目或出口上,特定于体系结构的系统调用条目代码将检查此
_TIF_SYSCALL_TRACE
标志 (直接在syscall的汇编器实现中,例如x86 arch/x86/kernel/entry_32.S
: ENTRY(system_call)
jnz syscall_trace_entry
和类似的代码) syscall_exit_work
),如果已设置,将通过信号(SIGTRAP)通知ptracer,并且将临时停止子级。 This is done usually in syscall_trace_enter
and syscall_trace_leave
: 这通常在
syscall_trace_enter
和syscall_trace_leave
:
1457 long syscall_trace_enter(struct pt_regs *regs)
1483 if ((ret || test_thread_flag(TIF_SYSCALL_TRACE)) &&
1484 tracehook_report_syscall_entry(regs))
1485 ret = -1L;
1507 void syscall_trace_leave(struct pt_regs *regs)
1531 if (step || test_thread_flag(TIF_SYSCALL_TRACE))
1532 tracehook_report_syscall_exit(regs, step);
The tracehook_report_syscall_*
are actual workers here, they will call ptrace_report_syscall
. tracehook_report_syscall_*
是这里的实际工作者,他们将调用ptrace_report_syscall
。 include/linux/tracehook.h
: include/linux/tracehook.h
:
80 /**
81 * tracehook_report_syscall_entry - task is about to attempt a system call
82 * @regs: user register state of current task
83 *
84 * This will be called if %TIF_SYSCALL_TRACE has been set, when the
85 * current task has just entered the kernel for a system call.
86 * Full user register state is available here. Changing the values
87 * in @regs can affect the system call number and arguments to be tried.
88 * It is safe to block here, preventing the system call from beginning.
89 *
90 * Returns zero normally, or nonzero if the calling arch code should abort
91 * the system call. That must prevent normal entry so no system call is
92 * made. If @task ever returns to user mode after this, its register state
93 * is unspecified, but should be something harmless like an %ENOSYS error
94 * return. It should preserve enough information so that syscall_rollback()
95 * can work (see asm-generic/syscall.h).
96 *
97 * Called without locks, just after entering kernel mode.
98 */
99 static inline __must_check int tracehook_report_syscall_entry(
100 struct pt_regs *regs)
101 {
102 return ptrace_report_syscall(regs);
103 }
104
105 /**
106 * tracehook_report_syscall_exit - task has just finished a system call
107 * @regs: user register state of current task
108 * @step: nonzero if simulating single-step or block-step
109 *
110 * This will be called if %TIF_SYSCALL_TRACE has been set, when the
111 * current task has just finished an attempted system call. Full
112 * user register state is available here. It is safe to block here,
113 * preventing signals from being processed.
114 *
115 * If @step is nonzero, this report is also in lieu of the normal
116 * trap that would follow the system call instruction because
117 * user_enable_block_step() or user_enable_single_step() was used.
118 * In this case, %TIF_SYSCALL_TRACE might not be set.
119 *
120 * Called without locks, just before checking for pending signals.
121 */
122 static inline void tracehook_report_syscall_exit(struct pt_regs *regs, int step)
123 {
...
130
131 ptrace_report_syscall(regs);
132 }
And ptrace_report_syscall
generates SIGTRAP for debugger or strace via ptrace_notify
/ ptrace_do_notify
: 并且
ptrace_report_syscall
通过ptrace_notify
/ ptrace_do_notify
为调试器或strace 生成SIGTRAP :
55 /*
56 * ptrace report for syscall entry and exit looks identical.
57 */
58 static inline int ptrace_report_syscall(struct pt_regs *regs)
59 {
60 int ptrace = current->ptrace;
61
62 if (!(ptrace & PT_PTRACED))
63 return 0;
64
65 ptrace_notify(SIGTRAP | ((ptrace & PT_TRACESYSGOOD) ? 0x80 : 0));
66
67 /*
68 * this isn't the same as continuing with a signal, but it will do
69 * for normal use. strace only continues with a signal if the
70 * stopping signal is not SIGTRAP. -brl
71 */
72 if (current->exit_code) {
73 send_sig(current->exit_code, current, 1);
74 current->exit_code = 0;
75 }
76
77 return fatal_signal_pending(current);
78 }
ptrace_notify
is implemented in kernel/signal.c
, it stops the child and pass sig_info to ptracer: ptrace_notify
在kernel/signal.c
ptrace_notify
中实现,它会停止子进程并将sig_info传递给ptracer:
1961 static void ptrace_do_notify(int signr, int exit_code, int why)
1962 {
1963 siginfo_t info;
1964
1965 memset(&info, 0, sizeof info);
1966 info.si_signo = signr;
1967 info.si_code = exit_code;
1968 info.si_pid = task_pid_vnr(current);
1969 info.si_uid = from_kuid_munged(current_user_ns(), current_uid());
1970
1971 /* Let the debugger run. */
1972 ptrace_stop(exit_code, why, 1, &info);
1973 }
1974
1975 void ptrace_notify(int exit_code)
1976 {
1977 BUG_ON((exit_code & (0x7f | ~0xffff)) != SIGTRAP);
1978 if (unlikely(current->task_works))
1979 task_work_run();
1980
1981 spin_lock_irq(¤t->sighand->siglock);
1982 ptrace_do_notify(SIGTRAP, exit_code, CLD_TRAPPED);
1983 spin_unlock_irq(¤t->sighand->siglock);
1984 }
ptrace_stop
is in the same signal.c
file, line 1839 for 3.13. ptrace_stop
是在同一个signal.c
文件,为3.13行1839年。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.