|  | /* | 
|  | * Server-side /proc support for Solaris | 
|  | * | 
|  | * Copyright (C) 2007 Alexandre Julliard | 
|  | * | 
|  | * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA | 
|  | */ | 
|  |  | 
|  | #include "config.h" | 
|  |  | 
|  | #include <assert.h> | 
|  | #include <errno.h> | 
|  | #include <fcntl.h> | 
|  | #include <stdio.h> | 
|  | #include <signal.h> | 
|  | #include <stdarg.h> | 
|  | #include <sys/types.h> | 
|  | #include <unistd.h> | 
|  |  | 
|  | #include "ntstatus.h" | 
|  | #define WIN32_NO_STATUS | 
|  | #include "winternl.h" | 
|  |  | 
|  | #include "file.h" | 
|  | #include "process.h" | 
|  | #include "thread.h" | 
|  |  | 
|  | #ifdef USE_PROCFS | 
|  |  | 
|  | #include <procfs.h> | 
|  |  | 
|  | static int open_proc_as( struct process *process, int flags ) | 
|  | { | 
|  | char buffer[32]; | 
|  | int fd; | 
|  |  | 
|  | if (process->unix_pid == -1) | 
|  | { | 
|  | set_error( STATUS_ACCESS_DENIED ); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | sprintf( buffer, "/proc/%u/as", process->unix_pid ); | 
|  | if ((fd = open( buffer, flags )) == -1) | 
|  | { | 
|  | if (errno == ENOENT)  /* probably got killed */ | 
|  | { | 
|  | process->unix_pid = -1; | 
|  | set_error( STATUS_ACCESS_DENIED ); | 
|  | } | 
|  | else file_set_error(); | 
|  | } | 
|  | return fd; | 
|  | } | 
|  |  | 
|  | static int open_proc_lwpctl( struct thread *thread ) | 
|  | { | 
|  | char buffer[48]; | 
|  | int fd; | 
|  |  | 
|  | if (thread->unix_pid == -1) return -1; | 
|  |  | 
|  | sprintf( buffer, "/proc/%u/lwp/%u/lwpctl", thread->unix_pid, thread->unix_tid ); | 
|  | if ((fd = open( buffer, O_WRONLY )) == -1) | 
|  | { | 
|  | if (errno == ENOENT)  /* probably got killed */ | 
|  | thread->unix_pid = thread->unix_tid = -1; | 
|  | else | 
|  | file_set_error(); | 
|  | } | 
|  | return fd; | 
|  | } | 
|  |  | 
|  |  | 
|  | /* handle a SIGCHLD signal */ | 
|  | void sigchld_callback(void) | 
|  | { | 
|  | assert( 0 );  /* should only be called when using ptrace */ | 
|  | } | 
|  |  | 
|  | /* initialize the process tracing mechanism */ | 
|  | void init_tracing_mechanism(void) | 
|  | { | 
|  | /* no initialization needed */ | 
|  | } | 
|  |  | 
|  | /* initialize the per-process tracing mechanism */ | 
|  | void init_process_tracing( struct process *process ) | 
|  | { | 
|  | /* setup is done on-demand */ | 
|  | } | 
|  |  | 
|  | /* terminate the per-process tracing mechanism */ | 
|  | void finish_process_tracing( struct process *process ) | 
|  | { | 
|  | } | 
|  |  | 
|  | /* send a Unix signal to a specific thread */ | 
|  | int send_thread_signal( struct thread *thread, int sig ) | 
|  | { | 
|  | int fd = open_proc_lwpctl( thread ); | 
|  | long kill[2]; | 
|  | ssize_t ret; | 
|  |  | 
|  | if (fd == -1) return 0; | 
|  |  | 
|  | kill[0] = PCKILL; | 
|  | kill[1] = sig; | 
|  | ret = write( fd, kill, sizeof(kill) ); | 
|  | close( fd ); | 
|  | return (ret == sizeof(kill)); | 
|  | } | 
|  |  | 
|  | /* read data from a process memory space */ | 
|  | int read_process_memory( struct process *process, const void *ptr, size_t size, char *dest ) | 
|  | { | 
|  | ssize_t ret; | 
|  | int fd = open_proc_as( process, O_RDONLY ); | 
|  |  | 
|  | if (fd == -1) return 0; | 
|  |  | 
|  | ret = pread( fd, dest, size, (off_t)ptr ); | 
|  | close( fd ); | 
|  | if (ret == size) return 1; | 
|  |  | 
|  | if (ret == -1) file_set_error(); | 
|  | else set_error( STATUS_ACCESS_VIOLATION ); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* write data to a process memory space */ | 
|  | int write_process_memory( struct process *process, void *ptr, size_t size, const char *src ) | 
|  | { | 
|  | ssize_t ret; | 
|  | int fd = open_proc_as( process, O_WRONLY ); | 
|  |  | 
|  | if (fd == -1) return 0; | 
|  |  | 
|  | ret = pwrite( fd, src, size, (off_t)ptr ); | 
|  | close( fd ); | 
|  | if (ret == size) return 1; | 
|  |  | 
|  | if (ret == -1) file_set_error(); | 
|  | else set_error( STATUS_ACCESS_VIOLATION ); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* retrieve an LDT selector entry */ | 
|  | void get_selector_entry( struct thread *thread, int entry, unsigned int *base, | 
|  | unsigned int *limit, unsigned char *flags ) | 
|  | { | 
|  | ssize_t ret; | 
|  | off_t pos = (off_t)thread->process->ldt_copy; | 
|  | int fd = open_proc_as( thread->process, O_RDONLY ); | 
|  |  | 
|  | if (fd == -1) return; | 
|  |  | 
|  | ret = pread( fd, base, sizeof(*base), pos + entry*sizeof(int) ); | 
|  | if (ret != sizeof(*base)) goto error; | 
|  | ret = pread( fd, limit, sizeof(*limit), pos + (8192 + entry)*sizeof(int) ); | 
|  | if (ret != sizeof(*limit)) goto error; | 
|  | ret = pread( fd, flags, sizeof(*flags), pos + 2*8192*sizeof(int) + entry ); | 
|  | if (ret != sizeof(*flags)) goto error; | 
|  | close( fd ); | 
|  | return; | 
|  |  | 
|  | error: | 
|  | if (ret == -1) file_set_error(); | 
|  | else set_error( STATUS_ACCESS_VIOLATION ); | 
|  | close( fd ); | 
|  | } | 
|  |  | 
|  | /* retrieve the thread registers */ | 
|  | void get_thread_context( struct thread *thread, CONTEXT *context, unsigned int flags ) | 
|  | { | 
|  | /* all other regs are handled on the client side */ | 
|  | assert( (flags | CONTEXT_i386) == CONTEXT_DEBUG_REGISTERS ); | 
|  |  | 
|  | /* FIXME: get debug registers */ | 
|  | } | 
|  |  | 
|  | /* set the thread registers */ | 
|  | void set_thread_context( struct thread *thread, const CONTEXT *context, unsigned int flags ) | 
|  | { | 
|  | /* all other regs are handled on the client side */ | 
|  | assert( (flags | CONTEXT_i386) == CONTEXT_DEBUG_REGISTERS ); | 
|  |  | 
|  | /* FIXME: set debug registers */ | 
|  | } | 
|  |  | 
|  | #endif /* USE_PROCFS */ |