Alexandre Julliard | 578c100 | 1999-11-14 21:23:21 +0000 | [diff] [blame] | 1 | /* |
| 2 | * Server-side ptrace support |
| 3 | * |
| 4 | * Copyright (C) 1999 Alexandre Julliard |
| 5 | */ |
| 6 | |
| 7 | #include "config.h" |
| 8 | |
| 9 | #include <assert.h> |
| 10 | #include <errno.h> |
| 11 | #include <stdio.h> |
| 12 | #include <signal.h> |
Alexandre Julliard | 42666ee | 1999-11-21 01:10:16 +0000 | [diff] [blame] | 13 | #include <sys/types.h> |
Dimitrie O. Paun | 2af03e4 | 2000-11-29 20:04:09 +0000 | [diff] [blame] | 14 | #ifdef HAVE_SYS_PTRACE_H |
| 15 | # include <sys/ptrace.h> |
| 16 | #endif |
Alexandre Julliard | 578c100 | 1999-11-14 21:23:21 +0000 | [diff] [blame] | 17 | #ifdef HAVE_SYS_WAIT_H |
Dimitrie O. Paun | 2af03e4 | 2000-11-29 20:04:09 +0000 | [diff] [blame] | 18 | # include <sys/wait.h> |
Alexandre Julliard | 578c100 | 1999-11-14 21:23:21 +0000 | [diff] [blame] | 19 | #endif |
| 20 | #include <unistd.h> |
| 21 | |
| 22 | #include "process.h" |
| 23 | #include "thread.h" |
| 24 | |
| 25 | |
| 26 | #ifndef PTRACE_CONT |
| 27 | #define PTRACE_CONT PT_CONTINUE |
| 28 | #endif |
Ove Kaaven | ada7383 | 2001-04-27 18:39:47 +0000 | [diff] [blame] | 29 | #ifndef PTRACE_SINGLESTEP |
| 30 | #define PTRACE_SINGLESTEP PT_STEP |
| 31 | #endif |
Alexandre Julliard | 578c100 | 1999-11-14 21:23:21 +0000 | [diff] [blame] | 32 | #ifndef PTRACE_ATTACH |
| 33 | #define PTRACE_ATTACH PT_ATTACH |
| 34 | #endif |
| 35 | #ifndef PTRACE_DETACH |
| 36 | #define PTRACE_DETACH PT_DETACH |
| 37 | #endif |
| 38 | #ifndef PTRACE_PEEKDATA |
| 39 | #define PTRACE_PEEKDATA PT_READ_D |
| 40 | #endif |
| 41 | #ifndef PTRACE_POKEDATA |
| 42 | #define PTRACE_POKEDATA PT_WRITE_D |
| 43 | #endif |
| 44 | |
Alexandre Julliard | 5f258c6 | 2001-07-14 00:50:30 +0000 | [diff] [blame] | 45 | #ifndef HAVE_SYS_PTRACE_H |
Dimitrie O. Paun | 2af03e4 | 2000-11-29 20:04:09 +0000 | [diff] [blame] | 46 | #define PT_CONTINUE 0 |
| 47 | #define PT_ATTACH 1 |
| 48 | #define PT_DETACH 2 |
| 49 | #define PT_READ_D 3 |
| 50 | #define PT_WRITE_D 4 |
Ove Kaaven | ada7383 | 2001-04-27 18:39:47 +0000 | [diff] [blame] | 51 | #define PT_STEP 5 |
Alexandre Julliard | 5f258c6 | 2001-07-14 00:50:30 +0000 | [diff] [blame] | 52 | inline static int ptrace(int req, ...) { errno = EPERM; return -1; /*FAIL*/ } |
| 53 | #endif /* HAVE_SYS_PTRACE_H */ |
Dimitrie O. Paun | 2af03e4 | 2000-11-29 20:04:09 +0000 | [diff] [blame] | 54 | |
Alexandre Julliard | 5f258c6 | 2001-07-14 00:50:30 +0000 | [diff] [blame] | 55 | static const int use_ptrace = 1; /* set to 0 to disable ptrace */ |
Alexandre Julliard | 578c100 | 1999-11-14 21:23:21 +0000 | [diff] [blame] | 56 | |
Alexandre Julliard | 247b8ae | 1999-12-13 00:16:44 +0000 | [diff] [blame] | 57 | /* handle a status returned by wait4 */ |
| 58 | static int handle_child_status( struct thread *thread, int pid, int status ) |
Alexandre Julliard | 578c100 | 1999-11-14 21:23:21 +0000 | [diff] [blame] | 59 | { |
Alexandre Julliard | 578c100 | 1999-11-14 21:23:21 +0000 | [diff] [blame] | 60 | if (WIFSTOPPED(status)) |
| 61 | { |
| 62 | int sig = WSTOPSIG(status); |
Alexandre Julliard | 247b8ae | 1999-12-13 00:16:44 +0000 | [diff] [blame] | 63 | if (debug_level && thread) |
| 64 | fprintf( stderr, "%08x: *signal* signal=%d\n", (unsigned int)thread, sig ); |
Alexandre Julliard | 578c100 | 1999-11-14 21:23:21 +0000 | [diff] [blame] | 65 | switch(sig) |
| 66 | { |
| 67 | case SIGSTOP: /* continue at once if not suspended */ |
Ove Kaaven | ada7383 | 2001-04-27 18:39:47 +0000 | [diff] [blame] | 68 | if (thread && (thread->process->suspend + thread->suspend)) break; |
| 69 | /* fall through */ |
Alexandre Julliard | 578c100 | 1999-11-14 21:23:21 +0000 | [diff] [blame] | 70 | default: /* ignore other signals for now */ |
Ove Kaaven | ada7383 | 2001-04-27 18:39:47 +0000 | [diff] [blame] | 71 | if (thread && get_thread_single_step( thread )) |
| 72 | ptrace( PTRACE_SINGLESTEP, pid, (caddr_t)1, sig ); |
| 73 | else |
| 74 | ptrace( PTRACE_CONT, pid, (caddr_t)1, sig ); |
Alexandre Julliard | 578c100 | 1999-11-14 21:23:21 +0000 | [diff] [blame] | 75 | break; |
| 76 | } |
Alexandre Julliard | 247b8ae | 1999-12-13 00:16:44 +0000 | [diff] [blame] | 77 | return sig; |
Alexandre Julliard | 578c100 | 1999-11-14 21:23:21 +0000 | [diff] [blame] | 78 | } |
Alexandre Julliard | 247b8ae | 1999-12-13 00:16:44 +0000 | [diff] [blame] | 79 | if (thread && (WIFSIGNALED(status) || WIFEXITED(status))) |
Alexandre Julliard | 578c100 | 1999-11-14 21:23:21 +0000 | [diff] [blame] | 80 | { |
Alexandre Julliard | 247b8ae | 1999-12-13 00:16:44 +0000 | [diff] [blame] | 81 | thread->attached = 0; |
| 82 | thread->unix_pid = 0; |
Alexandre Julliard | 578c100 | 1999-11-14 21:23:21 +0000 | [diff] [blame] | 83 | if (debug_level) |
Alexandre Julliard | 247b8ae | 1999-12-13 00:16:44 +0000 | [diff] [blame] | 84 | { |
| 85 | if (WIFSIGNALED(status)) |
| 86 | fprintf( stderr, "%08x: *exited* signal=%d\n", |
| 87 | (unsigned int)thread, WTERMSIG(status) ); |
| 88 | else |
| 89 | fprintf( stderr, "%08x: *exited* status=%d\n", |
| 90 | (unsigned int)thread, WEXITSTATUS(status) ); |
| 91 | } |
Alexandre Julliard | 578c100 | 1999-11-14 21:23:21 +0000 | [diff] [blame] | 92 | } |
Alexandre Julliard | 247b8ae | 1999-12-13 00:16:44 +0000 | [diff] [blame] | 93 | return 0; |
| 94 | } |
| 95 | |
| 96 | /* handle a SIGCHLD signal */ |
| 97 | void sigchld_handler() |
| 98 | { |
| 99 | int pid, status; |
| 100 | |
| 101 | for (;;) |
Alexandre Julliard | 578c100 | 1999-11-14 21:23:21 +0000 | [diff] [blame] | 102 | { |
Alexandre Julliard | 247b8ae | 1999-12-13 00:16:44 +0000 | [diff] [blame] | 103 | if (!(pid = wait4( -1, &status, WUNTRACED | WNOHANG, NULL ))) break; |
| 104 | if (pid != -1) handle_child_status( get_thread_from_pid(pid), pid, status ); |
| 105 | else break; |
Alexandre Julliard | 578c100 | 1999-11-14 21:23:21 +0000 | [diff] [blame] | 106 | } |
Alexandre Julliard | 247b8ae | 1999-12-13 00:16:44 +0000 | [diff] [blame] | 107 | } |
| 108 | |
| 109 | /* wait for a ptraced child to get a certain signal */ |
| 110 | void wait4_thread( struct thread *thread, int signal ) |
| 111 | { |
| 112 | int res, status; |
| 113 | |
| 114 | do |
| 115 | { |
| 116 | if ((res = wait4( thread->unix_pid, &status, WUNTRACED, NULL )) == -1) |
| 117 | { |
| 118 | perror( "wait4" ); |
| 119 | return; |
| 120 | } |
| 121 | res = handle_child_status( thread, res, status ); |
| 122 | } while (res && res != signal); |
Alexandre Julliard | 578c100 | 1999-11-14 21:23:21 +0000 | [diff] [blame] | 123 | } |
| 124 | |
| 125 | /* attach to a Unix thread */ |
| 126 | static int attach_thread( struct thread *thread ) |
| 127 | { |
| 128 | /* this may fail if the client is already being debugged */ |
Alexandre Julliard | 5f258c6 | 2001-07-14 00:50:30 +0000 | [diff] [blame] | 129 | if (!use_ptrace) return 0; |
| 130 | if (ptrace( PTRACE_ATTACH, thread->unix_pid, 0, 0 ) == -1) |
| 131 | { |
| 132 | if (errno == ESRCH) thread->unix_pid = 0; /* process got killed */ |
| 133 | return 0; |
| 134 | } |
Alexandre Julliard | 247b8ae | 1999-12-13 00:16:44 +0000 | [diff] [blame] | 135 | if (debug_level) fprintf( stderr, "%08x: *attached*\n", (unsigned int)thread ); |
Alexandre Julliard | 578c100 | 1999-11-14 21:23:21 +0000 | [diff] [blame] | 136 | thread->attached = 1; |
| 137 | wait4_thread( thread, SIGSTOP ); |
| 138 | return 1; |
| 139 | } |
| 140 | |
| 141 | /* detach from a Unix thread and kill it */ |
Alexandre Julliard | 12f29b5 | 2000-03-17 15:16:57 +0000 | [diff] [blame] | 142 | void detach_thread( struct thread *thread, int sig ) |
Alexandre Julliard | 578c100 | 1999-11-14 21:23:21 +0000 | [diff] [blame] | 143 | { |
| 144 | if (!thread->unix_pid) return; |
Alexandre Julliard | 578c100 | 1999-11-14 21:23:21 +0000 | [diff] [blame] | 145 | if (thread->attached) |
| 146 | { |
Alexandre Julliard | 05f0b71 | 2000-03-09 18:18:41 +0000 | [diff] [blame] | 147 | /* make sure it is stopped */ |
Alexandre Julliard | 5f258c6 | 2001-07-14 00:50:30 +0000 | [diff] [blame] | 148 | suspend_thread( thread, 0 ); |
Alexandre Julliard | 12f29b5 | 2000-03-17 15:16:57 +0000 | [diff] [blame] | 149 | if (sig) kill( thread->unix_pid, sig ); |
Alexandre Julliard | 247b8ae | 1999-12-13 00:16:44 +0000 | [diff] [blame] | 150 | if (debug_level) fprintf( stderr, "%08x: *detached*\n", (unsigned int)thread ); |
Gerald Pfeifer | 1f01236 | 2000-10-17 00:25:59 +0000 | [diff] [blame] | 151 | ptrace( PTRACE_DETACH, thread->unix_pid, (caddr_t)1, sig ); |
Alexandre Julliard | 5f258c6 | 2001-07-14 00:50:30 +0000 | [diff] [blame] | 152 | thread->suspend = 0; /* detach makes it continue */ |
Alexandre Julliard | 578c100 | 1999-11-14 21:23:21 +0000 | [diff] [blame] | 153 | thread->attached = 0; |
| 154 | } |
Alexandre Julliard | 05f0b71 | 2000-03-09 18:18:41 +0000 | [diff] [blame] | 155 | else |
| 156 | { |
Alexandre Julliard | 12f29b5 | 2000-03-17 15:16:57 +0000 | [diff] [blame] | 157 | if (sig) kill( thread->unix_pid, sig ); |
Alexandre Julliard | 05f0b71 | 2000-03-09 18:18:41 +0000 | [diff] [blame] | 158 | if (thread->suspend + thread->process->suspend) continue_thread( thread ); |
| 159 | } |
Alexandre Julliard | 578c100 | 1999-11-14 21:23:21 +0000 | [diff] [blame] | 160 | } |
| 161 | |
| 162 | /* stop a thread (at the Unix level) */ |
| 163 | void stop_thread( struct thread *thread ) |
| 164 | { |
| 165 | /* can't stop a thread while initialisation is in progress */ |
| 166 | if (!thread->unix_pid || thread->process->init_event) return; |
| 167 | /* first try to attach to it */ |
| 168 | if (!thread->attached) |
| 169 | if (attach_thread( thread )) return; /* this will have stopped it */ |
| 170 | /* attached already, or attach failed -> send a signal */ |
| 171 | kill( thread->unix_pid, SIGSTOP ); |
| 172 | if (thread->attached) wait4_thread( thread, SIGSTOP ); |
| 173 | } |
| 174 | |
| 175 | /* make a thread continue (at the Unix level) */ |
| 176 | void continue_thread( struct thread *thread ) |
| 177 | { |
| 178 | if (!thread->unix_pid) return; |
| 179 | if (!thread->attached) kill( thread->unix_pid, SIGCONT ); |
Ove Kaaven | ada7383 | 2001-04-27 18:39:47 +0000 | [diff] [blame] | 180 | else ptrace( get_thread_single_step(thread) ? PTRACE_SINGLESTEP : PTRACE_CONT, |
| 181 | thread->unix_pid, (caddr_t)1, SIGSTOP ); |
Alexandre Julliard | 578c100 | 1999-11-14 21:23:21 +0000 | [diff] [blame] | 182 | } |
| 183 | |
Alexandre Julliard | 98aacc7 | 2000-03-15 19:46:14 +0000 | [diff] [blame] | 184 | /* suspend a thread to allow using ptrace on it */ |
| 185 | /* you must do a resume_thread when finished with the thread */ |
| 186 | int suspend_for_ptrace( struct thread *thread ) |
| 187 | { |
| 188 | if (thread->attached) |
| 189 | { |
| 190 | suspend_thread( thread, 0 ); |
| 191 | return 1; |
| 192 | } |
Alexandre Julliard | d392831 | 2000-04-04 19:55:28 +0000 | [diff] [blame] | 193 | /* can't stop a thread while initialisation is in progress */ |
| 194 | if (!thread->unix_pid || thread->process->init_event) goto error; |
| 195 | thread->suspend++; |
| 196 | if (attach_thread( thread )) return 1; |
| 197 | thread->suspend--; |
| 198 | error: |
Alexandre Julliard | 98aacc7 | 2000-03-15 19:46:14 +0000 | [diff] [blame] | 199 | set_error( STATUS_ACCESS_DENIED ); |
| 200 | return 0; |
| 201 | } |
| 202 | |
Alexandre Julliard | 578c100 | 1999-11-14 21:23:21 +0000 | [diff] [blame] | 203 | /* read an int from a thread address space */ |
| 204 | int read_thread_int( struct thread *thread, const int *addr, int *data ) |
| 205 | { |
Gerald Pfeifer | 1f01236 | 2000-10-17 00:25:59 +0000 | [diff] [blame] | 206 | *data = ptrace( PTRACE_PEEKDATA, thread->unix_pid, (caddr_t)addr, 0 ); |
| 207 | if ( *data == -1 && errno) |
Alexandre Julliard | 578c100 | 1999-11-14 21:23:21 +0000 | [diff] [blame] | 208 | { |
| 209 | file_set_error(); |
| 210 | return -1; |
| 211 | } |
| 212 | return 0; |
| 213 | } |
| 214 | |
| 215 | /* write an int to a thread address space */ |
| 216 | int write_thread_int( struct thread *thread, int *addr, int data, unsigned int mask ) |
| 217 | { |
| 218 | int res; |
| 219 | if (mask != ~0) |
| 220 | { |
| 221 | if (read_thread_int( thread, addr, &res ) == -1) return -1; |
| 222 | data = (data & mask) | (res & ~mask); |
| 223 | } |
Gerald Pfeifer | 1f01236 | 2000-10-17 00:25:59 +0000 | [diff] [blame] | 224 | if ((res = ptrace( PTRACE_POKEDATA, thread->unix_pid, (caddr_t)addr, data )) == -1) |
| 225 | file_set_error(); |
Alexandre Julliard | 578c100 | 1999-11-14 21:23:21 +0000 | [diff] [blame] | 226 | return res; |
| 227 | } |