ptrace如何在Linux中运行?

时间:2014-05-29 07:59:32

标签: c linux debugging strace ptrace

ptrace系统调用允许父进程检查附加的子进程。例如,在Linux中,strace(使用ptrace系统调用实现)可以检查子进程调用的系统调用。

当附加的子进程调用系统调用时,可以通知ptracing父进程。但究竟是怎么发生的呢?我想知道这个机制背后的技术细节。

提前谢谢。

1 个答案:

答案 0 :(得分:30)

  

当附加的子进程调用系统调用时,可以通知ptracing父进程。但究竟是怎么发生的?

父进程使用ptrace调用PTRACE_ATTACH,他的孩子使用ptrace选项调用PTRACE_TRACEME。该对将通过填充task_structkernel/ptrace.c: sys_ptrace内的某些字段来连接两个进程,子PT_PTRACED ptrace字段中的struct task_struct标记和pid ptracer进程为父级和ptrace_entry列表 - __ptrace_link;父级将在ptraced列表中记录子级的pid。)

然后strace将使用ptrace标志调用PTRACE_SYSCALL将自己注册为系统调用调试器,在子进程的TIF_SYSCALL_TRACE中设置thread_flag struct thread_info(类似于set_tsk_thread_flag(child, TIF_SYSCALL_TRACE); )。 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)

在每个系统调用条目或出口上,特定于体系结构的系统调用条目代码将检查此_TIF_SYSCALL_TRACE flag(直接在syscall的汇编程序实现中,例如x86 arch/x86/kernel/entry_32.Sjnz syscall_trace_entry {{1}和syscall_exit_work中的类似代码,如果设置了,ptracer将通过signal(SIGTRAP)通知,child将被临时停止。这通常在syscall_trace_enterENTRY(system_call)

中完成
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); 是这里的实际工作人员,他们会致电tracehook_report_syscall_*include/linux/tracehook.h

ptrace_report_syscall

ptrace_report_syscall generates SIGTRAP用于调试器或strace通过 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 } / ptrace_notify

ptrace_do_notify

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 } kernel/signal.c中实现,它会停止子进程并将sig_info传递给ptracer:

ptrace_notify

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(&current->sighand->siglock); 1982 ptrace_do_notify(SIGTRAP, exit_code, CLD_TRAPPED); 1983 spin_unlock_irq(&current->sighand->siglock); 1984 } 位于同一个ptrace_stop文件中,第1939行为3.13。