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 |
Alexandre Julliard | 0799c1a | 2002-03-09 23:29:33 +0000 | [diff] [blame] | 5 | * |
| 6 | * This library is free software; you can redistribute it and/or |
| 7 | * modify it under the terms of the GNU Lesser General Public |
| 8 | * License as published by the Free Software Foundation; either |
| 9 | * version 2.1 of the License, or (at your option) any later version. |
| 10 | * |
| 11 | * This library is distributed in the hope that it will be useful, |
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 14 | * Lesser General Public License for more details. |
| 15 | * |
| 16 | * You should have received a copy of the GNU Lesser General Public |
| 17 | * License along with this library; if not, write to the Free Software |
Jonathan Ernst | 360a3f9 | 2006-05-18 14:49:52 +0200 | [diff] [blame] | 18 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA |
Alexandre Julliard | 578c100 | 1999-11-14 21:23:21 +0000 | [diff] [blame] | 19 | */ |
| 20 | |
| 21 | #include "config.h" |
Alexandre Julliard | 1a79912 | 2010-07-21 10:50:12 +0200 | [diff] [blame] | 22 | #include "wine/port.h" |
Alexandre Julliard | 578c100 | 1999-11-14 21:23:21 +0000 | [diff] [blame] | 23 | |
| 24 | #include <assert.h> |
| 25 | #include <errno.h> |
Alexandre Julliard | 1a79912 | 2010-07-21 10:50:12 +0200 | [diff] [blame] | 26 | #include <fcntl.h> |
Alexandre Julliard | 578c100 | 1999-11-14 21:23:21 +0000 | [diff] [blame] | 27 | #include <stdio.h> |
| 28 | #include <signal.h> |
Ge van Geldorp | 1a1583a | 2005-11-28 17:32:54 +0100 | [diff] [blame] | 29 | #include <stdarg.h> |
Alexandre Julliard | 42666ee | 1999-11-21 01:10:16 +0000 | [diff] [blame] | 30 | #include <sys/types.h> |
Dimitrie O. Paun | 2af03e4 | 2000-11-29 20:04:09 +0000 | [diff] [blame] | 31 | #ifdef HAVE_SYS_PTRACE_H |
| 32 | # include <sys/ptrace.h> |
| 33 | #endif |
Alexandre Julliard | 498742f | 2006-06-27 21:27:47 +0200 | [diff] [blame] | 34 | #ifdef HAVE_SYS_PARAM_H |
| 35 | # include <sys/param.h> |
| 36 | #endif |
Alexandre Julliard | 578c100 | 1999-11-14 21:23:21 +0000 | [diff] [blame] | 37 | #ifdef HAVE_SYS_WAIT_H |
Dimitrie O. Paun | 2af03e4 | 2000-11-29 20:04:09 +0000 | [diff] [blame] | 38 | # include <sys/wait.h> |
Alexandre Julliard | 578c100 | 1999-11-14 21:23:21 +0000 | [diff] [blame] | 39 | #endif |
Alexandre Julliard | 185f637 | 2010-12-27 12:07:16 +0100 | [diff] [blame] | 40 | #ifdef HAVE_SYS_SYSCALL_H |
| 41 | # include <sys/syscall.h> |
| 42 | #endif |
Tijl Coosemans | 0c4f152 | 2007-08-23 14:12:37 +0200 | [diff] [blame] | 43 | #ifdef HAVE_SYS_THR_H |
| 44 | # include <sys/ucontext.h> |
| 45 | # include <sys/thr.h> |
| 46 | #endif |
Alexandre Julliard | 578c100 | 1999-11-14 21:23:21 +0000 | [diff] [blame] | 47 | #include <unistd.h> |
| 48 | |
Ge van Geldorp | 1a1583a | 2005-11-28 17:32:54 +0100 | [diff] [blame] | 49 | #include "ntstatus.h" |
| 50 | #define WIN32_NO_STATUS |
| 51 | #include "winternl.h" |
| 52 | |
Alexandre Julliard | cf27a7f | 2003-02-14 20:27:09 +0000 | [diff] [blame] | 53 | #include "file.h" |
Alexandre Julliard | 578c100 | 1999-11-14 21:23:21 +0000 | [diff] [blame] | 54 | #include "process.h" |
| 55 | #include "thread.h" |
| 56 | |
Alexandre Julliard | c273498 | 2006-12-29 20:38:49 +0100 | [diff] [blame] | 57 | #ifdef USE_PTRACE |
| 58 | |
Alexandre Julliard | 578c100 | 1999-11-14 21:23:21 +0000 | [diff] [blame] | 59 | #ifndef PTRACE_CONT |
| 60 | #define PTRACE_CONT PT_CONTINUE |
| 61 | #endif |
Ove Kaaven | ada7383 | 2001-04-27 18:39:47 +0000 | [diff] [blame] | 62 | #ifndef PTRACE_SINGLESTEP |
| 63 | #define PTRACE_SINGLESTEP PT_STEP |
| 64 | #endif |
Alexandre Julliard | 578c100 | 1999-11-14 21:23:21 +0000 | [diff] [blame] | 65 | #ifndef PTRACE_ATTACH |
| 66 | #define PTRACE_ATTACH PT_ATTACH |
| 67 | #endif |
| 68 | #ifndef PTRACE_DETACH |
| 69 | #define PTRACE_DETACH PT_DETACH |
| 70 | #endif |
| 71 | #ifndef PTRACE_PEEKDATA |
| 72 | #define PTRACE_PEEKDATA PT_READ_D |
| 73 | #endif |
| 74 | #ifndef PTRACE_POKEDATA |
| 75 | #define PTRACE_POKEDATA PT_WRITE_D |
| 76 | #endif |
Alexandre Julliard | 498742f | 2006-06-27 21:27:47 +0200 | [diff] [blame] | 77 | #ifndef PTRACE_PEEKUSER |
| 78 | #define PTRACE_PEEKUSER PT_READ_U |
| 79 | #endif |
| 80 | #ifndef PTRACE_POKEUSER |
| 81 | #define PTRACE_POKEUSER PT_WRITE_U |
| 82 | #endif |
| 83 | |
| 84 | #ifdef PT_GETDBREGS |
| 85 | #define PTRACE_GETDBREGS PT_GETDBREGS |
| 86 | #endif |
| 87 | #ifdef PT_SETDBREGS |
| 88 | #define PTRACE_SETDBREGS PT_SETDBREGS |
| 89 | #endif |
Alexandre Julliard | 578c100 | 1999-11-14 21:23:21 +0000 | [diff] [blame] | 90 | |
Alexandre Julliard | 5f258c6 | 2001-07-14 00:50:30 +0000 | [diff] [blame] | 91 | #ifndef HAVE_SYS_PTRACE_H |
Dimitrie O. Paun | 2af03e4 | 2000-11-29 20:04:09 +0000 | [diff] [blame] | 92 | #define PT_CONTINUE 0 |
| 93 | #define PT_ATTACH 1 |
| 94 | #define PT_DETACH 2 |
| 95 | #define PT_READ_D 3 |
| 96 | #define PT_WRITE_D 4 |
Ove Kaaven | ada7383 | 2001-04-27 18:39:47 +0000 | [diff] [blame] | 97 | #define PT_STEP 5 |
Andrew Talbot | b1788c8 | 2007-03-17 10:52:14 +0000 | [diff] [blame] | 98 | static inline int ptrace(int req, ...) { errno = EPERM; return -1; /*FAIL*/ } |
Alexandre Julliard | 5f258c6 | 2001-07-14 00:50:30 +0000 | [diff] [blame] | 99 | #endif /* HAVE_SYS_PTRACE_H */ |
Dimitrie O. Paun | 2af03e4 | 2000-11-29 20:04:09 +0000 | [diff] [blame] | 100 | |
Alexandre Julliard | 247b8ae | 1999-12-13 00:16:44 +0000 | [diff] [blame] | 101 | /* handle a status returned by wait4 */ |
Alexandre Julliard | d04ccb8 | 2003-03-04 22:18:43 +0000 | [diff] [blame] | 102 | static int handle_child_status( struct thread *thread, int pid, int status, int want_sig ) |
Alexandre Julliard | 578c100 | 1999-11-14 21:23:21 +0000 | [diff] [blame] | 103 | { |
Alexandre Julliard | 578c100 | 1999-11-14 21:23:21 +0000 | [diff] [blame] | 104 | if (WIFSTOPPED(status)) |
| 105 | { |
| 106 | int sig = WSTOPSIG(status); |
Alexandre Julliard | 247b8ae | 1999-12-13 00:16:44 +0000 | [diff] [blame] | 107 | if (debug_level && thread) |
Alexandre Julliard | 91befe1 | 2003-02-01 01:38:40 +0000 | [diff] [blame] | 108 | fprintf( stderr, "%04x: *signal* signal=%d\n", thread->id, sig ); |
Alexandre Julliard | d04ccb8 | 2003-03-04 22:18:43 +0000 | [diff] [blame] | 109 | if (sig != want_sig) |
Alexandre Julliard | 578c100 | 1999-11-14 21:23:21 +0000 | [diff] [blame] | 110 | { |
Alexandre Julliard | d04ccb8 | 2003-03-04 22:18:43 +0000 | [diff] [blame] | 111 | /* ignore other signals for now */ |
Alexandre Julliard | 3dd0b2e | 2006-04-03 21:42:42 +0200 | [diff] [blame] | 112 | ptrace( PTRACE_CONT, pid, (caddr_t)1, sig ); |
Alexandre Julliard | 578c100 | 1999-11-14 21:23:21 +0000 | [diff] [blame] | 113 | } |
Alexandre Julliard | 247b8ae | 1999-12-13 00:16:44 +0000 | [diff] [blame] | 114 | return sig; |
Alexandre Julliard | 578c100 | 1999-11-14 21:23:21 +0000 | [diff] [blame] | 115 | } |
Alexandre Julliard | 247b8ae | 1999-12-13 00:16:44 +0000 | [diff] [blame] | 116 | if (thread && (WIFSIGNALED(status) || WIFEXITED(status))) |
Alexandre Julliard | 578c100 | 1999-11-14 21:23:21 +0000 | [diff] [blame] | 117 | { |
Alexandre Julliard | 02a53c1 | 2003-02-25 04:17:22 +0000 | [diff] [blame] | 118 | thread->unix_pid = -1; |
Alexandre Julliard | a8497bd | 2003-03-22 21:00:09 +0000 | [diff] [blame] | 119 | thread->unix_tid = -1; |
Alexandre Julliard | 578c100 | 1999-11-14 21:23:21 +0000 | [diff] [blame] | 120 | if (debug_level) |
Alexandre Julliard | 247b8ae | 1999-12-13 00:16:44 +0000 | [diff] [blame] | 121 | { |
| 122 | if (WIFSIGNALED(status)) |
Alexandre Julliard | 91befe1 | 2003-02-01 01:38:40 +0000 | [diff] [blame] | 123 | fprintf( stderr, "%04x: *exited* signal=%d\n", |
| 124 | thread->id, WTERMSIG(status) ); |
Alexandre Julliard | 247b8ae | 1999-12-13 00:16:44 +0000 | [diff] [blame] | 125 | else |
Alexandre Julliard | 91befe1 | 2003-02-01 01:38:40 +0000 | [diff] [blame] | 126 | fprintf( stderr, "%04x: *exited* status=%d\n", |
| 127 | thread->id, WEXITSTATUS(status) ); |
Alexandre Julliard | 247b8ae | 1999-12-13 00:16:44 +0000 | [diff] [blame] | 128 | } |
Alexandre Julliard | 578c100 | 1999-11-14 21:23:21 +0000 | [diff] [blame] | 129 | } |
Alexandre Julliard | 247b8ae | 1999-12-13 00:16:44 +0000 | [diff] [blame] | 130 | return 0; |
| 131 | } |
| 132 | |
Alexandre Julliard | 77fde13 | 2003-04-26 02:14:02 +0000 | [diff] [blame] | 133 | /* wait4 wrapper to handle missing __WALL flag in older kernels */ |
| 134 | static inline pid_t wait4_wrapper( pid_t pid, int *status, int options, struct rusage *usage ) |
| 135 | { |
| 136 | #ifdef __WALL |
| 137 | static int wall_flag = __WALL; |
| 138 | |
| 139 | for (;;) |
| 140 | { |
| 141 | pid_t ret = wait4( pid, status, options | wall_flag, usage ); |
| 142 | if (ret != -1 || !wall_flag || errno != EINVAL) return ret; |
| 143 | wall_flag = 0; |
| 144 | } |
| 145 | #else |
| 146 | return wait4( pid, status, options, usage ); |
| 147 | #endif |
| 148 | } |
| 149 | |
Alexandre Julliard | 247b8ae | 1999-12-13 00:16:44 +0000 | [diff] [blame] | 150 | /* handle a SIGCHLD signal */ |
Alexandre Julliard | 9037f4b | 2003-03-26 01:32:18 +0000 | [diff] [blame] | 151 | void sigchld_callback(void) |
Alexandre Julliard | 247b8ae | 1999-12-13 00:16:44 +0000 | [diff] [blame] | 152 | { |
| 153 | int pid, status; |
| 154 | |
| 155 | for (;;) |
Alexandre Julliard | 578c100 | 1999-11-14 21:23:21 +0000 | [diff] [blame] | 156 | { |
Alexandre Julliard | 77fde13 | 2003-04-26 02:14:02 +0000 | [diff] [blame] | 157 | if (!(pid = wait4_wrapper( -1, &status, WUNTRACED | WNOHANG, NULL ))) break; |
Alexandre Julliard | 48b74b3 | 2006-12-29 16:53:33 +0100 | [diff] [blame] | 158 | if (pid != -1) |
| 159 | { |
| 160 | struct thread *thread = get_thread_from_tid( pid ); |
| 161 | if (!thread) thread = get_thread_from_pid( pid ); |
| 162 | handle_child_status( thread, pid, status, -1 ); |
| 163 | } |
Alexandre Julliard | 247b8ae | 1999-12-13 00:16:44 +0000 | [diff] [blame] | 164 | else break; |
Alexandre Julliard | 578c100 | 1999-11-14 21:23:21 +0000 | [diff] [blame] | 165 | } |
Alexandre Julliard | 247b8ae | 1999-12-13 00:16:44 +0000 | [diff] [blame] | 166 | } |
| 167 | |
Tijl Coosemans | 60833da | 2007-08-08 00:25:24 +0200 | [diff] [blame] | 168 | /* return the Unix pid to use in ptrace calls for a given process */ |
Alexandre Julliard | 498742f | 2006-06-27 21:27:47 +0200 | [diff] [blame] | 169 | static int get_ptrace_pid( struct thread *thread ) |
| 170 | { |
Tijl Coosemans | 60833da | 2007-08-08 00:25:24 +0200 | [diff] [blame] | 171 | #ifdef linux /* linux always uses thread id */ |
| 172 | if (thread->unix_tid != -1) return thread->unix_tid; |
| 173 | #endif |
| 174 | return thread->unix_pid; |
| 175 | } |
| 176 | |
| 177 | /* return the Unix tid to use in ptrace calls for a given thread */ |
| 178 | static int get_ptrace_tid( struct thread *thread ) |
| 179 | { |
Alexandre Julliard | 498742f | 2006-06-27 21:27:47 +0200 | [diff] [blame] | 180 | if (thread->unix_tid != -1) return thread->unix_tid; |
| 181 | return thread->unix_pid; |
| 182 | } |
| 183 | |
Alexandre Julliard | 247b8ae | 1999-12-13 00:16:44 +0000 | [diff] [blame] | 184 | /* wait for a ptraced child to get a certain signal */ |
Alexandre Julliard | fcbd0da | 2003-05-06 00:21:21 +0000 | [diff] [blame] | 185 | static int wait4_thread( struct thread *thread, int signal ) |
Alexandre Julliard | 247b8ae | 1999-12-13 00:16:44 +0000 | [diff] [blame] | 186 | { |
| 187 | int res, status; |
| 188 | |
Alexandre Julliard | 9d99a04 | 2005-08-19 14:01:43 +0000 | [diff] [blame] | 189 | start_watchdog(); |
Alexandre Julliard | de1990f | 2003-08-21 21:35:15 +0000 | [diff] [blame] | 190 | for (;;) |
Alexandre Julliard | 247b8ae | 1999-12-13 00:16:44 +0000 | [diff] [blame] | 191 | { |
Alexandre Julliard | 77fde13 | 2003-04-26 02:14:02 +0000 | [diff] [blame] | 192 | if ((res = wait4_wrapper( get_ptrace_pid(thread), &status, WUNTRACED, NULL )) == -1) |
Alexandre Julliard | 247b8ae | 1999-12-13 00:16:44 +0000 | [diff] [blame] | 193 | { |
Alexandre Julliard | 9d99a04 | 2005-08-19 14:01:43 +0000 | [diff] [blame] | 194 | if (errno == EINTR) |
| 195 | { |
| 196 | if (!watchdog_triggered()) continue; |
| 197 | if (debug_level) fprintf( stderr, "%04x: *watchdog* wait4 aborted\n", thread->id ); |
| 198 | } |
| 199 | else if (errno == ECHILD) /* must have died */ |
Alexandre Julliard | d04ccb8 | 2003-03-04 22:18:43 +0000 | [diff] [blame] | 200 | { |
| 201 | thread->unix_pid = -1; |
Alexandre Julliard | a8497bd | 2003-03-22 21:00:09 +0000 | [diff] [blame] | 202 | thread->unix_tid = -1; |
Alexandre Julliard | d04ccb8 | 2003-03-04 22:18:43 +0000 | [diff] [blame] | 203 | } |
| 204 | else perror( "wait4" ); |
Alexandre Julliard | 9d99a04 | 2005-08-19 14:01:43 +0000 | [diff] [blame] | 205 | stop_watchdog(); |
Alexandre Julliard | fcbd0da | 2003-05-06 00:21:21 +0000 | [diff] [blame] | 206 | return 0; |
Alexandre Julliard | 247b8ae | 1999-12-13 00:16:44 +0000 | [diff] [blame] | 207 | } |
Alexandre Julliard | d04ccb8 | 2003-03-04 22:18:43 +0000 | [diff] [blame] | 208 | res = handle_child_status( thread, res, status, signal ); |
Alexandre Julliard | de1990f | 2003-08-21 21:35:15 +0000 | [diff] [blame] | 209 | if (!res || res == signal) break; |
| 210 | } |
Alexandre Julliard | 9d99a04 | 2005-08-19 14:01:43 +0000 | [diff] [blame] | 211 | stop_watchdog(); |
Alexandre Julliard | fcbd0da | 2003-05-06 00:21:21 +0000 | [diff] [blame] | 212 | return (thread->unix_pid != -1); |
Alexandre Julliard | 578c100 | 1999-11-14 21:23:21 +0000 | [diff] [blame] | 213 | } |
| 214 | |
Alexandre Julliard | 7dbd1f0 | 2006-04-10 21:07:21 +0200 | [diff] [blame] | 215 | /* send a signal to a specific thread */ |
| 216 | static inline int tkill( int tgid, int pid, int sig ) |
| 217 | { |
Alexandre Julliard | 7dbd1f0 | 2006-04-10 21:07:21 +0200 | [diff] [blame] | 218 | #ifdef __linux__ |
Alexandre Julliard | 185f637 | 2010-12-27 12:07:16 +0100 | [diff] [blame] | 219 | int ret = syscall( SYS_tgkill, tgid, pid, sig ); |
| 220 | if (ret < 0 && errno == ENOSYS) ret = syscall( SYS_tkill, pid, sig ); |
Maarten Lankhorst | c054b5f | 2010-06-06 01:06:48 +0200 | [diff] [blame] | 221 | return ret; |
Austin English | 599afa0 | 2011-01-15 15:53:24 -0800 | [diff] [blame] | 222 | #elif (defined(__FreeBSD__) || defined (__FreeBSD_kernel__)) && defined(HAVE_THR_KILL2) |
Tijl Coosemans | 0c4f152 | 2007-08-23 14:12:37 +0200 | [diff] [blame] | 223 | return thr_kill2( tgid, pid, sig ); |
| 224 | #else |
| 225 | errno = ENOSYS; |
| 226 | return -1; |
| 227 | #endif |
Alexandre Julliard | 7dbd1f0 | 2006-04-10 21:07:21 +0200 | [diff] [blame] | 228 | } |
| 229 | |
Alexandre Julliard | cd1c7fc | 2006-12-29 16:56:11 +0100 | [diff] [blame] | 230 | /* initialize the process tracing mechanism */ |
| 231 | void init_tracing_mechanism(void) |
| 232 | { |
| 233 | /* no initialization needed for ptrace */ |
| 234 | } |
| 235 | |
| 236 | /* initialize the per-process tracing mechanism */ |
| 237 | void init_process_tracing( struct process *process ) |
| 238 | { |
| 239 | /* ptrace setup is done on-demand */ |
| 240 | } |
| 241 | |
| 242 | /* terminate the per-process tracing mechanism */ |
| 243 | void finish_process_tracing( struct process *process ) |
| 244 | { |
| 245 | } |
| 246 | |
Alexandre Julliard | 02a53c1 | 2003-02-25 04:17:22 +0000 | [diff] [blame] | 247 | /* send a Unix signal to a specific thread */ |
| 248 | int send_thread_signal( struct thread *thread, int sig ) |
| 249 | { |
| 250 | int ret = -1; |
| 251 | |
| 252 | if (thread->unix_pid != -1) |
| 253 | { |
Alexandre Julliard | 3dddc11 | 2003-04-16 23:34:05 +0000 | [diff] [blame] | 254 | if (thread->unix_tid != -1) |
| 255 | { |
Eric Pouech | 22357ca | 2006-02-14 12:24:32 +0100 | [diff] [blame] | 256 | ret = tkill( thread->unix_pid, thread->unix_tid, sig ); |
Alexandre Julliard | 3dddc11 | 2003-04-16 23:34:05 +0000 | [diff] [blame] | 257 | if (ret == -1 && errno == ENOSYS) ret = kill( thread->unix_pid, sig ); |
| 258 | } |
Alexandre Julliard | a8497bd | 2003-03-22 21:00:09 +0000 | [diff] [blame] | 259 | else ret = kill( thread->unix_pid, sig ); |
| 260 | |
Alexandre Julliard | 02a53c1 | 2003-02-25 04:17:22 +0000 | [diff] [blame] | 261 | if (ret == -1 && errno == ESRCH) /* thread got killed */ |
| 262 | { |
| 263 | thread->unix_pid = -1; |
Alexandre Julliard | a8497bd | 2003-03-22 21:00:09 +0000 | [diff] [blame] | 264 | thread->unix_tid = -1; |
Alexandre Julliard | 02a53c1 | 2003-02-25 04:17:22 +0000 | [diff] [blame] | 265 | } |
| 266 | } |
Alexandre Julliard | ae4ecb6 | 2007-01-18 12:23:29 +0100 | [diff] [blame] | 267 | if (debug_level && ret != -1) |
| 268 | fprintf( stderr, "%04x: *sent signal* signal=%d\n", thread->id, sig ); |
Alexandre Julliard | 02a53c1 | 2003-02-25 04:17:22 +0000 | [diff] [blame] | 269 | return (ret != -1); |
| 270 | } |
| 271 | |
Alexandre Julliard | 498742f | 2006-06-27 21:27:47 +0200 | [diff] [blame] | 272 | /* resume a thread after we have used ptrace on it */ |
| 273 | static void resume_after_ptrace( struct thread *thread ) |
| 274 | { |
| 275 | if (thread->unix_pid == -1) return; |
| 276 | if (ptrace( PTRACE_DETACH, get_ptrace_pid(thread), (caddr_t)1, 0 ) == -1) |
| 277 | { |
| 278 | if (errno == ESRCH) thread->unix_pid = thread->unix_tid = -1; /* thread got killed */ |
| 279 | } |
| 280 | } |
| 281 | |
Alexandre Julliard | 98aacc7 | 2000-03-15 19:46:14 +0000 | [diff] [blame] | 282 | /* suspend a thread to allow using ptrace on it */ |
Alexandre Julliard | d04ccb8 | 2003-03-04 22:18:43 +0000 | [diff] [blame] | 283 | /* you must do a resume_after_ptrace when finished with the thread */ |
Alexandre Julliard | 498742f | 2006-06-27 21:27:47 +0200 | [diff] [blame] | 284 | static int suspend_for_ptrace( struct thread *thread ) |
Alexandre Julliard | 98aacc7 | 2000-03-15 19:46:14 +0000 | [diff] [blame] | 285 | { |
Alexandre Julliard | d392831 | 2000-04-04 19:55:28 +0000 | [diff] [blame] | 286 | /* can't stop a thread while initialisation is in progress */ |
Alexandre Julliard | 02a53c1 | 2003-02-25 04:17:22 +0000 | [diff] [blame] | 287 | if (thread->unix_pid == -1 || !is_process_init_done(thread->process)) goto error; |
Alexandre Julliard | d04ccb8 | 2003-03-04 22:18:43 +0000 | [diff] [blame] | 288 | |
Alexandre Julliard | 820c592 | 2006-04-10 20:25:22 +0200 | [diff] [blame] | 289 | /* this may fail if the client is already being debugged */ |
| 290 | if (ptrace( PTRACE_ATTACH, get_ptrace_pid(thread), 0, 0 ) == -1) |
Alexandre Julliard | d04ccb8 | 2003-03-04 22:18:43 +0000 | [diff] [blame] | 291 | { |
Alexandre Julliard | 820c592 | 2006-04-10 20:25:22 +0200 | [diff] [blame] | 292 | if (errno == ESRCH) thread->unix_pid = thread->unix_tid = -1; /* thread got killed */ |
| 293 | goto error; |
Alexandre Julliard | 9d99a04 | 2005-08-19 14:01:43 +0000 | [diff] [blame] | 294 | } |
| 295 | if (wait4_thread( thread, SIGSTOP )) return 1; |
| 296 | resume_after_ptrace( thread ); |
Alexandre Julliard | d392831 | 2000-04-04 19:55:28 +0000 | [diff] [blame] | 297 | error: |
Alexandre Julliard | 98aacc7 | 2000-03-15 19:46:14 +0000 | [diff] [blame] | 298 | set_error( STATUS_ACCESS_DENIED ); |
| 299 | return 0; |
| 300 | } |
| 301 | |
Alexandre Julliard | 034c6c3 | 2009-06-17 20:53:32 +0200 | [diff] [blame] | 302 | /* read a long from a thread address space */ |
| 303 | static long read_thread_long( struct thread *thread, long *addr, long *data ) |
Alexandre Julliard | 578c100 | 1999-11-14 21:23:21 +0000 | [diff] [blame] | 304 | { |
Alexandre Julliard | d04ccb8 | 2003-03-04 22:18:43 +0000 | [diff] [blame] | 305 | errno = 0; |
Alexandre Julliard | baf0a06 | 2003-03-11 01:48:53 +0000 | [diff] [blame] | 306 | *data = ptrace( PTRACE_PEEKDATA, get_ptrace_pid(thread), (caddr_t)addr, 0 ); |
Gerald Pfeifer | 1f01236 | 2000-10-17 00:25:59 +0000 | [diff] [blame] | 307 | if ( *data == -1 && errno) |
Alexandre Julliard | 578c100 | 1999-11-14 21:23:21 +0000 | [diff] [blame] | 308 | { |
| 309 | file_set_error(); |
| 310 | return -1; |
| 311 | } |
| 312 | return 0; |
| 313 | } |
| 314 | |
Alexandre Julliard | 034c6c3 | 2009-06-17 20:53:32 +0200 | [diff] [blame] | 315 | /* write a long to a thread address space */ |
| 316 | static long write_thread_long( struct thread *thread, long *addr, long data, unsigned long mask ) |
Alexandre Julliard | 578c100 | 1999-11-14 21:23:21 +0000 | [diff] [blame] | 317 | { |
Alexandre Julliard | 034c6c3 | 2009-06-17 20:53:32 +0200 | [diff] [blame] | 318 | long res; |
Eric Pouech | 1298f22 | 2010-04-19 22:33:53 +0200 | [diff] [blame] | 319 | if (mask != ~0ul) |
Alexandre Julliard | 578c100 | 1999-11-14 21:23:21 +0000 | [diff] [blame] | 320 | { |
Alexandre Julliard | 034c6c3 | 2009-06-17 20:53:32 +0200 | [diff] [blame] | 321 | if (read_thread_long( thread, addr, &res ) == -1) return -1; |
Alexandre Julliard | 578c100 | 1999-11-14 21:23:21 +0000 | [diff] [blame] | 322 | data = (data & mask) | (res & ~mask); |
| 323 | } |
Alexandre Julliard | baf0a06 | 2003-03-11 01:48:53 +0000 | [diff] [blame] | 324 | if ((res = ptrace( PTRACE_POKEDATA, get_ptrace_pid(thread), (caddr_t)addr, data )) == -1) |
Gerald Pfeifer | 1f01236 | 2000-10-17 00:25:59 +0000 | [diff] [blame] | 325 | file_set_error(); |
Alexandre Julliard | 578c100 | 1999-11-14 21:23:21 +0000 | [diff] [blame] | 326 | return res; |
| 327 | } |
Alexandre Julliard | cb70931 | 2006-04-07 19:52:12 +0200 | [diff] [blame] | 328 | |
Alexandre Julliard | 7b767fb | 2006-07-25 11:41:47 +0200 | [diff] [blame] | 329 | /* return a thread of the process suitable for ptracing */ |
| 330 | static struct thread *get_ptrace_thread( struct process *process ) |
| 331 | { |
| 332 | struct thread *thread; |
| 333 | |
| 334 | LIST_FOR_EACH_ENTRY( thread, &process->thread_list, struct thread, proc_entry ) |
| 335 | { |
| 336 | if (thread->unix_pid != -1) return thread; |
| 337 | } |
| 338 | set_error( STATUS_ACCESS_DENIED ); /* process is dead */ |
| 339 | return NULL; |
| 340 | } |
| 341 | |
Alexandre Julliard | cb70931 | 2006-04-07 19:52:12 +0200 | [diff] [blame] | 342 | /* read data from a process memory space */ |
Alexandre Julliard | 8e9c156 | 2008-12-30 14:11:58 +0100 | [diff] [blame] | 343 | int read_process_memory( struct process *process, client_ptr_t ptr, data_size_t size, char *dest ) |
Alexandre Julliard | cb70931 | 2006-04-07 19:52:12 +0200 | [diff] [blame] | 344 | { |
Alexandre Julliard | 7b767fb | 2006-07-25 11:41:47 +0200 | [diff] [blame] | 345 | struct thread *thread = get_ptrace_thread( process ); |
Alexandre Julliard | cb70931 | 2006-04-07 19:52:12 +0200 | [diff] [blame] | 346 | unsigned int first_offset, last_offset, len; |
Alexandre Julliard | 034c6c3 | 2009-06-17 20:53:32 +0200 | [diff] [blame] | 347 | long data, *addr; |
Alexandre Julliard | cb70931 | 2006-04-07 19:52:12 +0200 | [diff] [blame] | 348 | |
Alexandre Julliard | 7b767fb | 2006-07-25 11:41:47 +0200 | [diff] [blame] | 349 | if (!thread) return 0; |
Alexandre Julliard | cb70931 | 2006-04-07 19:52:12 +0200 | [diff] [blame] | 350 | |
Alexandre Julliard | 8e9c156 | 2008-12-30 14:11:58 +0100 | [diff] [blame] | 351 | if ((unsigned long)ptr != ptr) |
| 352 | { |
| 353 | set_error( STATUS_ACCESS_DENIED ); |
| 354 | return 0; |
| 355 | } |
| 356 | |
Alexandre Julliard | 034c6c3 | 2009-06-17 20:53:32 +0200 | [diff] [blame] | 357 | first_offset = ptr % sizeof(long); |
| 358 | last_offset = (size + first_offset) % sizeof(long); |
| 359 | if (!last_offset) last_offset = sizeof(long); |
Alexandre Julliard | cb70931 | 2006-04-07 19:52:12 +0200 | [diff] [blame] | 360 | |
Alexandre Julliard | 034c6c3 | 2009-06-17 20:53:32 +0200 | [diff] [blame] | 361 | addr = (long *)(unsigned long)(ptr - first_offset); |
| 362 | len = (size + first_offset + sizeof(long) - 1) / sizeof(long); |
Alexandre Julliard | cb70931 | 2006-04-07 19:52:12 +0200 | [diff] [blame] | 363 | |
| 364 | if (suspend_for_ptrace( thread )) |
| 365 | { |
Alexandre Julliard | 1a79912 | 2010-07-21 10:50:12 +0200 | [diff] [blame] | 366 | if (len > 3) /* /proc/pid/mem should be faster for large sizes */ |
| 367 | { |
| 368 | char procmem[24]; |
| 369 | int fd; |
| 370 | |
| 371 | sprintf( procmem, "/proc/%u/mem", process->unix_pid ); |
| 372 | if ((fd = open( procmem, O_RDONLY )) != -1) |
| 373 | { |
| 374 | ssize_t ret = pread( fd, dest, size, ptr ); |
| 375 | close( fd ); |
| 376 | if (ret == size) |
| 377 | { |
| 378 | len = 0; |
| 379 | goto done; |
| 380 | } |
| 381 | } |
| 382 | } |
| 383 | |
Alexandre Julliard | cb70931 | 2006-04-07 19:52:12 +0200 | [diff] [blame] | 384 | if (len > 1) |
| 385 | { |
Alexandre Julliard | 034c6c3 | 2009-06-17 20:53:32 +0200 | [diff] [blame] | 386 | if (read_thread_long( thread, addr++, &data ) == -1) goto done; |
| 387 | memcpy( dest, (char *)&data + first_offset, sizeof(long) - first_offset ); |
| 388 | dest += sizeof(long) - first_offset; |
Alexandre Julliard | cb70931 | 2006-04-07 19:52:12 +0200 | [diff] [blame] | 389 | first_offset = 0; |
| 390 | len--; |
| 391 | } |
| 392 | |
| 393 | while (len > 1) |
| 394 | { |
Alexandre Julliard | 034c6c3 | 2009-06-17 20:53:32 +0200 | [diff] [blame] | 395 | if (read_thread_long( thread, addr++, &data ) == -1) goto done; |
| 396 | memcpy( dest, &data, sizeof(long) ); |
| 397 | dest += sizeof(long); |
Alexandre Julliard | cb70931 | 2006-04-07 19:52:12 +0200 | [diff] [blame] | 398 | len--; |
| 399 | } |
| 400 | |
Alexandre Julliard | 034c6c3 | 2009-06-17 20:53:32 +0200 | [diff] [blame] | 401 | if (read_thread_long( thread, addr++, &data ) == -1) goto done; |
Alexandre Julliard | cb70931 | 2006-04-07 19:52:12 +0200 | [diff] [blame] | 402 | memcpy( dest, (char *)&data + first_offset, last_offset - first_offset ); |
| 403 | len--; |
| 404 | |
| 405 | done: |
| 406 | resume_after_ptrace( thread ); |
| 407 | } |
| 408 | return !len; |
| 409 | } |
| 410 | |
| 411 | /* make sure we can write to the whole address range */ |
| 412 | /* len is the total size (in ints) */ |
Alexandre Julliard | 034c6c3 | 2009-06-17 20:53:32 +0200 | [diff] [blame] | 413 | static int check_process_write_access( struct thread *thread, long *addr, data_size_t len ) |
Alexandre Julliard | cb70931 | 2006-04-07 19:52:12 +0200 | [diff] [blame] | 414 | { |
| 415 | int page = get_page_size() / sizeof(int); |
| 416 | |
| 417 | for (;;) |
| 418 | { |
Alexandre Julliard | 034c6c3 | 2009-06-17 20:53:32 +0200 | [diff] [blame] | 419 | if (write_thread_long( thread, addr, 0, 0 ) == -1) return 0; |
Alexandre Julliard | cb70931 | 2006-04-07 19:52:12 +0200 | [diff] [blame] | 420 | if (len <= page) break; |
| 421 | addr += page; |
| 422 | len -= page; |
| 423 | } |
Alexandre Julliard | 034c6c3 | 2009-06-17 20:53:32 +0200 | [diff] [blame] | 424 | return (write_thread_long( thread, addr + len - 1, 0, 0 ) != -1); |
Alexandre Julliard | cb70931 | 2006-04-07 19:52:12 +0200 | [diff] [blame] | 425 | } |
| 426 | |
| 427 | /* write data to a process memory space */ |
Alexandre Julliard | 8e9c156 | 2008-12-30 14:11:58 +0100 | [diff] [blame] | 428 | int write_process_memory( struct process *process, client_ptr_t ptr, data_size_t size, const char *src ) |
Alexandre Julliard | cb70931 | 2006-04-07 19:52:12 +0200 | [diff] [blame] | 429 | { |
Alexandre Julliard | 7b767fb | 2006-07-25 11:41:47 +0200 | [diff] [blame] | 430 | struct thread *thread = get_ptrace_thread( process ); |
Alexandre Julliard | 034c6c3 | 2009-06-17 20:53:32 +0200 | [diff] [blame] | 431 | int ret = 0; |
| 432 | long data = 0; |
Alexandre Julliard | 0f273c1 | 2006-07-26 10:43:25 +0200 | [diff] [blame] | 433 | data_size_t len; |
Alexandre Julliard | 034c6c3 | 2009-06-17 20:53:32 +0200 | [diff] [blame] | 434 | long *addr; |
| 435 | unsigned long first_mask, first_offset, last_mask, last_offset; |
Alexandre Julliard | cb70931 | 2006-04-07 19:52:12 +0200 | [diff] [blame] | 436 | |
Alexandre Julliard | 7b767fb | 2006-07-25 11:41:47 +0200 | [diff] [blame] | 437 | if (!thread) return 0; |
Alexandre Julliard | cb70931 | 2006-04-07 19:52:12 +0200 | [diff] [blame] | 438 | |
Alexandre Julliard | 8e9c156 | 2008-12-30 14:11:58 +0100 | [diff] [blame] | 439 | if ((unsigned long)ptr != ptr) |
| 440 | { |
| 441 | set_error( STATUS_ACCESS_DENIED ); |
| 442 | return 0; |
| 443 | } |
| 444 | |
Alexandre Julliard | 034c6c3 | 2009-06-17 20:53:32 +0200 | [diff] [blame] | 445 | /* compute the mask for the first long */ |
Alexandre Julliard | cb70931 | 2006-04-07 19:52:12 +0200 | [diff] [blame] | 446 | first_mask = ~0; |
Alexandre Julliard | 034c6c3 | 2009-06-17 20:53:32 +0200 | [diff] [blame] | 447 | first_offset = ptr % sizeof(long); |
Alexandre Julliard | cb70931 | 2006-04-07 19:52:12 +0200 | [diff] [blame] | 448 | memset( &first_mask, 0, first_offset ); |
| 449 | |
Alexandre Julliard | 034c6c3 | 2009-06-17 20:53:32 +0200 | [diff] [blame] | 450 | /* compute the mask for the last long */ |
| 451 | last_offset = (size + first_offset) % sizeof(long); |
| 452 | if (!last_offset) last_offset = sizeof(long); |
Alexandre Julliard | cb70931 | 2006-04-07 19:52:12 +0200 | [diff] [blame] | 453 | last_mask = 0; |
| 454 | memset( &last_mask, 0xff, last_offset ); |
| 455 | |
Alexandre Julliard | 034c6c3 | 2009-06-17 20:53:32 +0200 | [diff] [blame] | 456 | addr = (long *)(unsigned long)(ptr - first_offset); |
| 457 | len = (size + first_offset + sizeof(long) - 1) / sizeof(long); |
Alexandre Julliard | cb70931 | 2006-04-07 19:52:12 +0200 | [diff] [blame] | 458 | |
| 459 | if (suspend_for_ptrace( thread )) |
| 460 | { |
| 461 | if (!check_process_write_access( thread, addr, len )) |
| 462 | { |
| 463 | set_error( STATUS_ACCESS_DENIED ); |
| 464 | goto done; |
| 465 | } |
| 466 | /* first word is special */ |
| 467 | if (len > 1) |
| 468 | { |
Alexandre Julliard | 034c6c3 | 2009-06-17 20:53:32 +0200 | [diff] [blame] | 469 | memcpy( (char *)&data + first_offset, src, sizeof(long) - first_offset ); |
| 470 | src += sizeof(long) - first_offset; |
| 471 | if (write_thread_long( thread, addr++, data, first_mask ) == -1) goto done; |
Alexandre Julliard | cb70931 | 2006-04-07 19:52:12 +0200 | [diff] [blame] | 472 | first_offset = 0; |
| 473 | len--; |
| 474 | } |
| 475 | else last_mask &= first_mask; |
| 476 | |
| 477 | while (len > 1) |
| 478 | { |
Alexandre Julliard | 034c6c3 | 2009-06-17 20:53:32 +0200 | [diff] [blame] | 479 | memcpy( &data, src, sizeof(long) ); |
| 480 | src += sizeof(long); |
| 481 | if (write_thread_long( thread, addr++, data, ~0ul ) == -1) goto done; |
Alexandre Julliard | cb70931 | 2006-04-07 19:52:12 +0200 | [diff] [blame] | 482 | len--; |
| 483 | } |
| 484 | |
| 485 | /* last word is special too */ |
| 486 | memcpy( (char *)&data + first_offset, src, last_offset - first_offset ); |
Alexandre Julliard | 034c6c3 | 2009-06-17 20:53:32 +0200 | [diff] [blame] | 487 | if (write_thread_long( thread, addr, data, last_mask ) == -1) goto done; |
Alexandre Julliard | cb70931 | 2006-04-07 19:52:12 +0200 | [diff] [blame] | 488 | ret = 1; |
| 489 | |
| 490 | done: |
| 491 | resume_after_ptrace( thread ); |
| 492 | } |
| 493 | return ret; |
| 494 | } |
| 495 | |
| 496 | /* retrieve an LDT selector entry */ |
| 497 | void get_selector_entry( struct thread *thread, int entry, unsigned int *base, |
| 498 | unsigned int *limit, unsigned char *flags ) |
| 499 | { |
| 500 | if (!thread->process->ldt_copy) |
| 501 | { |
| 502 | set_error( STATUS_ACCESS_DENIED ); |
| 503 | return; |
| 504 | } |
| 505 | if (entry >= 8192) |
| 506 | { |
Eric Pouech | 5b1f3b1 | 2006-11-04 11:52:44 +0100 | [diff] [blame] | 507 | set_error( STATUS_ACCESS_VIOLATION ); |
Alexandre Julliard | cb70931 | 2006-04-07 19:52:12 +0200 | [diff] [blame] | 508 | return; |
| 509 | } |
| 510 | if (suspend_for_ptrace( thread )) |
| 511 | { |
Alexandre Julliard | 034c6c3 | 2009-06-17 20:53:32 +0200 | [diff] [blame] | 512 | unsigned char flags_buf[sizeof(long)]; |
| 513 | long *addr = (long *)(unsigned long)thread->process->ldt_copy + entry; |
| 514 | if (read_thread_long( thread, addr, (long *)base ) == -1) goto done; |
| 515 | if (read_thread_long( thread, addr + 8192, (long *)limit ) == -1) goto done; |
| 516 | addr = (long *)(unsigned long)thread->process->ldt_copy + 2*8192 + (entry / sizeof(long)); |
| 517 | if (read_thread_long( thread, addr, (long *)flags_buf ) == -1) goto done; |
| 518 | *flags = flags_buf[entry % sizeof(long)]; |
Alexandre Julliard | cb70931 | 2006-04-07 19:52:12 +0200 | [diff] [blame] | 519 | done: |
| 520 | resume_after_ptrace( thread ); |
| 521 | } |
| 522 | } |
Alexandre Julliard | 498742f | 2006-06-27 21:27:47 +0200 | [diff] [blame] | 523 | |
| 524 | |
| 525 | #if defined(linux) && (defined(__i386__) || defined(__x86_64__)) |
| 526 | |
| 527 | #ifdef HAVE_SYS_USER_H |
| 528 | # include <sys/user.h> |
| 529 | #endif |
| 530 | |
| 531 | /* debug register offset in struct user */ |
Alexandre Julliard | c242c3b | 2006-07-11 12:06:38 +0200 | [diff] [blame] | 532 | #define DR_OFFSET(dr) ((((struct user *)0)->u_debugreg) + (dr)) |
Alexandre Julliard | 498742f | 2006-06-27 21:27:47 +0200 | [diff] [blame] | 533 | |
| 534 | /* retrieve the thread x86 registers */ |
Alexandre Julliard | 5316dd0 | 2009-04-08 19:38:02 +0200 | [diff] [blame] | 535 | void get_thread_context( struct thread *thread, context_t *context, unsigned int flags ) |
Alexandre Julliard | 498742f | 2006-06-27 21:27:47 +0200 | [diff] [blame] | 536 | { |
Tijl Coosemans | 60833da | 2007-08-08 00:25:24 +0200 | [diff] [blame] | 537 | int i, pid = get_ptrace_tid(thread); |
Alexandre Julliard | c242c3b | 2006-07-11 12:06:38 +0200 | [diff] [blame] | 538 | long data[8]; |
Alexandre Julliard | 498742f | 2006-06-27 21:27:47 +0200 | [diff] [blame] | 539 | |
| 540 | /* all other regs are handled on the client side */ |
Alexandre Julliard | 5316dd0 | 2009-04-08 19:38:02 +0200 | [diff] [blame] | 541 | assert( flags == SERVER_CTX_DEBUG_REGISTERS ); |
Alexandre Julliard | 498742f | 2006-06-27 21:27:47 +0200 | [diff] [blame] | 542 | |
| 543 | if (!suspend_for_ptrace( thread )) return; |
| 544 | |
Alexandre Julliard | c242c3b | 2006-07-11 12:06:38 +0200 | [diff] [blame] | 545 | for (i = 0; i < 8; i++) |
| 546 | { |
| 547 | if (i == 4 || i == 5) continue; |
| 548 | errno = 0; |
| 549 | data[i] = ptrace( PTRACE_PEEKUSER, pid, DR_OFFSET(i), 0 ); |
| 550 | if ((data[i] == -1) && errno) |
| 551 | { |
| 552 | file_set_error(); |
| 553 | goto done; |
| 554 | } |
| 555 | } |
Eric Pouech | 9f49c6a | 2010-04-07 21:05:45 +0200 | [diff] [blame] | 556 | switch (context->cpu) |
| 557 | { |
| 558 | case CPU_x86: |
| 559 | context->debug.i386_regs.dr0 = data[0]; |
| 560 | context->debug.i386_regs.dr1 = data[1]; |
| 561 | context->debug.i386_regs.dr2 = data[2]; |
| 562 | context->debug.i386_regs.dr3 = data[3]; |
| 563 | context->debug.i386_regs.dr6 = data[6]; |
| 564 | context->debug.i386_regs.dr7 = data[7]; |
| 565 | break; |
| 566 | case CPU_x86_64: |
| 567 | context->debug.x86_64_regs.dr0 = data[0]; |
| 568 | context->debug.x86_64_regs.dr1 = data[1]; |
| 569 | context->debug.x86_64_regs.dr2 = data[2]; |
| 570 | context->debug.x86_64_regs.dr3 = data[3]; |
| 571 | context->debug.x86_64_regs.dr6 = data[6]; |
| 572 | context->debug.x86_64_regs.dr7 = data[7]; |
| 573 | break; |
| 574 | default: |
| 575 | set_error( STATUS_INVALID_PARAMETER ); |
| 576 | goto done; |
| 577 | } |
Alexandre Julliard | 5316dd0 | 2009-04-08 19:38:02 +0200 | [diff] [blame] | 578 | context->flags |= SERVER_CTX_DEBUG_REGISTERS; |
Alexandre Julliard | c242c3b | 2006-07-11 12:06:38 +0200 | [diff] [blame] | 579 | done: |
Alexandre Julliard | 498742f | 2006-06-27 21:27:47 +0200 | [diff] [blame] | 580 | resume_after_ptrace( thread ); |
| 581 | } |
| 582 | |
| 583 | /* set the thread x86 registers */ |
Alexandre Julliard | 5316dd0 | 2009-04-08 19:38:02 +0200 | [diff] [blame] | 584 | void set_thread_context( struct thread *thread, const context_t *context, unsigned int flags ) |
Alexandre Julliard | 498742f | 2006-06-27 21:27:47 +0200 | [diff] [blame] | 585 | { |
Tijl Coosemans | 60833da | 2007-08-08 00:25:24 +0200 | [diff] [blame] | 586 | int pid = get_ptrace_tid( thread ); |
Alexandre Julliard | 498742f | 2006-06-27 21:27:47 +0200 | [diff] [blame] | 587 | |
| 588 | /* all other regs are handled on the client side */ |
Alexandre Julliard | 5316dd0 | 2009-04-08 19:38:02 +0200 | [diff] [blame] | 589 | assert( flags == SERVER_CTX_DEBUG_REGISTERS ); |
Alexandre Julliard | 498742f | 2006-06-27 21:27:47 +0200 | [diff] [blame] | 590 | |
| 591 | if (!suspend_for_ptrace( thread )) return; |
| 592 | |
Eric Pouech | 9f49c6a | 2010-04-07 21:05:45 +0200 | [diff] [blame] | 593 | switch (context->cpu) |
| 594 | { |
| 595 | case CPU_x86: |
Grazvydas Ignotas | fa179b1 | 2011-07-15 23:19:10 +0300 | [diff] [blame] | 596 | /* Linux 2.6.33+ does DR0-DR3 alignment validation, so it has to know LEN bits first */ |
| 597 | if (ptrace( PTRACE_POKEUSER, pid, DR_OFFSET(7), context->debug.i386_regs.dr7 & 0xffff0000 ) == -1) goto error; |
Eric Pouech | 9f49c6a | 2010-04-07 21:05:45 +0200 | [diff] [blame] | 598 | if (ptrace( PTRACE_POKEUSER, pid, DR_OFFSET(0), context->debug.i386_regs.dr0 ) == -1) goto error; |
| 599 | if (thread->context) thread->context->debug.i386_regs.dr0 = context->debug.i386_regs.dr0; |
| 600 | if (ptrace( PTRACE_POKEUSER, pid, DR_OFFSET(1), context->debug.i386_regs.dr1 ) == -1) goto error; |
| 601 | if (thread->context) thread->context->debug.i386_regs.dr1 = context->debug.i386_regs.dr1; |
| 602 | if (ptrace( PTRACE_POKEUSER, pid, DR_OFFSET(2), context->debug.i386_regs.dr2 ) == -1) goto error; |
| 603 | if (thread->context) thread->context->debug.i386_regs.dr2 = context->debug.i386_regs.dr2; |
| 604 | if (ptrace( PTRACE_POKEUSER, pid, DR_OFFSET(3), context->debug.i386_regs.dr3 ) == -1) goto error; |
| 605 | if (thread->context) thread->context->debug.i386_regs.dr3 = context->debug.i386_regs.dr3; |
| 606 | if (ptrace( PTRACE_POKEUSER, pid, DR_OFFSET(6), context->debug.i386_regs.dr6 ) == -1) goto error; |
| 607 | if (thread->context) thread->context->debug.i386_regs.dr6 = context->debug.i386_regs.dr6; |
Grazvydas Ignotas | 2cc56f3 | 2011-07-15 23:19:11 +0300 | [diff] [blame] | 608 | /* Linux 2.6.33+ needs enable bits set briefly to update value returned by PEEKUSER later */ |
| 609 | ptrace( PTRACE_POKEUSER, pid, DR_OFFSET(7), context->debug.i386_regs.dr7 | 0x55 ); |
Eric Pouech | 9f49c6a | 2010-04-07 21:05:45 +0200 | [diff] [blame] | 610 | if (ptrace( PTRACE_POKEUSER, pid, DR_OFFSET(7), context->debug.i386_regs.dr7 ) == -1) goto error; |
| 611 | if (thread->context) thread->context->debug.i386_regs.dr7 = context->debug.i386_regs.dr7; |
| 612 | break; |
| 613 | case CPU_x86_64: |
Grazvydas Ignotas | fa179b1 | 2011-07-15 23:19:10 +0300 | [diff] [blame] | 614 | if (ptrace( PTRACE_POKEUSER, pid, DR_OFFSET(7), context->debug.x86_64_regs.dr7 & 0xffff0000 ) == -1) goto error; |
Eric Pouech | 9f49c6a | 2010-04-07 21:05:45 +0200 | [diff] [blame] | 615 | if (ptrace( PTRACE_POKEUSER, pid, DR_OFFSET(0), context->debug.x86_64_regs.dr0 ) == -1) goto error; |
| 616 | if (thread->context) thread->context->debug.x86_64_regs.dr0 = context->debug.x86_64_regs.dr0; |
| 617 | if (ptrace( PTRACE_POKEUSER, pid, DR_OFFSET(1), context->debug.x86_64_regs.dr1 ) == -1) goto error; |
| 618 | if (thread->context) thread->context->debug.x86_64_regs.dr1 = context->debug.x86_64_regs.dr1; |
| 619 | if (ptrace( PTRACE_POKEUSER, pid, DR_OFFSET(2), context->debug.x86_64_regs.dr2 ) == -1) goto error; |
| 620 | if (thread->context) thread->context->debug.x86_64_regs.dr2 = context->debug.x86_64_regs.dr2; |
| 621 | if (ptrace( PTRACE_POKEUSER, pid, DR_OFFSET(3), context->debug.x86_64_regs.dr3 ) == -1) goto error; |
| 622 | if (thread->context) thread->context->debug.x86_64_regs.dr3 = context->debug.x86_64_regs.dr3; |
| 623 | if (ptrace( PTRACE_POKEUSER, pid, DR_OFFSET(6), context->debug.x86_64_regs.dr6 ) == -1) goto error; |
| 624 | if (thread->context) thread->context->debug.x86_64_regs.dr6 = context->debug.x86_64_regs.dr6; |
Grazvydas Ignotas | 2cc56f3 | 2011-07-15 23:19:11 +0300 | [diff] [blame] | 625 | ptrace( PTRACE_POKEUSER, pid, DR_OFFSET(7), context->debug.x86_64_regs.dr7 | 0x55 ); |
Eric Pouech | 9f49c6a | 2010-04-07 21:05:45 +0200 | [diff] [blame] | 626 | if (ptrace( PTRACE_POKEUSER, pid, DR_OFFSET(7), context->debug.x86_64_regs.dr7 ) == -1) goto error; |
| 627 | if (thread->context) thread->context->debug.x86_64_regs.dr7 = context->debug.x86_64_regs.dr7; |
| 628 | break; |
| 629 | default: |
| 630 | set_error( STATUS_INVALID_PARAMETER ); |
| 631 | } |
Alexandre Julliard | 498742f | 2006-06-27 21:27:47 +0200 | [diff] [blame] | 632 | resume_after_ptrace( thread ); |
| 633 | return; |
| 634 | error: |
| 635 | file_set_error(); |
| 636 | resume_after_ptrace( thread ); |
| 637 | } |
| 638 | |
| 639 | #elif defined(__i386__) && defined(PTRACE_GETDBREGS) && defined(PTRACE_SETDBREGS) && \ |
| 640 | (defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__OpenBSD__) || defined(__NetBSD__)) |
| 641 | |
| 642 | #include <machine/reg.h> |
| 643 | |
| 644 | /* retrieve the thread x86 registers */ |
Alexandre Julliard | 5316dd0 | 2009-04-08 19:38:02 +0200 | [diff] [blame] | 645 | void get_thread_context( struct thread *thread, context_t *context, unsigned int flags ) |
Alexandre Julliard | 498742f | 2006-06-27 21:27:47 +0200 | [diff] [blame] | 646 | { |
Tijl Coosemans | 60833da | 2007-08-08 00:25:24 +0200 | [diff] [blame] | 647 | int pid = get_ptrace_tid(thread); |
Alexandre Julliard | 498742f | 2006-06-27 21:27:47 +0200 | [diff] [blame] | 648 | struct dbreg dbregs; |
| 649 | |
| 650 | /* all other regs are handled on the client side */ |
Alexandre Julliard | 5316dd0 | 2009-04-08 19:38:02 +0200 | [diff] [blame] | 651 | assert( flags == SERVER_CTX_DEBUG_REGISTERS ); |
Alexandre Julliard | 498742f | 2006-06-27 21:27:47 +0200 | [diff] [blame] | 652 | |
| 653 | if (!suspend_for_ptrace( thread )) return; |
| 654 | |
| 655 | if (ptrace( PTRACE_GETDBREGS, pid, (caddr_t) &dbregs, 0 ) == -1) file_set_error(); |
| 656 | else |
| 657 | { |
| 658 | #ifdef DBREG_DRX |
| 659 | /* needed for FreeBSD, the structure fields have changed under 5.x */ |
Alexandre Julliard | 5316dd0 | 2009-04-08 19:38:02 +0200 | [diff] [blame] | 660 | context->debug.i386_regs.dr0 = DBREG_DRX((&dbregs), 0); |
| 661 | context->debug.i386_regs.dr1 = DBREG_DRX((&dbregs), 1); |
| 662 | context->debug.i386_regs.dr2 = DBREG_DRX((&dbregs), 2); |
| 663 | context->debug.i386_regs.dr3 = DBREG_DRX((&dbregs), 3); |
| 664 | context->debug.i386_regs.dr6 = DBREG_DRX((&dbregs), 6); |
| 665 | context->debug.i386_regs.dr7 = DBREG_DRX((&dbregs), 7); |
Alexandre Julliard | 498742f | 2006-06-27 21:27:47 +0200 | [diff] [blame] | 666 | #else |
Alexandre Julliard | 5316dd0 | 2009-04-08 19:38:02 +0200 | [diff] [blame] | 667 | context->debug.i386_regs.dr0 = dbregs.dr0; |
| 668 | context->debug.i386_regs.dr1 = dbregs.dr1; |
| 669 | context->debug.i386_regs.dr2 = dbregs.dr2; |
| 670 | context->debug.i386_regs.dr3 = dbregs.dr3; |
| 671 | context->debug.i386_regs.dr6 = dbregs.dr6; |
| 672 | context->debug.i386_regs.dr7 = dbregs.dr7; |
Alexandre Julliard | 498742f | 2006-06-27 21:27:47 +0200 | [diff] [blame] | 673 | #endif |
Alexandre Julliard | 5316dd0 | 2009-04-08 19:38:02 +0200 | [diff] [blame] | 674 | context->flags |= SERVER_CTX_DEBUG_REGISTERS; |
Alexandre Julliard | 498742f | 2006-06-27 21:27:47 +0200 | [diff] [blame] | 675 | } |
| 676 | resume_after_ptrace( thread ); |
| 677 | } |
| 678 | |
| 679 | /* set the thread x86 registers */ |
Alexandre Julliard | 5316dd0 | 2009-04-08 19:38:02 +0200 | [diff] [blame] | 680 | void set_thread_context( struct thread *thread, const context_t *context, unsigned int flags ) |
Alexandre Julliard | 498742f | 2006-06-27 21:27:47 +0200 | [diff] [blame] | 681 | { |
Tijl Coosemans | 60833da | 2007-08-08 00:25:24 +0200 | [diff] [blame] | 682 | int pid = get_ptrace_tid(thread); |
Alexandre Julliard | 498742f | 2006-06-27 21:27:47 +0200 | [diff] [blame] | 683 | struct dbreg dbregs; |
| 684 | |
| 685 | /* all other regs are handled on the client side */ |
Alexandre Julliard | 5316dd0 | 2009-04-08 19:38:02 +0200 | [diff] [blame] | 686 | assert( flags == SERVER_CTX_DEBUG_REGISTERS ); |
Alexandre Julliard | 498742f | 2006-06-27 21:27:47 +0200 | [diff] [blame] | 687 | |
| 688 | if (!suspend_for_ptrace( thread )) return; |
| 689 | |
| 690 | #ifdef DBREG_DRX |
| 691 | /* needed for FreeBSD, the structure fields have changed under 5.x */ |
Alexandre Julliard | 5316dd0 | 2009-04-08 19:38:02 +0200 | [diff] [blame] | 692 | DBREG_DRX((&dbregs), 0) = context->debug.i386_regs.dr0; |
| 693 | DBREG_DRX((&dbregs), 1) = context->debug.i386_regs.dr1; |
| 694 | DBREG_DRX((&dbregs), 2) = context->debug.i386_regs.dr2; |
| 695 | DBREG_DRX((&dbregs), 3) = context->debug.i386_regs.dr3; |
Alexandre Julliard | 498742f | 2006-06-27 21:27:47 +0200 | [diff] [blame] | 696 | DBREG_DRX((&dbregs), 4) = 0; |
| 697 | DBREG_DRX((&dbregs), 5) = 0; |
Alexandre Julliard | 5316dd0 | 2009-04-08 19:38:02 +0200 | [diff] [blame] | 698 | DBREG_DRX((&dbregs), 6) = context->debug.i386_regs.dr6; |
| 699 | DBREG_DRX((&dbregs), 7) = context->debug.i386_regs.dr7; |
Alexandre Julliard | 498742f | 2006-06-27 21:27:47 +0200 | [diff] [blame] | 700 | #else |
Alexandre Julliard | 5316dd0 | 2009-04-08 19:38:02 +0200 | [diff] [blame] | 701 | dbregs.dr0 = context->debug.i386_regs.dr0; |
| 702 | dbregs.dr1 = context->debug.i386_regs.dr1; |
| 703 | dbregs.dr2 = context->debug.i386_regs.dr2; |
| 704 | dbregs.dr3 = context->debug.i386_regs.dr3; |
Alexandre Julliard | 498742f | 2006-06-27 21:27:47 +0200 | [diff] [blame] | 705 | dbregs.dr4 = 0; |
| 706 | dbregs.dr5 = 0; |
Alexandre Julliard | 5316dd0 | 2009-04-08 19:38:02 +0200 | [diff] [blame] | 707 | dbregs.dr6 = context->debug.i386_regs.dr6; |
| 708 | dbregs.dr7 = context->debug.i386_regs.dr7; |
Alexandre Julliard | 498742f | 2006-06-27 21:27:47 +0200 | [diff] [blame] | 709 | #endif |
| 710 | if (ptrace( PTRACE_SETDBREGS, pid, (caddr_t) &dbregs, 0 ) == -1) file_set_error(); |
Alexandre Julliard | 5316dd0 | 2009-04-08 19:38:02 +0200 | [diff] [blame] | 711 | else if (thread->context) |
| 712 | thread->context->debug.i386_regs = context->debug.i386_regs; /* update the cached values */ |
Alexandre Julliard | 498742f | 2006-06-27 21:27:47 +0200 | [diff] [blame] | 713 | resume_after_ptrace( thread ); |
| 714 | } |
| 715 | |
| 716 | #else /* linux || __FreeBSD__ */ |
| 717 | |
| 718 | /* retrieve the thread x86 registers */ |
Alexandre Julliard | 5316dd0 | 2009-04-08 19:38:02 +0200 | [diff] [blame] | 719 | void get_thread_context( struct thread *thread, context_t *context, unsigned int flags ) |
Alexandre Julliard | 498742f | 2006-06-27 21:27:47 +0200 | [diff] [blame] | 720 | { |
| 721 | } |
| 722 | |
| 723 | /* set the thread x86 debug registers */ |
Alexandre Julliard | 5316dd0 | 2009-04-08 19:38:02 +0200 | [diff] [blame] | 724 | void set_thread_context( struct thread *thread, const context_t *context, unsigned int flags ) |
Alexandre Julliard | 498742f | 2006-06-27 21:27:47 +0200 | [diff] [blame] | 725 | { |
| 726 | } |
| 727 | |
| 728 | #endif /* linux || __FreeBSD__ */ |
Alexandre Julliard | c273498 | 2006-12-29 20:38:49 +0100 | [diff] [blame] | 729 | |
| 730 | #endif /* USE_PTRACE */ |