Skip to content
Snippets Groups Projects
Commit 578c1009 authored by Alexandre Julliard's avatar Alexandre Julliard
Browse files

Moved ptrace support to ptrace.c. Tried to improve portability.

parent 3b832225
No related branches found
No related tags found
No related merge requests found
......@@ -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 );
......
/*
* 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;
}
......@@ -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); }
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment