Moved ptrace support to ptrace.c. Tried to improve portability.
This commit is contained in:
parent
3b832225f1
commit
578c10090b
|
@ -18,6 +18,7 @@ C_SRCS = \
|
|||
object.c \
|
||||
pipe.c \
|
||||
process.c \
|
||||
ptrace.c \
|
||||
request.c \
|
||||
snapshot.c \
|
||||
select.c \
|
||||
|
|
|
@ -316,30 +316,6 @@ static void set_process_info( struct process *process,
|
|||
}
|
||||
}
|
||||
|
||||
/* wrapper for reading an int with ptrace */
|
||||
static inline int read_word( int pid, const int *addr, int *data )
|
||||
{
|
||||
if (((*data = ptrace( PT_READ_D, pid, addr )) == -1) && errno)
|
||||
{
|
||||
file_set_error();
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* wrapper for writing an int with ptrace */
|
||||
static inline int write_word( int pid, int *addr, int data, unsigned int mask )
|
||||
{
|
||||
int res;
|
||||
if (mask != ~0)
|
||||
{
|
||||
if (read_word( pid, addr, &res ) == -1) return -1;
|
||||
data = (data & mask) | (res & ~mask);
|
||||
}
|
||||
if ((res = ptrace( PT_WRITE_D, pid, addr, data )) == -1) file_set_error();
|
||||
return res;
|
||||
}
|
||||
|
||||
/* read data from a process memory space */
|
||||
/* len is the total size (in ints), max is the size we can actually store in the output buffer */
|
||||
/* we read the total size in all cases to check for permissions */
|
||||
|
@ -347,7 +323,6 @@ static void read_process_memory( struct process *process, const int *addr,
|
|||
size_t len, size_t max, int *dest )
|
||||
{
|
||||
struct thread *thread = process->thread_list;
|
||||
int pid = thread->unix_pid;
|
||||
|
||||
if ((unsigned int)addr % sizeof(int)) /* address must be aligned */
|
||||
{
|
||||
|
@ -359,7 +334,7 @@ static void read_process_memory( struct process *process, const int *addr,
|
|||
{
|
||||
while (len > 0 && max)
|
||||
{
|
||||
if (read_word( pid, addr++, dest++ ) == -1) goto done;
|
||||
if (read_thread_int( thread, addr++, dest++ ) == -1) goto done;
|
||||
max--;
|
||||
len--;
|
||||
}
|
||||
|
@ -371,9 +346,9 @@ static void read_process_memory( struct process *process, const int *addr,
|
|||
{
|
||||
addr += page;
|
||||
len -= page;
|
||||
if (read_word( pid, addr - 1, &dummy ) == -1) goto done;
|
||||
if (read_thread_int( thread, addr - 1, &dummy ) == -1) goto done;
|
||||
}
|
||||
if (len && (read_word( pid, addr + len - 1, &dummy ) == -1)) goto done;
|
||||
if (len && (read_thread_int( thread, addr + len - 1, &dummy ) == -1)) goto done;
|
||||
}
|
||||
}
|
||||
else set_error( ERROR_ACCESS_DENIED );
|
||||
|
@ -389,7 +364,6 @@ static void write_process_memory( struct process *process, int *addr, size_t len
|
|||
unsigned int last_mask, const int *src )
|
||||
{
|
||||
struct thread *thread = process->thread_list;
|
||||
int pid = thread->unix_pid;
|
||||
|
||||
if (!len || ((unsigned int)addr % sizeof(int))) /* address must be aligned */
|
||||
{
|
||||
|
@ -402,7 +376,7 @@ static void write_process_memory( struct process *process, int *addr, size_t len
|
|||
/* first word is special */
|
||||
if (len > 1)
|
||||
{
|
||||
if (write_word( pid, addr++, *src++, first_mask ) == -1) goto done;
|
||||
if (write_thread_int( thread, addr++, *src++, first_mask ) == -1) goto done;
|
||||
len--;
|
||||
max--;
|
||||
}
|
||||
|
@ -410,7 +384,7 @@ static void write_process_memory( struct process *process, int *addr, size_t len
|
|||
|
||||
while (len > 1 && max)
|
||||
{
|
||||
if (write_word( pid, addr++, *src++, ~0 ) == -1) goto done;
|
||||
if (write_thread_int( thread, addr++, *src++, ~0 ) == -1) goto done;
|
||||
max--;
|
||||
len--;
|
||||
}
|
||||
|
@ -418,7 +392,7 @@ static void write_process_memory( struct process *process, int *addr, size_t len
|
|||
if (max)
|
||||
{
|
||||
/* last word is special too */
|
||||
if (write_word( pid, addr, *src, last_mask ) == -1) goto done;
|
||||
if (write_thread_int( thread, addr, *src, last_mask ) == -1) goto done;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -428,9 +402,9 @@ static void write_process_memory( struct process *process, int *addr, size_t len
|
|||
{
|
||||
addr += page;
|
||||
len -= page;
|
||||
if (write_word( pid, addr - 1, 0, 0 ) == -1) goto done;
|
||||
if (write_thread_int( thread, addr - 1, 0, 0 ) == -1) goto done;
|
||||
}
|
||||
if (len && (write_word( pid, addr + len - 1, 0, 0 ) == -1)) goto done;
|
||||
if (len && (write_thread_int( thread, addr + len - 1, 0, 0 ) == -1)) goto done;
|
||||
}
|
||||
}
|
||||
else set_error( ERROR_ACCESS_DENIED );
|
||||
|
|
|
@ -0,0 +1,164 @@
|
|||
/*
|
||||
* Server-side ptrace support
|
||||
*
|
||||
* Copyright (C) 1999 Alexandre Julliard
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <signal.h>
|
||||
#include <sys/ptrace.h>
|
||||
#ifdef HAVE_SYS_WAIT_H
|
||||
#include <sys/wait.h>
|
||||
#endif
|
||||
#include <unistd.h>
|
||||
|
||||
#include "process.h"
|
||||
#include "thread.h"
|
||||
|
||||
|
||||
#ifndef PTRACE_CONT
|
||||
#define PTRACE_CONT PT_CONTINUE
|
||||
#endif
|
||||
#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
|
||||
|
||||
static const int use_ptrace = 1; /* set to 0 to disable ptrace */
|
||||
|
||||
|
||||
/* wait for a ptraced child to get a certain signal */
|
||||
/* if the signal is 0, we simply check if anything is pending and return at once */
|
||||
void wait4_thread( struct thread *thread, int signal )
|
||||
{
|
||||
int status;
|
||||
int pid;
|
||||
|
||||
restart:
|
||||
pid = thread ? thread->unix_pid : -1;
|
||||
if ((pid = wait4( pid, &status, WUNTRACED | (signal ? 0 : WNOHANG), NULL )) == -1)
|
||||
{
|
||||
perror( "wait4" );
|
||||
return;
|
||||
}
|
||||
if (WIFSTOPPED(status))
|
||||
{
|
||||
int sig = WSTOPSIG(status);
|
||||
if (debug_level) fprintf( stderr, "ptrace: pid %d got sig %d\n", pid, sig );
|
||||
switch(sig)
|
||||
{
|
||||
case SIGSTOP: /* continue at once if not suspended */
|
||||
if (!thread)
|
||||
if (!(thread = get_thread_from_pid( pid ))) break;
|
||||
if (!(thread->process->suspend + thread->suspend))
|
||||
ptrace( PTRACE_CONT, pid, 0, sig );
|
||||
break;
|
||||
default: /* ignore other signals for now */
|
||||
ptrace( PTRACE_CONT, pid, 0, sig );
|
||||
break;
|
||||
}
|
||||
if (signal && sig != signal) goto restart;
|
||||
}
|
||||
else if (WIFSIGNALED(status))
|
||||
{
|
||||
int exit_code = WTERMSIG(status);
|
||||
if (debug_level)
|
||||
fprintf( stderr, "ptrace: pid %d killed by sig %d\n", pid, exit_code );
|
||||
if (!thread)
|
||||
if (!(thread = get_thread_from_pid( pid ))) return;
|
||||
if (thread->client) remove_client( thread->client, exit_code );
|
||||
}
|
||||
else if (WIFEXITED(status))
|
||||
{
|
||||
int exit_code = WEXITSTATUS(status);
|
||||
if (debug_level)
|
||||
fprintf( stderr, "ptrace: pid %d exited with status %d\n", pid, exit_code );
|
||||
if (!thread)
|
||||
if (!(thread = get_thread_from_pid( pid ))) return;
|
||||
if (thread->client) remove_client( thread->client, exit_code );
|
||||
}
|
||||
else fprintf( stderr, "wait4: pid %d unknown status %x\n", pid, status );
|
||||
}
|
||||
|
||||
/* attach to a Unix thread */
|
||||
static int attach_thread( struct thread *thread )
|
||||
{
|
||||
/* this may fail if the client is already being debugged */
|
||||
if (!use_ptrace || (ptrace( PTRACE_ATTACH, thread->unix_pid, 0, 0 ) == -1)) return 0;
|
||||
if (debug_level) fprintf( stderr, "ptrace: attached to pid %d\n", thread->unix_pid );
|
||||
thread->attached = 1;
|
||||
wait4_thread( thread, SIGSTOP );
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* detach from a Unix thread and kill it */
|
||||
void detach_thread( struct thread *thread )
|
||||
{
|
||||
if (!thread->unix_pid) return;
|
||||
kill( thread->unix_pid, SIGTERM );
|
||||
if (thread->suspend + thread->process->suspend) continue_thread( thread );
|
||||
if (thread->attached)
|
||||
{
|
||||
wait4_thread( thread, SIGTERM );
|
||||
if (debug_level) fprintf( stderr, "ptrace: detaching from %d\n", thread->unix_pid );
|
||||
ptrace( PTRACE_DETACH, thread->unix_pid, 0, SIGTERM );
|
||||
thread->attached = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* stop a thread (at the Unix level) */
|
||||
void stop_thread( struct thread *thread )
|
||||
{
|
||||
/* can't stop a thread while initialisation is in progress */
|
||||
if (!thread->unix_pid || thread->process->init_event) return;
|
||||
/* first try to attach to it */
|
||||
if (!thread->attached)
|
||||
if (attach_thread( thread )) return; /* this will have stopped it */
|
||||
/* attached already, or attach failed -> send a signal */
|
||||
kill( thread->unix_pid, SIGSTOP );
|
||||
if (thread->attached) wait4_thread( thread, SIGSTOP );
|
||||
}
|
||||
|
||||
/* make a thread continue (at the Unix level) */
|
||||
void continue_thread( struct thread *thread )
|
||||
{
|
||||
if (!thread->unix_pid) return;
|
||||
if (!thread->attached) kill( thread->unix_pid, SIGCONT );
|
||||
else ptrace( PTRACE_CONT, thread->unix_pid, 0, SIGSTOP );
|
||||
}
|
||||
|
||||
/* read an int from a thread address space */
|
||||
int read_thread_int( struct thread *thread, const int *addr, int *data )
|
||||
{
|
||||
if (((*data = ptrace( PTRACE_PEEKDATA, thread->unix_pid, addr )) == -1) && errno)
|
||||
{
|
||||
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);
|
||||
}
|
||||
if ((res = ptrace( PTRACE_POKEDATA, thread->unix_pid, addr, data )) == -1) file_set_error();
|
||||
return res;
|
||||
}
|
120
server/thread.c
120
server/thread.c
|
@ -15,10 +15,8 @@
|
|||
#ifdef HAVE_SYS_MMAN_H
|
||||
#include <sys/mman.h>
|
||||
#endif
|
||||
#include <sys/ptrace.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/uio.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
|
@ -82,7 +80,6 @@ static const struct object_ops thread_ops =
|
|||
destroy_thread
|
||||
};
|
||||
|
||||
static const int use_ptrace = 1; /* set to 0 to disable ptrace */
|
||||
static struct thread *first_thread;
|
||||
|
||||
/* allocate the buffer for the communication with the client */
|
||||
|
@ -214,6 +211,14 @@ struct thread *get_thread_from_handle( int handle, unsigned int access )
|
|||
access, &thread_ops );
|
||||
}
|
||||
|
||||
/* find a thread from a Unix pid */
|
||||
struct thread *get_thread_from_pid( int pid )
|
||||
{
|
||||
struct thread *t = first_thread;
|
||||
while (t && (t->unix_pid != pid)) t = t->next;
|
||||
return t;
|
||||
}
|
||||
|
||||
/* set all information about a thread */
|
||||
static void set_thread_info( struct thread *thread,
|
||||
struct set_thread_info_request *req )
|
||||
|
@ -227,115 +232,6 @@ static void set_thread_info( struct thread *thread,
|
|||
}
|
||||
}
|
||||
|
||||
/* find a thread from a Unix pid */
|
||||
static struct thread *get_thread_from_pid( int pid )
|
||||
{
|
||||
struct thread *t = first_thread;
|
||||
while (t && (t->unix_pid != pid)) t = t->next;
|
||||
return t;
|
||||
}
|
||||
|
||||
/* wait for a ptraced child to get a certain signal */
|
||||
/* if the signal is 0, we simply check if anything is pending and return at once */
|
||||
void wait4_thread( struct thread *thread, int signal )
|
||||
{
|
||||
int status;
|
||||
int pid;
|
||||
|
||||
|
||||
restart:
|
||||
pid = thread ? thread->unix_pid : -1;
|
||||
if ((pid = wait4( pid, &status, WUNTRACED | (signal ? 0 : WNOHANG), NULL )) == -1)
|
||||
{
|
||||
perror( "wait4" );
|
||||
return;
|
||||
}
|
||||
if (WIFSTOPPED(status))
|
||||
{
|
||||
int sig = WSTOPSIG(status);
|
||||
if (debug_level) fprintf( stderr, "ptrace: pid %d got sig %d\n", pid, sig );
|
||||
switch(sig)
|
||||
{
|
||||
case SIGSTOP: /* continue at once if not suspended */
|
||||
if (!thread)
|
||||
if (!(thread = get_thread_from_pid( pid ))) break;
|
||||
if (!(thread->process->suspend + thread->suspend))
|
||||
ptrace( PT_CONTINUE, pid, 0, sig );
|
||||
break;
|
||||
default: /* ignore other signals for now */
|
||||
ptrace( PT_CONTINUE, pid, 0, sig );
|
||||
break;
|
||||
}
|
||||
if (signal && sig != signal) goto restart;
|
||||
}
|
||||
else if (WIFSIGNALED(status))
|
||||
{
|
||||
int exit_code = WTERMSIG(status);
|
||||
if (debug_level)
|
||||
fprintf( stderr, "ptrace: pid %d killed by sig %d\n", pid, exit_code );
|
||||
if (!thread)
|
||||
if (!(thread = get_thread_from_pid( pid ))) return;
|
||||
if (thread->client) remove_client( thread->client, exit_code );
|
||||
}
|
||||
else if (WIFEXITED(status))
|
||||
{
|
||||
int exit_code = WEXITSTATUS(status);
|
||||
if (debug_level)
|
||||
fprintf( stderr, "ptrace: pid %d exited with status %d\n", pid, exit_code );
|
||||
if (!thread)
|
||||
if (!(thread = get_thread_from_pid( pid ))) return;
|
||||
if (thread->client) remove_client( thread->client, exit_code );
|
||||
}
|
||||
else fprintf( stderr, "wait4: pid %d unknown status %x\n", pid, status );
|
||||
}
|
||||
|
||||
/* attach to a Unix thread */
|
||||
static int attach_thread( struct thread *thread )
|
||||
{
|
||||
/* this may fail if the client is already being debugged */
|
||||
if (!use_ptrace || (ptrace( PT_ATTACH, thread->unix_pid, 0, 0 ) == -1)) return 0;
|
||||
if (debug_level) fprintf( stderr, "ptrace: attached to pid %d\n", thread->unix_pid );
|
||||
thread->attached = 1;
|
||||
wait4_thread( thread, SIGSTOP );
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* detach from a Unix thread and kill it */
|
||||
static void detach_thread( struct thread *thread )
|
||||
{
|
||||
if (!thread->unix_pid) return;
|
||||
kill( thread->unix_pid, SIGTERM );
|
||||
if (thread->suspend + thread->process->suspend) continue_thread( thread );
|
||||
if (thread->attached)
|
||||
{
|
||||
wait4_thread( thread, SIGTERM );
|
||||
if (debug_level) fprintf( stderr, "ptrace: detaching from %d\n", thread->unix_pid );
|
||||
ptrace( PT_DETACH, thread->unix_pid, 0, SIGTERM );
|
||||
thread->attached = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* stop a thread (at the Unix level) */
|
||||
void stop_thread( struct thread *thread )
|
||||
{
|
||||
/* can't stop a thread while initialisation is in progress */
|
||||
if (!thread->unix_pid || thread->process->init_event) return;
|
||||
/* first try to attach to it */
|
||||
if (!thread->attached)
|
||||
if (attach_thread( thread )) return; /* this will have stopped it */
|
||||
/* attached already, or attach failed -> send a signal */
|
||||
kill( thread->unix_pid, SIGSTOP );
|
||||
if (thread->attached) wait4_thread( thread, SIGSTOP );
|
||||
}
|
||||
|
||||
/* make a thread continue (at the Unix level) */
|
||||
void continue_thread( struct thread *thread )
|
||||
{
|
||||
if (!thread->unix_pid) return;
|
||||
if (!thread->attached) kill( thread->unix_pid, SIGCONT );
|
||||
else ptrace( PT_CONTINUE, thread->unix_pid, 0, SIGSTOP );
|
||||
}
|
||||
|
||||
/* suspend a thread */
|
||||
int suspend_thread( struct thread *thread, int check_limit )
|
||||
{
|
||||
|
|
|
@ -66,9 +66,7 @@ extern struct thread *current;
|
|||
extern void create_initial_thread( int fd );
|
||||
extern struct thread *get_thread_from_id( void *id );
|
||||
extern struct thread *get_thread_from_handle( int handle, unsigned int access );
|
||||
extern void wait4_thread( struct thread *thread, int wait );
|
||||
extern void stop_thread( struct thread *thread );
|
||||
extern void continue_thread( struct thread *thread );
|
||||
extern struct thread *get_thread_from_pid( int pid );
|
||||
extern int suspend_thread( struct thread *thread, int check_limit );
|
||||
extern int resume_thread( struct thread *thread );
|
||||
extern void suspend_all_threads( void );
|
||||
|
@ -80,6 +78,16 @@ extern void thread_killed( struct thread *thread, int exit_code );
|
|||
extern void thread_timeout(void);
|
||||
extern void wake_up( struct object *obj, int max );
|
||||
|
||||
/* ptrace functions */
|
||||
|
||||
extern void wait4_thread( struct thread *thread, int wait );
|
||||
extern void stop_thread( struct thread *thread );
|
||||
extern void continue_thread( struct thread *thread );
|
||||
extern void detach_thread( struct thread *thread );
|
||||
extern int read_thread_int( struct thread *thread, const int *addr, int *data );
|
||||
extern int write_thread_int( struct thread *thread, int *addr, int data, unsigned int mask );
|
||||
|
||||
|
||||
static inline int get_error(void) { return current->error; }
|
||||
static inline void set_error( int err ) { current->error = err; }
|
||||
static inline void clear_error(void) { set_error(0); }
|
||||
|
|
Loading…
Reference in New Issue