tag editar easytag c linux debugging strace ptrace

c - easytag - editar tags mp3 linux



¿Cómo funciona ptrace en Linux? (1)

Cuando el proceso hijo adjunto invoca una llamada al sistema, se puede notificar al proceso padre que realiza el seguimiento. ¿Pero cómo exactamente eso sucede?

El proceso padre llama a ptrace con PTRACE_ATTACH y su hijo llama ptrace con la opción PTRACE_TRACEME . Este par conectará dos procesos llenando algunos campos dentro de task_struct ( kernel / ptrace.c: sys_ptrace , child tendrá el indicador PT_PTRACED en el campo ptrace de struct task_struct , y pid del proceso ptracer como padre y en la lista __ptrace_link - __ptrace_link ; ptraced el pid del niño en la lista de ptraced .

Luego, strace llamará a ptrace con el indicador PTRACE_SYSCALL para registrarse como depurador syscall, configurando thread_flag TIF_SYSCALL_TRACE en la struct thread_info del proceso hijo struct thread_info (por algo como 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)

En cada entrada o salida de syscall, el código de entrada syscall específico de la arquitectura comprobará este indicador _TIF_SYSCALL_TRACE (directamente en la implementación de syscall en ensamblador, por ejemplo x86 arch/x86/kernel/entry_32.S : jnz syscall_trace_entry en ENTRY(system_call) y código similar en syscall_exit_work ), y si está configurado, se notificará a ptracer con la señal (SIGTRAP) y el niño se detendrá temporalmente. Esto se hace generalmente en syscall_trace_enter y 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);

Los tracehook_report_syscall_* son trabajadores reales aquí, llamarán a ptrace_report_syscall . 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 }

Y ptrace_report_syscall genera SIGTRAP para el depurador o strace a través de 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 }

ptrace_notify se implementa en kernel/signal.c , detiene al hijo y pasa sig_info a 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(&current->sighand->siglock); 1982 ptrace_do_notify(SIGTRAP, exit_code, CLD_TRAPPED); 1983 spin_unlock_irq(&current->sighand->siglock); 1984 }

ptrace_stop está en el mismo archivo signal.c , línea 1839 para 3.13.

La ptrace sistema ptrace permite que el proceso padre inspeccione al hijo adjunto. Por ejemplo, en Linux, strace (que se implementa con la ptrace sistema ptrace ) puede inspeccionar las llamadas al sistema invocadas por el proceso hijo.

Cuando el proceso hijo adjunto invoca una llamada al sistema, se puede notificar al proceso padre que realiza el seguimiento. ¿Pero cómo exactamente eso sucede? Quiero saber los detalles técnicos detrás de este mecanismo.

Gracias de antemano.