Hello,
I ran across the need to do memory allocations across processes and came upon Alexander Yaworsky's patch at http://www.winehq.org/pipermail/wine-devel/2004-September/029953.html and took a different approach. Instead of waiting for the target process to check a fd, I changed the implementation to use ptrace to interrupt the target process and execute code in kernel32 that does an mmap/munmap.
This is a simple implementation that does not do a full-on call to Nt(Allocate|Free)VirtualMemory inside the target process, mainly because the locks taken in the allocation management code would cause deadlocks.
I've done informal testing to make sure the code doesn't crash Wine by running applications alongside this memory-churner: http://www.seas.ucla.edu/~kho/wine/alloc_test/alloc_test.exe http://www.seas.ucla.edu/~kho/wine/alloc_test/alloc_test.cpp
One thought that has recently crossed my mind: Building off his framework and putting much of the code in the server may not have been a great idea, since the only real need for the server is to get the unix pid of the thread.
Is this a step in the right direction? Comments appreciated!
Thomas Kho
--- Index: dlls/kernel/kernel32.spec =================================================================== RCS file: /home/wine/wine/dlls/kernel/kernel32.spec,v retrieving revision 1.174 diff -u -r1.174 kernel32.spec --- dlls/kernel/kernel32.spec 27 May 2006 11:36:56 -0000 1.174 +++ dlls/kernel/kernel32.spec 2 Jun 2006 05:43:15 -0000 @@ -1239,3 +1239,8 @@
# Init code @ cdecl __wine_kernel_init() + +# Remote memory allocation +# kernel32 is mapped to the same place in every process +@ cdecl wine_remote_NtAllocateVirtualMemory(ptr long long) +@ cdecl wine_remote_NtFreeVirtualMemory(ptr long) Index: dlls/kernel/virtual.c =================================================================== RCS file: /home/wine/wine/dlls/kernel/virtual.c,v retrieving revision 1.18 diff -u -r1.18 virtual.c --- dlls/kernel/virtual.c 23 May 2006 12:48:03 -0000 1.18 +++ dlls/kernel/virtual.c 2 Jun 2006 05:43:15 -0000 @@ -42,6 +42,7 @@ #include "wine/exception.h" #include "excpt.h" #include "wine/debug.h" +#include "wine/library.h"
#include "kernel_private.h"
@@ -778,3 +779,77 @@ __ENDTRY return FALSE; } + +/*********************************************************************** + * wine_remote_NtAllocateVirtualMemory (KERNEL32.@) + * + * Reentrant function that allocates memory + * + * PARAMS + * base [I] Desired base address + * size [I] Number of bytes to allocate + * prot [I] Memory protection flags + * + * RETURNS + * Success: %eax contains base address of allocated region of pages. + * Failure: %eax is NULL. + */ +void wine_remote_NtAllocateVirtualMemory( LPVOID base, DWORD size, DWORD prot ) +{ + char *mem; + size += sizeof(int *); + mem = wine_anon_mmap(base, size, prot, 0); + /*printf("mmap base=0x%08x size=%d prot=%d, allocated at 0x%x\n", + base, size, prot, mem);*/ + *(int *) mem = size; + + mem += sizeof(int); + /* return base address of allocated memory in eax */ + asm("movl %0, %%eax" + : + :"r"(mem) + :"%eax"); + asm("int3"); /* execution doesn't continue past here */ + return; +} + +/*********************************************************************** + * wine_remote_NtFreeVirtualMemory (KERNEL32.@) + * + * Reentrant function that frees mmap'd memory + * + * PARAMS + * base [I] Desired base address + * size [I] Number of bytes to free + * + * RETURNS + * Success: %eax contains size of unmapped region + * Failure: %eax is 0. + */ +void wine_remote_NtFreeVirtualMemory( LPVOID base, DWORD size ) +{ + int result; + if (size == 0) + { + base = (char *) base - sizeof(int *); + size = *(int *) base; + result = munmap(base, size); + } + else /* FIXME unsafe for invalid base */ + result = 1; + + /*printf("munmap base=0x%08x size=%d, result: %d (%s)\n", + base, size, result, result == 0 ? "success" : "failure");*/ + + if (result == 0) /* munmap success */ + result = size; + else + result = 0; + /* return munmap return value in eax */ + asm("movl %0, %%eax" + : + :"r"(result) + :"%eax"); + asm("int3"); /* execution doesn't continue past here */ + return; +} Index: dlls/ntdll/virtual.c =================================================================== RCS file: /home/wine/wine/dlls/ntdll/virtual.c,v retrieving revision 1.90 diff -u -r1.90 virtual.c --- dlls/ntdll/virtual.c 23 May 2006 12:48:22 -0000 1.90 +++ dlls/ntdll/virtual.c 2 Jun 2006 05:43:16 -0000 @@ -144,6 +144,36 @@
/*********************************************************************** + * remote_op + * + */ +NTSTATUS remote_op( HANDLE process, int opcode, + void* params, int params_size, + void* result, int* result_size ) +{ + NTSTATUS status, remote_status = 0; + + SERVER_START_REQ( remote_operation ) + { + req->handle = process; + req->code = opcode; + if (params) wine_server_add_data( req, params, params_size ); + wine_server_set_reply( req, result, *result_size ); + if (!(status = wine_server_call( req ))) + { + remote_status = reply->status; + if (result) + *result_size = wine_server_reply_size( reply ); + TRACE("result_size=%d\n", *result_size); + } + } + SERVER_END_REQ; + + return status ? status : remote_status; +} + + +/*********************************************************************** * VIRTUAL_GetProtStr */ static const char *VIRTUAL_GetProtStr( BYTE prot ) @@ -1250,8 +1280,8 @@ * NtAllocateVirtualMemory (NTDLL.@) * ZwAllocateVirtualMemory (NTDLL.@) */ -NTSTATUS WINAPI NtAllocateVirtualMemory( HANDLE process, PVOID *ret, ULONG zero_bits, - SIZE_T *size_ptr, ULONG type, ULONG protect ) +NTSTATUS WINAPI NtAllocateVirtualMemory( HANDLE process, PVOID *ret, + ULONG zero_bits, SIZE_T *size_ptr, ULONG type, ULONG protect ) { void *base; BYTE vprot; @@ -1265,8 +1295,48 @@
if (!is_current_process( process )) { - ERR("Unsupported on other process\n"); - return STATUS_ACCESS_DENIED; + struct remote_op_params_vm_alloc alloc_params; + struct remote_op_result_vm_alloc alloc_result; + int result_size = sizeof(alloc_result); + typedef void (*allocator_type)(LPVOID base, DWORD size, DWORD prot); + static allocator_type allocator = NULL; + + if (!allocator) + { + HMODULE hkernel32; + UNICODE_STRING module; + ANSI_STRING function; + WCHAR kernel32W[] = {'k','e','r','n','e','l','3','2', + '.','d','l','l',0}; + RtlInitUnicodeString(&module, kernel32W); + RtlInitAnsiString(&function, "wine_remote_NtAllocateVirtualMemory"); + if (LdrGetDllHandle(0, 0, &module, &hkernel32) == STATUS_SUCCESS) + LdrGetProcedureAddress(hkernel32, &function, 0, + (void**)&allocator); + else + return STATUS_DLL_NOT_FOUND; + printf("using allocator in kernel32 at 0x%08x\n", + (unsigned) allocator); + } + + alloc_params.addr = *ret; + alloc_params.zerobits = zero_bits; + alloc_params.size = size; + alloc_params.type = type; + alloc_params.protect = VIRTUAL_GetProt( protect ); + alloc_params.allocator = allocator; + status = remote_op( process, REMOTE_OP_VM_ALLOC, + &alloc_params, sizeof(alloc_params), + &alloc_result, &result_size ); + if (!status) + { + *ret = alloc_result.addr; + *size_ptr = alloc_result.size; + } + printf("NtAllocateVirtualMemory status=0x%08x ptr=0x%08x size=%d\n", + (unsigned) status, (unsigned) alloc_result.addr, + alloc_result.size); + return status; }
/* Round parameters to a page boundary */ @@ -1372,8 +1442,42 @@
if (!is_current_process( process )) { - ERR("Unsupported on other process\n"); - return STATUS_ACCESS_DENIED; + struct remote_op_params_vm_free free_params; + struct remote_op_result_vm_free free_result; + int result_size = sizeof(free_result); + typedef void (*deallocator_type)(LPVOID base, DWORD size); + static deallocator_type deallocator = NULL; + + if (!deallocator) { + HMODULE hkernel32; + UNICODE_STRING module; + ANSI_STRING function; + WCHAR kernel32W[] = {'k','e','r','n','e','l','3','2', + '.','d','l','l',0}; + RtlInitUnicodeString(&module, kernel32W); + RtlInitAnsiString(&function, "wine_remote_NtFreeVirtualMemory"); + + if (LdrGetDllHandle(0, 0, &module, &hkernel32) == STATUS_SUCCESS) + LdrGetProcedureAddress(hkernel32, &function, 0, + (void**)&deallocator); + else + return STATUS_DLL_NOT_FOUND; + printf("using deallocator in kernel32 at 0x%08x\n", + (unsigned) deallocator); + } + + free_params.addr = *addr_ptr; + free_params.size = size; + free_params.type = type; + free_params.deallocator = deallocator; + status = remote_op( process, REMOTE_OP_VM_FREE, + &free_params, sizeof(free_params), + &free_result, &result_size ); + if (!status) + *size_ptr = free_result.size; + printf("NtFreeVirtualMemory status=0x%08x size=%d\n", + (unsigned) status, (unsigned) free_result.size); + return status; }
/* Fix the parameters */ Index: include/winternl.h =================================================================== RCS file: /home/wine/wine/include/winternl.h,v retrieving revision 1.181 diff -u -r1.181 winternl.h --- include/winternl.h 27 May 2006 11:37:02 -0000 1.181 +++ include/winternl.h 2 Jun 2006 05:43:17 -0000 @@ -2242,7 +2242,8 @@ extern NTSTATUS wine_nt_to_unix_file_name( const UNICODE_STRING *nameW, ANSI_STRING *unix_name_ret, UINT disposition, BOOLEAN check_case ); extern NTSTATUS wine_unix_to_nt_file_name( const ANSI_STRING *name, UNICODE_STRING *nt ); - +extern void wine_remote_NtAllocateVirtualMemory( LPVOID base, DWORD size, DWORD prot ); +extern void wine_remote_NtFreeVirtualMemory( LPVOID base, DWORD size );
/*********************************************************************** * Inline functions Index: include/wine/server_protocol.h =================================================================== RCS file: /home/wine/wine/include/wine/server_protocol.h,v retrieving revision 1.198 diff -u -r1.198 server_protocol.h --- include/wine/server_protocol.h 27 May 2006 11:37:01 -0000 1.198 +++ include/wine/server_protocol.h 2 Jun 2006 05:43:18 -0000 @@ -3727,6 +3727,132 @@ };
+ +struct remote_operation_request +{ + struct request_header __header; + obj_handle_t handle; + int code; + /* VARARG(data,bytes); */ +}; +struct remote_operation_reply +{ + struct reply_header __header; + unsigned int status; + /* VARARG(data,bytes); */ +}; +enum remote_op_code +{ + REMOTE_OP_NEW_THREAD, + REMOTE_OP_VM_ALLOC, + REMOTE_OP_VM_FREE, + REMOTE_OP_VM_PROTECT, + REMOTE_OP_VM_QUERY, + REMOTE_OP_VM_MAP, + REMOTE_OP_VM_UNMAP, + REMOTE_OP_VM_FLUSH +}; + +struct remote_op_params_new_thread +{ + int suspend; + void *stack_addr; + unsigned int stack_reserve; + unsigned int stack_commit; + void *start; + void *param; +}; +struct remote_op_result_new_thread +{ + obj_handle_t handle; + thread_id_t tid; +}; + +struct remote_op_params_vm_alloc +{ + void *addr; + unsigned long zerobits; + unsigned long size; + unsigned long type; + unsigned long protect; + void *allocator; +}; +struct remote_op_result_vm_alloc +{ + void *addr; + unsigned long size; +}; + +struct remote_op_params_vm_free +{ + void *addr; + unsigned long size; + unsigned long type; + void *deallocator; +}; +struct remote_op_result_vm_free +{ + unsigned long size; +}; + +struct remote_op_params_vm_protect +{ + void *addr; + unsigned long size; + unsigned long new_prot; +}; +struct remote_op_result_vm_protect +{ + void *base; + unsigned long size; + unsigned long old_prot; +}; + +struct remote_op_params_vm_query +{ + void *addr; + int info_class; +}; + +struct remote_op_params_vm_map +{ + void *addr; + unsigned long zero_bits; + unsigned long commit_size; + unsigned long offset_low; + long offset_high; + unsigned long size; + int inherit; + unsigned long alloc_type; + unsigned long protect; +}; +struct remote_op_result_vm_map +{ + void *base; + unsigned long size; +}; + +struct remote_op_params_vm_flush +{ + void *addr; + unsigned long size; + unsigned long unknown; +}; +struct remote_op_result_vm_flush +{ + void *base; + unsigned long size; +}; + + +struct remote_op_request +{ + enum remote_op_code op_code; + thread_id_t originator; + int params_size; +}; + + enum request { REQ_new_process, @@ -3942,6 +4068,7 @@ REQ_create_symlink, REQ_open_symlink, REQ_query_symlink, + REQ_remote_operation, REQ_NB_REQUESTS };
@@ -4162,6 +4289,7 @@ struct create_symlink_request create_symlink_request; struct open_symlink_request open_symlink_request; struct query_symlink_request query_symlink_request; + struct remote_operation_request remote_operation_request; }; union generic_reply { @@ -4380,8 +4508,9 @@ struct create_symlink_reply create_symlink_reply; struct open_symlink_reply open_symlink_reply; struct query_symlink_reply query_symlink_reply; + struct remote_operation_reply remote_operation_reply; };
-#define SERVER_PROTOCOL_VERSION 234 +#define SERVER_PROTOCOL_VERSION 238
#endif /* __WINE_WINE_SERVER_PROTOCOL_H */ Index: server/process.c =================================================================== RCS file: /home/wine/wine/server/process.c,v retrieving revision 1.167 diff -u -r1.167 process.c --- server/process.c 23 May 2006 12:49:34 -0000 1.167 +++ server/process.c 2 Jun 2006 05:43:18 -0000 @@ -22,12 +22,15 @@ #include "wine/port.h"
#include <assert.h> +#include <errno.h> #include <limits.h> +#include <linux/user.h> #include <signal.h> #include <string.h> #include <stdarg.h> #include <stdio.h> #include <stdlib.h> +#include <sys/ptrace.h> #include <sys/time.h> #ifdef HAVE_SYS_SOCKET_H # include <sys/socket.h> @@ -36,6 +39,7 @@ #ifdef HAVE_POLL_H #include <poll.h> #endif +#include <sys/wait.h>
#include "ntstatus.h" #define WIN32_NO_STATUS @@ -1081,3 +1085,209 @@ release_object( process ); } } + +/* Accept parameters for remote operation and start it */ +DECL_HANDLER(remote_operation) +{ + int access; + struct process *process; + struct thread *thread; + + /* define required access rights */ + switch( req->code ) + { + case REMOTE_OP_NEW_THREAD: + access = PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION + | PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE; + break; + case REMOTE_OP_VM_ALLOC: access = PROCESS_VM_OPERATION; break; + case REMOTE_OP_VM_FREE: access = PROCESS_VM_OPERATION; break; + case REMOTE_OP_VM_PROTECT: access = PROCESS_VM_OPERATION; break; + case REMOTE_OP_VM_QUERY: access = PROCESS_QUERY_INFORMATION; break; + case REMOTE_OP_VM_MAP: access = PROCESS_VM_OPERATION; break; + case REMOTE_OP_VM_UNMAP: access = PROCESS_VM_OPERATION; break; + case REMOTE_OP_VM_FLUSH: access = PROCESS_VM_OPERATION; break; /* FIXME: is access right? */ + default: + set_error( STATUS_INVALID_PARAMETER ); + return; + } + + switch( req->code ) + { + case REMOTE_OP_NEW_THREAD: + break; + case REMOTE_OP_VM_ALLOC: + { +#if defined(linux) && defined(__i386__) + int pid; + struct user_regs_struct oldregs, regs; + struct remote_op_params_vm_alloc *params; + struct remote_op_result_vm_alloc result; + + params = (struct remote_op_params_vm_alloc *) get_req_data(); + + /* get process object */ + if (!(process = get_process_from_handle( req->handle, access ))) + return; + + thread = get_process_first_thread(process); + pid = thread->unix_pid; + release_object(process); + + /*printf("attaching to process with pid %d\n", pid);*/ + if (ptrace(PTRACE_ATTACH, pid, NULL, NULL) == -1) + goto errorbeforeattach; + + wait(NULL); + if (ptrace(PTRACE_GETREGS, pid, NULL, ®s) == -1) + goto error; + + memcpy(&oldregs, ®s, sizeof(struct user_regs_struct)); + + regs.eip = (int) params->allocator; + + /* cdecl */ + if (ptrace(PTRACE_POKEDATA, pid, regs.esp-4, params->protect) == -1) + goto error; + if (ptrace(PTRACE_POKEDATA, pid, regs.esp-8, params->size) == -1) + goto error; + if (ptrace(PTRACE_POKEDATA, pid, regs.esp-12, params->addr) == -1) + goto error; + regs.esp -= 16; + + /* prevent the kernel from restarting the system call. + * see i386_linux_write_pc() in gdb/i386-linux-tdep.c of gdb */ + regs.orig_eax = -1; + + if (ptrace(PTRACE_SETREGS, pid, NULL, ®s) == -1) + goto error; + if (ptrace(PTRACE_CONT, pid, NULL, NULL) == -1) + goto error; + + wait(NULL); + if (ptrace(PTRACE_GETREGS, pid, NULL, ®s) == -1) + goto error; + + /* eax has pointer to allocated memory */ + /*printf("mem allocated in target process at 0x%08x\n", + (unsigned) regs.eax);*/ + if (ptrace(PTRACE_SETREGS, pid, NULL, &oldregs) == -1) + goto error; + if (ptrace(PTRACE_DETACH, pid, NULL, NULL) == -1) + goto error; + + reply->status = regs.eax ? STATUS_SUCCESS + : STATUS_INVALID_PARAMETER; + result.addr = (void *) regs.eax; + result.size = params->size; + + assert(sizeof(result) <= get_reply_max_size()); + set_reply_data(&result, sizeof(result)); + + break; + +error: + printf("error!\n"); + ptrace(PTRACE_DETACH, pid, NULL, NULL); + set_error( STATUS_INVALID_PARAMETER ); + break; + +errorbeforeattach: + printf("error before attach!\n"); + set_error( STATUS_INVALID_PARAMETER ); + break; +#else + set_error( STATUS_NOT_IMPLEMENTED ); +#endif + } + case REMOTE_OP_VM_FREE: + { +#if defined(linux) && defined(__i386__) + int pid; + struct user_regs_struct oldregs, regs; + struct remote_op_params_vm_free *params; + struct remote_op_result_vm_free result; + + params = (struct remote_op_params_vm_free *) get_req_data(); + + /* get process object */ + if (!(process = get_process_from_handle( req->handle, access ))) + return; + + thread = get_process_first_thread(process); + pid = thread->unix_pid; + release_object(process); + + /*printf("attaching to process with pid %d\n", pid);*/ + if (ptrace(PTRACE_ATTACH, pid, NULL, NULL) == -1) + goto freeerrorbeforeattach; + + wait(NULL); + if (ptrace(PTRACE_GETREGS, pid, NULL, ®s) == -1) + goto freeerror; + memcpy(&oldregs, ®s, sizeof(struct user_regs_struct)); + + regs.eip = (int) params->deallocator; + + /* cdecl */ + if (ptrace(PTRACE_POKEDATA, pid, regs.esp-4, params->size) == -1) + goto freeerror; + if (ptrace(PTRACE_POKEDATA, pid, regs.esp-8, params->addr) == -1) + goto freeerror; + regs.esp -= 12; + + /* prevent the kernel from restarting the system call. + * see i386_linux_write_pc() in gdb/i386-linux-tdep.c of gdb */ + regs.orig_eax = -1; + + if (ptrace(PTRACE_SETREGS, pid, NULL, ®s) == -1) + goto freeerror; + if (ptrace(PTRACE_CONT, pid, NULL, NULL) == -1) + goto freeerror; + + wait(NULL); + if (ptrace(PTRACE_GETREGS, pid, NULL, ®s) == -1) + goto freeerror; + + /* eax has return value */ + /*printf("munmap'd in target process, result=0x%08x\n", + (unsigned) regs.eax);*/ + if (ptrace(PTRACE_SETREGS, pid, NULL, &oldregs) == -1) + goto freeerror; + if (ptrace(PTRACE_DETACH, pid, NULL, NULL) == -1) + goto freeerror; + + reply->status = regs.eax ? STATUS_SUCCESS + : STATUS_INVALID_PARAMETER; + result.size = regs.eax; + + assert(sizeof(result) <= get_reply_max_size()); + set_reply_data(&result, sizeof(result)); + + break; + +freeerror: + printf("error!\n"); + ptrace(PTRACE_DETACH, pid, NULL, NULL); + set_error( STATUS_INVALID_PARAMETER ); + break; + +freeerrorbeforeattach: + printf("error before attach!\n"); + set_error( STATUS_INVALID_PARAMETER ); + break; +#else + set_error( STATUS_NOT_IMPLEMENTED ); +#endif + } + case REMOTE_OP_VM_PROTECT: + case REMOTE_OP_VM_QUERY: + case REMOTE_OP_VM_MAP: + case REMOTE_OP_VM_UNMAP: + case REMOTE_OP_VM_FLUSH: + break; + default: + set_error( STATUS_INVALID_PARAMETER ); + return; + } +} Index: server/protocol.def =================================================================== RCS file: /home/wine/wine/server/protocol.def,v retrieving revision 1.198 diff -u -r1.198 protocol.def --- server/protocol.def 27 May 2006 11:37:01 -0000 1.198 +++ server/protocol.def 2 Jun 2006 05:43:18 -0000 @@ -2615,3 +2615,124 @@ @REPLY VARARG(target_name,unicode_str); /* target name */ @END + + +/* Accept parameters for remote operation and start it */ +@REQ(remote_operation) + obj_handle_t handle; /* process handle */ + int code; /* operation code (see below) */ + VARARG(data,bytes); /* operation parameters (see below) */ +@REPLY + unsigned int status; /* operation status */ + VARARG(data,bytes); /* operation result data (see below) */ +@END +enum remote_op_code +{ + REMOTE_OP_NEW_THREAD, + REMOTE_OP_VM_ALLOC, + REMOTE_OP_VM_FREE, + REMOTE_OP_VM_PROTECT, + REMOTE_OP_VM_QUERY, + REMOTE_OP_VM_MAP, + REMOTE_OP_VM_UNMAP, + REMOTE_OP_VM_FLUSH +}; + +struct remote_op_params_new_thread +{ + int suspend; + void *stack_addr; + unsigned int stack_reserve; + unsigned int stack_commit; + void *start; + void *param; +}; +struct remote_op_result_new_thread +{ + obj_handle_t handle; + thread_id_t tid; +}; + +struct remote_op_params_vm_alloc +{ + void *addr; + unsigned long zerobits; + unsigned long size; + unsigned long type; + unsigned long protect; + void *allocator; +}; +struct remote_op_result_vm_alloc +{ + void *addr; + unsigned long size; +}; + +struct remote_op_params_vm_free +{ + void *addr; + unsigned long size; + unsigned long type; + void *deallocator; +}; +struct remote_op_result_vm_free +{ + unsigned long size; +}; + +struct remote_op_params_vm_protect +{ + void *addr; + unsigned long size; + unsigned long new_prot; +}; +struct remote_op_result_vm_protect +{ + void *base; + unsigned long size; + unsigned long old_prot; +}; + +struct remote_op_params_vm_query +{ + void *addr; + int info_class; +}; + +struct remote_op_params_vm_map +{ + void *addr; + unsigned long zero_bits; + unsigned long commit_size; + unsigned long offset_low; + long offset_high; + unsigned long size; + int inherit; + unsigned long alloc_type; + unsigned long protect; +}; +struct remote_op_result_vm_map +{ + void *base; + unsigned long size; +}; + +struct remote_op_params_vm_flush +{ + void *addr; + unsigned long size; + unsigned long unknown; +}; +struct remote_op_result_vm_flush +{ + void *base; + unsigned long size; +}; + +/* structure for marshalling remote operation requests */ +struct remote_op_request +{ + enum remote_op_code op_code; + thread_id_t originator; + int params_size; +}; Index: server/request.h =================================================================== RCS file: /home/wine/wine/server/request.h,v retrieving revision 1.132 diff -u -r1.132 request.h --- server/request.h 23 May 2006 12:49:34 -0000 1.132 +++ server/request.h 2 Jun 2006 05:43:18 -0000 @@ -323,6 +323,7 @@ DECL_HANDLER(create_symlink); DECL_HANDLER(open_symlink); DECL_HANDLER(query_symlink); +DECL_HANDLER(remote_operation);
#ifdef WANT_REQUEST_HANDLERS
@@ -542,6 +543,7 @@ (req_handler)req_create_symlink, (req_handler)req_open_symlink, (req_handler)req_query_symlink, + (req_handler)req_remote_operation, }; #endif /* WANT_REQUEST_HANDLERS */
Index: server/trace.c =================================================================== RCS file: /home/wine/wine/server/trace.c,v retrieving revision 1.310 diff -u -r1.310 trace.c --- server/trace.c 27 May 2006 11:37:01 -0000 1.310 +++ server/trace.c 2 Jun 2006 05:43:19 -0000 @@ -3263,6 +3263,21 @@ dump_varargs_unicode_str( cur_size ); }
+static void dump_remote_operation_request( const struct remote_operation_request *req ) +{ + fprintf( stderr, " handle=%p,", req->handle ); + fprintf( stderr, " code=%d,", req->code ); + fprintf( stderr, " data=" ); + dump_varargs_bytes( cur_size ); +} + +static void dump_remote_operation_reply( const struct remote_operation_reply *req ) +{ + fprintf( stderr, " status=%08x,", req->status ); + fprintf( stderr, " data=" ); + dump_varargs_bytes( cur_size ); +} + static const dump_func req_dumpers[REQ_NB_REQUESTS] = { (dump_func)dump_new_process_request, (dump_func)dump_get_new_process_info_request, @@ -3477,6 +3492,7 @@ (dump_func)dump_create_symlink_request, (dump_func)dump_open_symlink_request, (dump_func)dump_query_symlink_request, + (dump_func)dump_remote_operation_request, };
static const dump_func reply_dumpers[REQ_NB_REQUESTS] = { @@ -3693,6 +3709,7 @@ (dump_func)dump_create_symlink_reply, (dump_func)dump_open_symlink_reply, (dump_func)dump_query_symlink_reply, + (dump_func)dump_remote_operation_reply, };
static const char * const req_names[REQ_NB_REQUESTS] = { @@ -3909,6 +3926,7 @@ "create_symlink", "open_symlink", "query_symlink", + "remote_operation", };
static const struct
On 6/1/06, tkho@ucla.edu tkho@ucla.edu wrote:
One thought that has recently crossed my mind: Building off his framework and putting much of the code in the server may not have been a great idea, since the only real need for the server is to get the unix pid of the thread.
Nah, I think it was the right thing to do. You definitely want the server to do the dirty work here.
- /* return base address of allocated memory in eax */
- asm("movl %0, %%eax"
:
:"r"(mem)
:"%eax");
- asm("int3"); /* execution doesn't continue past here */
Better surround that with #if defined(LINUX) && defined(__i386__), or whatever the right symbol is, and explain better why 'int3' is how you return to the caller of ptrace.
printf("using allocator in kernel32 at 0x%08x\n",
(unsigned) allocator);
You might want to strip out the debugging prints...
+enum remote_op_code +{
- REMOTE_OP_NEW_THREAD,
- REMOTE_OP_VM_ALLOC,
- REMOTE_OP_VM_FREE,
- REMOTE_OP_VM_PROTECT,
- REMOTE_OP_VM_QUERY,
- REMOTE_OP_VM_MAP,
- REMOTE_OP_VM_UNMAP,
- REMOTE_OP_VM_FLUSH
+};
I would have ripped out any part of his patch you're not implementing, just to keep the size down...
case REMOTE_OP_VM_ALLOC:
{
+#if defined(linux) && defined(__i386__)
int pid;
struct user_regs_struct oldregs, regs;
struct remote_op_params_vm_alloc *params;
struct remote_op_result_vm_alloc result;
- ...
You probably want to hide all the ptrace stuff inside a separate function called, maybe, posix_remote_mmap(...).
Also, which bugzilla bugs / conformance tests will this fix? If the answer is 'none', maybe you need to write them. - Dan
tkho@ucla.edu writes:
I ran across the need to do memory allocations across processes and came upon Alexander Yaworsky's patch at http://www.winehq.org/pipermail/wine-devel/2004-September/029953.html and took a different approach. Instead of waiting for the target process to check a fd, I changed the implementation to use ptrace to interrupt the target process and execute code in kernel32 that does an mmap/munmap.
The problem here is that you don't control at what point you interrupt the process, so you can't do anything in it except system calls, and that won't be enough. Another problem is that you make the server wait on the client to execute the code, and that's a big no-no, the server can't trust the client.
On 6/2/06, Alexandre Julliard julliard@winehq.org wrote:
The problem here is that you don't control at what point you interrupt the process, so you can't do anything in it except system calls, and that won't be enough.
Why isn't a call to mmap enough? (We're trying to support techniques like the one documented in http://msdn.microsoft.com/library/en-us/dnwinforms/html/autowforms.asp for retrieving control names and captions from another process, if that helps.)
Another problem is that you make the server wait on the client to execute the code, and that's a big no-no, the server can't trust the client.
Fine, he can move the wait to the client, that's no problem. - Dan
"Dan Kegel" dank@kegel.com writes:
On 6/2/06, Alexandre Julliard julliard@winehq.org wrote:
The problem here is that you don't control at what point you interrupt the process, so you can't do anything in it except system calls, and that won't be enough.
Why isn't a call to mmap enough?
You need to build the proper structures on the client side, just like a normal VirtualAlloc would, otherwise the process won't know about that memory area.
On 6/2/06, Alexandre Julliard julliard@winehq.org wrote:
Why isn't a call to mmap enough?
You need to build the proper structures on the client side, just like a normal VirtualAlloc would, otherwise the process won't know about that memory area.
OK, new plan: how about we preallocate a memory area just big enough for the common case, with the structures all set up, and let the server hand it out to the first process to ask for it? I don't think it needs to be very big. - Dan
"Dan Kegel" dank@kegel.com writes:
OK, new plan: how about we preallocate a memory area just big enough for the common case, with the structures all set up, and let the server hand it out to the first process to ask for it? I don't think it needs to be very big.
Well, that may work for your specific problem, but it's clearly not an acceptable general solution.
On 6/2/06, Alexandre Julliard julliard@winehq.org wrote:
OK, new plan: how about we preallocate a memory area just big enough for the common case, with the structures all set up, and let the server hand it out to the first process to ask for it? I don't think it needs to be very big.
Well, that may work for your specific problem, but it's clearly not an acceptable general solution.
How about this: we combine the original sychronous approach with the small preallocated cache as a fallback for when no threads are waiting?
If that's not good enough, what (short of a Linux kernel module) might do? - Dan
"Dan Kegel" dank@kegel.com writes:
How about this: we combine the original sychronous approach with the small preallocated cache as a fallback for when no threads are waiting?
You need to have a thread waiting one way or another, since you need to perform operations on the client side that require a thread context. No, it's not an easy problem to solve, that's why it has not been done yet...
On 6/2/06, Alexandre Julliard julliard@winehq.org wrote:
How about this: we combine the original sychronous approach with the small preallocated cache as a fallback for when no threads are waiting?
You need to have a thread waiting one way or another, since you need to perform operations on the client side that require a thread context. No, it's not an easy problem to solve, that's why it has not been done yet...
Are you willing to accept a patch which makes some common use cases work well, but does not address others? If so, we can continue. Otherwise we'll have to wash our hands and walk away screaming. - Dan
On 6/2/06, Dan Kegel dank@kegel.com wrote:
You need to have a thread waiting one way or another, since you need to perform operations on the client side that require a thread context. No, it's not an easy problem to solve, that's why it has not been done yet...
Are you willing to accept a patch which makes some common use cases work well, but does not address others?
Oh, heck, I guess we can keep thinking about it even if you do insist on a full-on solution.
How 'bout we also implement remote thread creation, and create a temporary service thread to service a remote memory allocation request? That would give the thread context you say is required. - Dan
Friday, June 2, 2006, 9:33:30 PM, Dan Kegel wrote:
On 6/2/06, Dan Kegel dank@kegel.com wrote:
You need to have a thread waiting one way or another, since you need to perform operations on the client side that require a thread context. No, it's not an easy problem to solve, that's why it has not been done yet...
Are you willing to accept a patch which makes some common use cases work well, but does not address others?
Oh, heck, I guess we can keep thinking about it even if you do insist on a full-on solution.
How 'bout we also implement remote thread creation, and create a temporary service thread to service a remote memory allocation request? That would give the thread context you say is required.
- Dan
That would be wonderful actually. It seems that some new stile copy-protection technics include use of injected threads. Also that would help properly implementing braking any process for debugging.
So the real use of this would be to support all sorts of debuggers - they all use all these functions on other processes.
Vitaliy
"Dan Kegel" dank@kegel.com writes:
How 'bout we also implement remote thread creation, and create a temporary service thread to service a remote memory allocation request? That would give the thread context you say is required.
Yes that's a possibility, we need support for creating remote threads in any case. But of course creating a remote thread correctly is a non-trivial problem too...
"Dan Kegel" dank@kegel.com writes:
Are you willing to accept a patch which makes some common use cases work well, but does not address others?
If it's a solution that can be expanded later on to cover the other cases, yes, but not if it's a dead-end.
On Sat, 03 Jun 2006 09:43:04 +0200, Alexandre Julliard wrote:
If it's a solution that can be expanded later on to cover the other cases, yes, but not if it's a dead-end.
When this was discussed before the idea of bringing back the service thread was raised. I still think we should do this, assuming details like suppressing attach notifications are done properly ...
thanks -mike
Alexandre Julliard wrote:
"Dan Kegel" dank@kegel.com writes:
On 6/2/06, Alexandre Julliard julliard@winehq.org wrote:
The problem here is that you don't control at what point you interrupt the process, so you can't do anything in it except system calls, and that won't be enough.
Why isn't a call to mmap enough?
You need to build the proper structures on the client side, just like a normal VirtualAlloc would, otherwise the process won't know about that memory area.
You could add an entry to a linked list of operations that would defer the building of the proper structures. This list could then be checked on entry to every Nt*VirtualMemory and if it was not empty, these structures could be updated then, so it should appear to the client as though the structures were updated instantly.