i hope i am not asking to much by this, but is there any chance you could send me a copy of the diff's with some fixme comments so I know exactly which things need to be cleaned up? I know that you said the server suspension code needs to be checked, and the 0x7ffe0000 page needs to be properly implemented, but im not sure what all would need to be changed to get this done.
Ok, here's a first version of safedisc.safe.diff with comments. I don't think it's usefull to comment safedisc.dmca.diff for now, but if you need some information about it let me know.
--- memory/emulate.c Thu Jan 1 01:00:00 1970 +++ memory/emulate.c Thu May 9 17:35:27 2002 @@ -0,0 +1,53 @@ +/* + * Emulation of privileged memory + * + * Copyright 2002 Laurent Pinchart + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "windef.h" +#include "wingdi.h" +#include "wine/winuser16.h" +#include "module.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(virtual); + +#ifdef __i386__ + +/*********************************************************************** + * MEMORY_SharedUserData + * + * Handles exceptions for the SharedUserData access. + */ +BOOL MEMORY_SharedUserData( LPVOID arg, LPCVOID addr ) +{ + DWORD dwProtection; + BOOL ret; + + TRACE( "MEMORY_SharedUserData\n" ); + + ret = VirtualProtect( (LPVOID)0x7ffe0000, 0x10000, PAGE_READWRITE, &dwProtection ); + if ( ret ) + { + *(LPDWORD)0x7ffe0000 = 0x12345678; + ret = VirtualProtect( (LPVOID)0x7ffe0000, 0x10000, PAGE_READONLY, &dwProtection ); + } + + return ret; +} + +#endif /* __i386__ */
This is the emulation of "SharedUserData". SharedUserData is a page of memory located right before the kernel space (0x80000000), so at address 0x7ffe0000. It contains miscelaneous information, and is read-only in user space and read-write in kernel space. You can find more information about it in ntddk.h (not included in wine).
We need 3 things here: 1) Write support for the other fields, not only TickCount (kernel timer). This is optional but quite easy for some fields. 2) Find a way to either remove the read rights after the read access, so that the next read access would trigger an exception again, and the information could be updated (this is IMHO *very* difficult), or periodically update the information contained in that page (mostly TickCount). The later should be easier, but implies a slow down. If would be nice to trigger that periodical update only after the first read of TickCount, so that programs which don't read it wouldn't suffer from that slow down. We could also remove the read rights after some time (instead of right after the read operation). That might be the best way, as it wouldn't imply a slow down (not periodical update). 3) Replace 0x12345678 with a correct value. Don't forget that secdrv.c needs to know that value (or at least a close enough value), so we need a portable and accurate way to set and retrieve it from inside wine.
Index: dlls/ntdll/Makefile.in =================================================================== RCS file: /home/wine/wine/dlls/ntdll/Makefile.in,v retrieving revision 1.27 diff -u -r1.27 Makefile.in --- dlls/ntdll/Makefile.in 14 May 2002 20:55:01 -0000 1.27 +++ dlls/ntdll/Makefile.in 23 May 2002 18:54:33 -0000 @@ -32,6 +32,7 @@ $(TOPOBJDIR)/loader/ne/segment.c \ $(TOPOBJDIR)/memory/atom.c \ $(TOPOBJDIR)/memory/codepage.c \ + $(TOPOBJDIR)/memory/emulate.c \ $(TOPOBJDIR)/memory/environ.c \ $(TOPOBJDIR)/memory/global.c \ $(TOPOBJDIR)/memory/heap.c \
When emulate.c will be applied this will be too :-)
Index: dlls/ntdll/signal_i386.c =================================================================== RCS file: /home/wine/wine/dlls/ntdll/signal_i386.c,v retrieving revision 1.35 diff -u -r1.35 signal_i386.c --- dlls/ntdll/signal_i386.c 16 May 2002 20:32:16 -0000 1.35 +++ dlls/ntdll/signal_i386.c 23 May 2002 18:54:40 -0000 @@ -679,6 +679,7 @@ { EXCEPTION_RECORD rec; DWORD page_fault_code = EXCEPTION_ACCESS_VIOLATION; + DWORD priv_instr_code = EXCEPTION_PRIV_INSTRUCTION;
#ifdef CR2_sig /* we want the page-fault case to be fast */ @@ -708,8 +709,8 @@ case T_SEGNPFLT: /* Segment not present exception */ case T_PROTFLT: /* General protection fault */ case T_UNKNOWN: /* Unknown fault code */ - if (INSTR_EmulateInstruction( context )) return; - rec.ExceptionCode = EXCEPTION_PRIV_INSTRUCTION; + if (INSTR_EmulateInstruction( context, &priv_instr_code )) return; + rec.ExceptionCode = priv_instr_code; break; case T_PAGEFLT: /* Page fault */ #ifdef CR2_sig Index: include/miscemu.h =================================================================== RCS file: /home/wine/wine/include/miscemu.h,v retrieving revision 1.46 diff -u -r1.46 miscemu.h --- include/miscemu.h 10 Mar 2002 00:02:34 -0000 1.46 +++ include/miscemu.h 23 May 2002 18:54:49 -0000 @@ -167,7 +167,7 @@ extern UINT DOSMEM_MapLinearToDos(LPVOID); /* linear Wine to DOS */
/* memory/instr.c */ -extern BOOL INSTR_EmulateInstruction( CONTEXT86 *context ); +extern BOOL INSTR_EmulateInstruction( CONTEXT86 *context, DWORD *exception_code );
/* msdos/interrupts.c */ typedef void (WINAPI *INTPROC)(CONTEXT86*); Index: memory/instr.c =================================================================== RCS file: /home/wine/wine/memory/instr.c,v retrieving revision 1.15 diff -u -r1.15 instr.c --- memory/instr.c 11 May 2002 23:01:12 -0000 1.15 +++ memory/instr.c 23 May 2002 18:54:58 -0000 @@ -396,7 +396,7 @@ * * Emulate a privileged instruction. Returns TRUE if emulation successful. */ -BOOL INSTR_EmulateInstruction( CONTEXT86 *context ) +BOOL INSTR_EmulateInstruction( CONTEXT86 *context, DWORD *exception_code ) { int prefix, segprefix, prefixlen, len, repX, long_op, long_addr; SEGPTR gpHandler; @@ -686,7 +686,9 @@ case 0xcd: /* int <XX> */ if (long_op) { - ERR("int xx from 32-bit code is not supported.\n"); + ERR("int 0x%02x from 32-bit code is not supported.\n", instr[1]); + if (instr[1] == 0x01) + *exception_code = EXCEPTION_ACCESS_VIOLATION; break; /* Unable to emulate it */ } else
This probably hasn't been applied because it lacks testing. It seems that some instructions which are emulated by wine (int 0x01 is one of them) should generate an EXCEPTION_PRIV_INSTRUCTION instead of EXCEPTION_ACCESS_VIOLATION. Or maybe my code is not clean enough to commit. I'd like to hear comments from Alexandre about this specific part of the patch.
Index: include/wine/server_protocol.h =================================================================== RCS file: /home/wine/wine/include/wine/server_protocol.h,v retrieving revision 1.35 diff -u -r1.35 server_protocol.h --- include/wine/server_protocol.h 25 Apr 2002 22:58:59 -0000 1.35 +++ include/wine/server_protocol.h 23 May 2002 18:54:56 -0000 @@ -296,6 +296,7 @@ struct init_process_done_reply { struct reply_header __header; + int suspended; int debugged; };
@@ -3198,6 +3199,6 @@ struct get_window_properties_reply get_window_properties_reply; };
-#define SERVER_PROTOCOL_VERSION 79 +#define SERVER_PROTOCOL_VERSION 80
#endif /* __WINE_WINE_SERVER_PROTOCOL_H */ Index: scheduler/process.c =================================================================== RCS file: /home/wine/wine/scheduler/process.c,v retrieving revision 1.182 diff -u -r1.182 process.c --- scheduler/process.c 22 May 2002 21:32:50 -0000 1.182 +++ scheduler/process.c 23 May 2002 18:55:04 -0000 @@ -25,6 +25,7 @@ #include <ctype.h> #include <errno.h> #include <fcntl.h> +#include <signal.h> #include <stdlib.h> #include <stdio.h> #include <string.h> @@ -43,6 +44,7 @@ #include "wine/server.h" #include "options.h" #include "wine/debug.h" +#include "global.h"
WINE_DEFAULT_DEBUG_CHANNEL(process); WINE_DECLARE_DEBUG_CHANNEL(relay); @@ -126,6 +128,9 @@ /* scheduler/pthread.c */ extern void PTHREAD_init_done(void);
+/* memory/emulate.c */ +extern BOOL MEMORY_SharedUserData(LPVOID, LPCVOID); + extern BOOL MAIN_MainInit(void);
typedef WORD (WINAPI *pUserSignalProc)( UINT, DWORD, DWORD, HMODULE16 ); @@ -416,6 +421,20 @@ SetStdHandle( STD_ERROR_HANDLE, current_startupinfo.hStdError ); }
+ /* Reserves a PAGE_NOACCESS at 0x7ffe0000. That page is used as shared + * memory between kernel space and user space (see SharedUserData in + * ntddk.h). It looks like it is at 0xffdf0000 on NT5. That is still to + * be checked. + */ + if ( VirtualAlloc( (LPVOID)0x7ffe0000, 0x10000, MEM_RESERVE|MEM_COMMIT, PAGE_NOACCESS ) == (LPCVOID)0x07ffe0000 ) + { + VIRTUAL_SetFaultHandler( (LPCVOID)0x7ffe0000, MEMORY_SharedUserData, 0 ); + } + else + { + WARN( "Unable to map SharedUserData, SafeDisc protection won't be supported\n" ); + } + /* Now we can use the pthreads routines */ PTHREAD_init_done();
@@ -440,7 +459,7 @@ */ static void start_process(void) { - int debugged, console_app; + int suspended, debugged, console_app; LPTHREAD_START_ROUTINE entry; WINE_MODREF *wm; HFILE main_file = main_exe_file; @@ -481,7 +500,8 @@ req->gui = !console_app; wine_server_add_data( req, main_exe_name, strlen(main_exe_name) ); wine_server_call( req ); - debugged = reply->debugged; + suspended = reply->suspended; + debugged = reply->debugged; } SERVER_END_REQ;
@@ -513,6 +533,8 @@ PROCESS_CallUserSignalProc( USIG_PROCESS_LOADED, 0 ); /* Call UserSignalProc ( USIG_PROCESS_RUNNING ... ) only for non-GUI win32 apps */ if (console_app) PROCESS_CallUserSignalProc( USIG_PROCESS_RUNNING, 0 ); + + if (suspended) raise( SIGSTOP );
if (TRACE_ON(relay)) DPRINTF( "%08lx:Starting process %s (entryproc=%p)\n", Index: server/process.c =================================================================== RCS file: /home/wine/wine/server/process.c,v retrieving revision 1.83 diff -u -r1.83 process.c --- server/process.c 26 Apr 2002 19:05:18 -0000 1.83 +++ server/process.c 23 May 2002 18:55:07 -0000 @@ -382,6 +382,14 @@ }
+/* signal the parent that a process init is finished */ +void process_init_done( struct process *process ) +{ + set_event( process->init_event ); + release_object( process->init_event ); + process->init_event = NULL; +} + /* get a process from an id (and increment the refcount) */ struct process *get_process_from_id( void *id ) { @@ -896,13 +904,27 @@ if ((process->exe.namelen = get_req_data_size())) process->exe.filename = memdup( get_req_data(), process->exe.namelen );
- generate_startup_debug_events( current->process, req->entry ); - set_event( process->init_event ); - release_object( process->init_event ); - process->init_event = NULL; + generate_startup_debug_events( process, req->entry ); if (req->gui) process->idle_event = create_event( NULL, 0, 1, 0 ); - if (current->suspend + current->process->suspend > 0) stop_thread( current ); - reply->debugged = (current->process->debugger != 0); + + reply->suspended = 0; + reply->debugged = (current->process->debugger != 0); + if (current->suspend + current->process->suspend > 0) + { + /* temporarily clear the init event so that ptrace functions work */ + struct event *init_event = process->init_event; + process->init_event = NULL; + stop_thread( current ); + process->init_event = init_event; + + if (current->attached) + { + reply->suspended = 1; + continue_thread( current ); /* it will suspend itself */ + return; + } + } + process_init_done( process ); }
/* open a handle to a process */ Index: server/process.h =================================================================== RCS file: /home/wine/wine/server/process.h,v retrieving revision 1.27 diff -u -r1.27 process.h --- server/process.h 22 Mar 2002 00:21:23 -0000 1.27 +++ server/process.h 23 May 2002 18:55:07 -0000 @@ -90,6 +90,7 @@ extern struct thread *create_process( int fd ); extern struct process *get_process_from_id( void *id ); extern struct process *get_process_from_handle( handle_t handle, unsigned int access ); +extern void process_init_done( struct process *process ); extern int process_set_debugger( struct process *process, struct thread *thread ); extern int debugger_detach( struct process* process, struct thread* debugger );
Index: server/protocol.def =================================================================== RCS file: /home/wine/wine/server/protocol.def,v retrieving revision 1.36 diff -u -r1.36 protocol.def --- server/protocol.def 25 Apr 2002 22:58:59 -0000 1.36 +++ server/protocol.def 23 May 2002 18:55:12 -0000 @@ -276,6 +276,7 @@ int gui; /* is it a GUI process? */ VARARG(filename,string); /* file name of main exe */ @REPLY + int suspended; /* created suspended? */ int debugged; /* being debugged? */ @END
Index: server/ptrace.c =================================================================== RCS file: /home/wine/wine/server/ptrace.c,v retrieving revision 1.14 diff -u -r1.14 ptrace.c --- server/ptrace.c 10 Mar 2002 00:18:36 -0000 1.14 +++ server/ptrace.c 23 May 2002 18:55:13 -0000 @@ -79,7 +79,12 @@ switch(sig) { case SIGSTOP: /* continue at once if not suspended */ - if (thread && (thread->process->suspend + thread->suspend)) break; + if (thread && (thread->process->suspend + thread->suspend)) + { + /* check if this was the self-suspend upon process init */ + if (thread->process->init_event) process_init_done( thread->process ); + break; + } /* fall through */ default: /* ignore other signals for now */ if (thread && get_thread_single_step( thread )) Index: server/trace.c =================================================================== RCS file: /home/wine/wine/server/trace.c,v retrieving revision 1.132 diff -u -r1.132 trace.c --- server/trace.c 26 Apr 2002 19:05:18 -0000 1.132 +++ server/trace.c 23 May 2002 18:55:20 -0000 @@ -441,6 +441,7 @@
static void dump_init_process_done_reply( const struct init_process_done_reply *req ) { + fprintf( stderr, " suspended=%d,", req->suspended ); fprintf( stderr, " debugged=%d", req->debugged ); }
There are two patches in there. The first one sets a fault handler for the SharedUserData. This looks fine to me, and will probably be applied when memory/emulate.c will be applied.
The second one has been written by Alexandre to fix the process suspension at creation time. Once again I'd like to hear from him to know what he doesn't like about the code, as he's the one who wrote it :-)
Hope this helps.
I'm available if you need more information
Laurent Pinchart
Laurent Pinchart laurent.pinchart@skynet.be writes:
There are two patches in there. The first one sets a fault handler for the SharedUserData. This looks fine to me, and will probably be applied when memory/emulate.c will be applied.
It still isn't clear to me why you need a fault handler at all. You could simply put the right data into it at allocation time.
The second one has been written by Alexandre to fix the process suspension at creation time. Once again I'd like to hear from him to know what he doesn't like about the code, as he's the one who wrote it :-)
The main problem is that it isn't generic enough, it only applies to the create process request but other requests have the same problem and should be fixed too. I'll work on a more general solution.
There are two patches in there. The first one sets a fault handler for the SharedUserData. This looks fine to me, and will probably be applied when memory/emulate.c will be applied.
It still isn't clear to me why you need a fault handler at all. You could simply put the right data into it at allocation time.
If I do so I'll have to update the TIckCount field periodically, which will slow down wine for all programs even if they don't access the SharedUserData. With a fault handler I can update the fields only when required (access from the user program), and thus be faster.
The second one has been written by Alexandre to fix the process suspension at creation time. Once again I'd like to hear from him to know what he doesn't like about the code, as he's the one who wrote it :-)
The main problem is that it isn't generic enough, it only applies to the create process request but other requests have the same problem and should be fixed too. I'll work on a more general solution.
Ok, thanks. I'll wait for your solution then. It was about time to clean up that bit cause the patch you wrote for me doesn't apply anymore to the latest CVS :-)
Laurent Pinchart
Laurent Pinchart laurent.pinchart@skynet.be writes:
If I do so I'll have to update the TIckCount field periodically, which will slow down wine for all programs even if they don't access the SharedUserData. With a fault handler I can update the fields only when required (access from the user program), and thus be faster.
My understanding was that SafeDisc works even if you don't increment it right? There's no point in doing extra work for this kind of hack.
"Alexandre" == Alexandre Julliard julliard@winehq.com writes:
Alexandre> Laurent Pinchart laurent.pinchart@skynet.be writes: >> If I do so I'll have to update the TIckCount field periodically, >> which will slow down wine for all programs even if they don't access >> the SharedUserData. With a fault handler I can update the fields only >> when required (access from the user program), and thus be faster.
Alexandre> My understanding was that SafeDisc works even if you don't Alexandre> increment it right? There's no point in doing extra work for Alexandre> this kind of hack.
But din't we need sime infrastructure for that page in place anyhow? I am quite sure that e.g. WinHelp() in WinNT uses that page.
Bye
Uwe Bonnes bon@elektron.ikp.physik.tu-darmstadt.de writes:
But din't we need sime infrastructure for that page in place anyhow? I am quite sure that e.g. WinHelp() in WinNT uses that page.
Most of user32.dll in NT uses it, that's how it communicates with the portions of user32 that are in the kernel. But we are not going to run the native NT user32 anyway, and our user32 does things differently.
"Alexandre" == Alexandre Julliard julliard@winehq.com writes:
Alexandre> Uwe Bonnes bon@elektron.ikp.physik.tu-darmstadt.de writes: >> But din't we need sime infrastructure for that page in place anyhow? >> I am quite sure that e.g. WinHelp() in WinNT uses that page.
Alexandre> Most of user32.dll in NT uses it, that's how it communicates Alexandre> with the portions of user32 that are in the kernel. But we Alexandre> are not going to run the native NT user32 anyway, and our Alexandre> user32 does things differently.
WinNT Explorer crashes with an access to 07ffe0000 too.
Bye
Uwe Bonnes bon@elektron.ikp.physik.tu-darmstadt.de writes:
WinNT Explorer crashes with an access to 07ffe0000 too.
Well, then we should debug that and implement whatever it needs. We are never going to have a 100% compatible user shared data anyway, so there's no point in implementing things unless we know they are needed.
WinNT Explorer crashes with an access to 07ffe0000 too.
Well, then we should debug that and implement whatever it needs. We are never going to have a 100% compatible user shared data anyway, so there's no point in implementing things unless we know they are needed.
You're right. I still like the idea of a fault handler, but I agree with you :-)
Uwe, could you tell me which memory location is accessed exactly (and if possible a short assembly context) ? If WinNT Explorer reads the 32-bit word at 0x7ffe0000 (TickCout) my SafeDisc patch should fix the problem.
Laurent Pinchart
"Laurent" == Laurent Pinchart laurent.pinchart@skynet.be writes:
>> > WinNT Explorer crashes with an access to 07ffe0000 too. >> >> Well, then we should debug that and implement whatever it needs. We >> are never going to have a 100% compatible user shared data anyway, so >> there's no point in implementing things unless we know they are >> needed.
Laurent> You're right. I still like the idea of a fault handler, but I Laurent> agree with you :-)
Laurent> Uwe, could you tell me which memory location is accessed Laurent> exactly (and if possible a short assembly context) ? If WinNT Laurent> Explorer reads the 32-bit word at 0x7ffe0000 (TickCout) my Laurent> SafeDisc patch should fix the problem.
It was not 0x7ffe000, if I remember right. Will try on Friday. Wine over ADSL with a lot of packet losses isn't fun.
Bye