From 578c10090b415685478cdbb41bda4bbcac0c4e84 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard <julliard@winehq.org> Date: Sun, 14 Nov 1999 21:23:21 +0000 Subject: [PATCH] Moved ptrace support to ptrace.c. Tried to improve portability. --- server/Makefile.in | 1 + server/process.c | 42 +++--------- server/ptrace.c | 164 +++++++++++++++++++++++++++++++++++++++++++++ server/thread.c | 120 +++------------------------------ server/thread.h | 14 +++- 5 files changed, 192 insertions(+), 149 deletions(-) create mode 100644 server/ptrace.c diff --git a/server/Makefile.in b/server/Makefile.in index fafd772fba1..f1f0f1da513 100644 --- a/server/Makefile.in +++ b/server/Makefile.in @@ -18,6 +18,7 @@ C_SRCS = \ object.c \ pipe.c \ process.c \ + ptrace.c \ request.c \ snapshot.c \ select.c \ diff --git a/server/process.c b/server/process.c index 034e90d28cb..ee3ca64a1b6 100644 --- a/server/process.c +++ b/server/process.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 ); diff --git a/server/ptrace.c b/server/ptrace.c new file mode 100644 index 00000000000..b2931267f92 --- /dev/null +++ b/server/ptrace.c @@ -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; +} diff --git a/server/thread.c b/server/thread.c index d8885462ec8..6d96eac1f29 100644 --- a/server/thread.c +++ b/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 ) { diff --git a/server/thread.h b/server/thread.h index 2d5c7a38875..ecb9226a0d0 100644 --- a/server/thread.h +++ b/server/thread.h @@ -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); } -- GitLab