ptrace.c 8.72 KB
Newer Older
1 2 3 4
/*
 * Server-side ptrace support
 *
 * Copyright (C) 1999 Alexandre Julliard
5 6 7 8 9 10 11 12 13 14 15 16 17 18
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19 20 21 22 23 24 25 26
 */

#include "config.h"

#include <assert.h>
#include <errno.h>
#include <stdio.h>
#include <signal.h>
Alexandre Julliard's avatar
Alexandre Julliard committed
27
#include <sys/types.h>
28 29 30
#ifdef HAVE_SYS_PTRACE_H
# include <sys/ptrace.h>
#endif
31
#ifdef HAVE_SYS_WAIT_H
32
# include <sys/wait.h>
33 34 35
#endif
#include <unistd.h>

36
#include "file.h"
37 38 39 40 41 42
#include "process.h"
#include "thread.h"

#ifndef PTRACE_CONT
#define PTRACE_CONT PT_CONTINUE
#endif
43 44 45
#ifndef PTRACE_SINGLESTEP
#define PTRACE_SINGLESTEP PT_STEP
#endif
46 47 48 49 50 51 52 53 54 55 56 57 58
#ifndef PTRACE_ATTACH
#define PTRACE_ATTACH PT_ATTACH
#endif
#ifndef PTRACE_DETACH
#define PTRACE_DETACH PT_DETACH
#endif
#ifndef PTRACE_PEEKDATA
#define PTRACE_PEEKDATA PT_READ_D
#endif
#ifndef PTRACE_POKEDATA
#define PTRACE_POKEDATA PT_WRITE_D
#endif

59
#ifndef HAVE_SYS_PTRACE_H
60 61 62 63 64
#define PT_CONTINUE 0
#define PT_ATTACH   1
#define PT_DETACH   2
#define PT_READ_D   3
#define PT_WRITE_D  4
65
#define PT_STEP     5
66 67
inline static int ptrace(int req, ...) { errno = EPERM; return -1; /*FAIL*/ }
#endif  /* HAVE_SYS_PTRACE_H */
68

69
static const int use_ptrace = 1;  /* set to 0 to disable ptrace */
70

71
/* handle a status returned by wait4 */
72
static int handle_child_status( struct thread *thread, int pid, int status, int want_sig )
73 74 75 76
{
    if (WIFSTOPPED(status))
    {
        int sig = WSTOPSIG(status);
77
        if (debug_level && thread)
78
            fprintf( stderr, "%04x: *signal* signal=%d\n", thread->id, sig );
79
        if (sig != want_sig)
80
        {
81
            /* ignore other signals for now */
82 83 84 85
            if (thread && get_thread_single_step( thread ))
                ptrace( PTRACE_SINGLESTEP, pid, (caddr_t)1, sig );
            else
                ptrace( PTRACE_CONT, pid, (caddr_t)1, sig );
86
        }
87
        return sig;
88
    }
89
    if (thread && (WIFSIGNALED(status) || WIFEXITED(status)))
90
    {
91
        thread->attached = 0;
92
        thread->unix_pid = -1;
93
        thread->unix_tid = -1;
94
        if (debug_level)
95 96
        {
            if (WIFSIGNALED(status))
97 98
                fprintf( stderr, "%04x: *exited* signal=%d\n",
                         thread->id, WTERMSIG(status) );
99
            else
100 101
                fprintf( stderr, "%04x: *exited* status=%d\n",
                         thread->id, WEXITSTATUS(status) );
102
        }
103
    }
104 105 106
    return 0;
}

107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
/* wait4 wrapper to handle missing __WALL flag in older kernels */
static inline pid_t wait4_wrapper( pid_t pid, int *status, int options, struct rusage *usage )
{
#ifdef __WALL
    static int wall_flag = __WALL;

    for (;;)
    {
        pid_t ret = wait4( pid, status, options | wall_flag, usage );
        if (ret != -1 || !wall_flag || errno != EINVAL) return ret;
        wall_flag = 0;
    }
#else
    return wait4( pid, status, options, usage );
#endif
}

124
/* handle a SIGCHLD signal */
125
void sigchld_callback(void)
126 127 128 129
{
    int pid, status;

    for (;;)
130
    {
131
        if (!(pid = wait4_wrapper( -1, &status, WUNTRACED | WNOHANG, NULL ))) break;
132
        if (pid != -1) handle_child_status( get_thread_from_pid(pid), pid, status, -1 );
133
        else break;
134
    }
135 136 137
}

/* wait for a ptraced child to get a certain signal */
138
static int wait4_thread( struct thread *thread, int signal )
139 140 141
{
    int res, status;

142
    for (;;)
143
    {
144
        if ((res = wait4_wrapper( get_ptrace_pid(thread), &status, WUNTRACED, NULL )) == -1)
145
        {
146
            if (errno == EINTR) continue;
147 148 149
            if (errno == ECHILD)  /* must have died */
            {
                thread->unix_pid = -1;
150
                thread->unix_tid = -1;
151 152 153
                thread->attached = 0;
            }
            else perror( "wait4" );
154
            return 0;
155
        }
156
        res = handle_child_status( thread, res, status, signal );
157 158
        if (!res || res == signal) break;
    }
159
    return (thread->unix_pid != -1);
160 161
}

162 163 164
/* return the Unix pid to use in ptrace calls for a given thread */
int get_ptrace_pid( struct thread *thread )
{
165
    if (thread->unix_tid != -1) return thread->unix_tid;
166 167 168
    return thread->unix_pid;
}

169 170 171 172 173 174 175
/* send a Unix signal to a specific thread */
int send_thread_signal( struct thread *thread, int sig )
{
    int ret = -1;

    if (thread->unix_pid != -1)
    {
176 177 178 179 180
        if (thread->unix_tid != -1)
        {
            ret = tkill( thread->unix_tid, sig );
            if (ret == -1 && errno == ENOSYS) ret = kill( thread->unix_pid, sig );
        }
181 182
        else ret = kill( thread->unix_pid, sig );

183 184 185
        if (ret == -1 && errno == ESRCH) /* thread got killed */
        {
            thread->unix_pid = -1;
186
            thread->unix_tid = -1;
187 188 189 190 191 192
            thread->attached = 0;
        }
    }
    return (ret != -1);
}

193 194 195 196
/* attach to a Unix thread */
static int attach_thread( struct thread *thread )
{
    /* this may fail if the client is already being debugged */
197
    if (!use_ptrace) return 0;
198
    if (ptrace( PTRACE_ATTACH, get_ptrace_pid(thread), 0, 0 ) == -1)
199
    {
200
        if (errno == ESRCH) thread->unix_pid = thread->unix_tid = -1;  /* thread got killed */
201 202
        return 0;
    }
203
    if (debug_level) fprintf( stderr, "%04x: *attached*\n", thread->id );
204
    thread->attached = 1;
205
    return wait4_thread( thread, SIGSTOP );
206 207 208
}

/* detach from a Unix thread and kill it */
209
void detach_thread( struct thread *thread, int sig )
210
{
211
    if (thread->unix_pid == -1) return;
212 213
    if (thread->attached)
    {
214
        /* make sure it is stopped */
215
        suspend_for_ptrace( thread );
216 217
        if (sig) send_thread_signal( thread, sig );
        if (thread->unix_pid == -1) return;
218
        if (debug_level) fprintf( stderr, "%04x: *detached*\n", thread->id );
219
        ptrace( PTRACE_DETACH, get_ptrace_pid(thread), (caddr_t)1, sig );
220 221
        thread->attached = 0;
    }
222
    else if (sig) send_thread_signal( thread, sig );
223 224
}

225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255
/* attach to a Unix process with ptrace */
int attach_process( struct process *process )
{
    struct thread *thread;
    int ret = 1;

    if (!process->thread_list)  /* need at least one running thread */
    {
        set_error( STATUS_ACCESS_DENIED );
        return 0;
    }
    for (thread = process->thread_list; thread; thread = thread->proc_next)
    {
        if (thread->attached) continue;
        if (suspend_for_ptrace( thread )) resume_after_ptrace( thread );
        else ret = 0;
    }
    return ret;
}

/* detach from a ptraced Unix process */
void detach_process( struct process *process )
{
    struct thread *thread;

    for (thread = process->thread_list; thread; thread = thread->proc_next)
    {
        if (thread->attached) detach_thread( thread, 0 );
    }
}

256
/* suspend a thread to allow using ptrace on it */
257
/* you must do a resume_after_ptrace when finished with the thread */
258 259
int suspend_for_ptrace( struct thread *thread )
{
260 261 262
    /* can't stop a thread while initialisation is in progress */
    if (thread->unix_pid == -1 || !is_process_init_done(thread->process)) goto error;

263 264
    if (thread->attached)
    {
265 266
        if (!send_thread_signal( thread, SIGSTOP )) goto error;
        if (!wait4_thread( thread, SIGSTOP )) goto error;
267 268
        return 1;
    }
269 270
    if (attach_thread( thread )) return 1;
 error:
271 272 273 274
    set_error( STATUS_ACCESS_DENIED );
    return 0;
}

Eric Pouech's avatar
Eric Pouech committed
275
/* resume a thread after we have used ptrace on it */
276 277 278 279 280
void resume_after_ptrace( struct thread *thread )
{
    if (thread->unix_pid == -1) return;
    assert( thread->attached );
    ptrace( get_thread_single_step(thread) ? PTRACE_SINGLESTEP : PTRACE_CONT,
281
            get_ptrace_pid(thread), (caddr_t)1, 0 /* cancel the SIGSTOP */ );
282 283
}

284 285 286
/* read an int from a thread address space */
int read_thread_int( struct thread *thread, const int *addr, int *data )
{
287
    errno = 0;
288
    *data = ptrace( PTRACE_PEEKDATA, get_ptrace_pid(thread), (caddr_t)addr, 0 );
289
    if ( *data == -1 && errno)
290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305
    {
        file_set_error();
        return -1;
    }
    return 0;
}

/* write an int to a thread address space */
int write_thread_int( struct thread *thread, int *addr, int data, unsigned int mask )
{
    int res;
    if (mask != ~0)
    {
        if (read_thread_int( thread, addr, &res ) == -1) return -1;
        data = (data & mask) | (res & ~mask);
    }
306
    if ((res = ptrace( PTRACE_POKEDATA, get_ptrace_pid(thread), (caddr_t)addr, data )) == -1)
307
        file_set_error();
308 309
    return res;
}