Hi I've been trying to get an older game(Heart Of Darkness) to work with wine. Appearently the game uses some kind of copy-protection(securom i suppose) which let wine abort with the following error: "Trace/breakpoint trap". After some debugging sessions I managed to reproduce the problem. See the attached program(debug.c, simply compile with mingw).
The problem is the following(as far as I understand it): - app sets up an exception handler - enables single-step debugging -> exception handler get invoked - in the exception handler:- re-enables single-step debugging - modifies some debug register in the CONTEXT struct -> because of that set_thread_context ist called and wineserver PTRACE_ATTACH to the process, modifies these registers, then resumes the process - as wineserver doesn't detach from the process it will get the next SIGTRAP signal and appearently it seems to ignore that signal - [as nobody cares about the SIGTRAP signal the process get killed] I'm not completly sure about the last step and the fact what wineserver really does with the SIGTRAP signal. However, I put together a solution which simply detaches from the process instead of resuming ptrace in set_thread_context(and als in get_thread_context, {read,write}_process_memory), see attached patch. With that patch my testcase finishes successfully and also the game starts.
However, as im not sure if this is a desired solution or if this breaks anything else, maybe somebody more familiar with the material can comment. There are also some more places with the same situation(suspend_for_ptrace with following resume_after_ptrace) which might expose the same problem.
Thx in advance. Peter
#include <stdio.h> #include <stdlib.h> #include <windows.h> #include <excpt.h>
DWORD icount = 0;
EXCEPTION_DISPOSITION __cdecl my_handler( struct _EXCEPTION_RECORD *ExceptionRecord, void * EstablisherFrame, struct _CONTEXT *ContextRecord, void * DispatcherContext) { printf("execption handler: code: %lx addr: %p\n", ExceptionRecord->ExceptionCode, ExceptionRecord->ExceptionAddress); if(ExceptionRecord->ExceptionCode == 0x80000004 && icount < 4) { /* single-step exception */ printf("single step debug: #%i\n",icount); icount++; ContextRecord->EFlags |= 0x100; /* just change it so wineserver has to update them */ ContextRecord->Dr0 = (DWORD)ExceptionRecord->ExceptionAddress + 0x100 + icount; ContextRecord->Dr7 = 0x7; } return ExceptionContinueExecution; }
int main(int argc, char *argv[]) { printf("install seh handler\n"); __asm__ ( "push %0 \n\t" "push %%fs:0 \n\t" "movl %%esp,%%fs:0\n\t" : : "q" (my_handler));
printf("now enable single step debugging\n"); __asm__ ( "push %eax \n\t" "pushfw \n\t" "pop %eax \n\t" "orb $1,%ah \n\t" "push %eax \n\t" "popfw \n\t" "pop %eax \n\t" "nop \n\t"); printf("finished successfully\n"); return 0; }
Index: server/context_i386.c =================================================================== RCS file: /home/wine/wine/server/context_i386.c,v retrieving revision 1.31 diff -p -u -r1.31 context_i386.c --- server/context_i386.c 21 Jun 2005 09:46:15 -0000 1.31 +++ server/context_i386.c 23 Oct 2005 16:31:53 -0000 @@ -599,7 +599,7 @@ DECL_HANDLER(get_thread_context) if (flags && suspend_for_ptrace( thread )) { get_thread_context( thread, flags, data ); - resume_after_ptrace( thread ); + detach_after_ptrace( thread ); } } release_object( thread ); @@ -627,7 +627,7 @@ DECL_HANDLER(set_thread_context) if (flags && suspend_for_ptrace( thread )) { set_thread_context( thread, flags, get_req_data() ); - resume_after_ptrace( thread ); + detach_after_ptrace( thread ); } release_object( thread ); } Index: server/process.c =================================================================== RCS file: /home/wine/wine/server/process.c,v retrieving revision 1.142 diff -p -u -r1.142 process.c --- server/process.c 12 Oct 2005 21:10:52 -0000 1.142 +++ server/process.c 23 Oct 2005 16:31:55 -0000 @@ -729,7 +729,7 @@ static int read_process_memory( struct p if (read_thread_int( thread, addr++, dest++ ) == -1) break; len--; } - resume_after_ptrace( thread ); + detach_after_ptrace( thread ); } return !len; } @@ -792,7 +792,7 @@ static int write_process_memory( struct ret = 1;
done: - resume_after_ptrace( thread ); + detach_after_ptrace( thread ); } return ret; } Index: server/ptrace.c =================================================================== RCS file: /home/wine/wine/server/ptrace.c,v retrieving revision 1.30 diff -p -u -r1.30 ptrace.c --- server/ptrace.c 22 Aug 2005 10:13:28 -0000 1.30 +++ server/ptrace.c 23 Oct 2005 16:31:56 -0000 @@ -267,6 +267,14 @@ int suspend_for_ptrace( struct thread *t return 0; }
+void detach_after_ptrace( struct thread *thread) +{ + if (thread->unix_pid == -1) return; + assert( thread->attached ); + ptrace( PTRACE_DETACH, get_ptrace_pid(thread), NULL, NULL ); + thread->attached = 0; +} + /* resume a thread after we have used ptrace on it */ void resume_after_ptrace( struct thread *thread ) {
--- Peter Beutner p.beutner@gmx.net wrote:
Hi I've been trying to get an older game(Heart Of Darkness) to work with wine. Appearently the game uses some kind of copy-protection(securom i suppose) which let wine abort with the following error: "Trace/breakpoint trap". After some debugging sessions I managed to reproduce the problem. See the attached program(debug.c, simply compile with mingw).
Heart of Darkness uses Securom R2 http://www.gameburnworld.com/PCprotectedgames_2.htm#Haegemonia
Oliver.
___________________________________________________________ To help you stay safe and secure online, we've developed the all new Yahoo! Security Centre. http://uk.security.yahoo.com
Peter Beutner p.beutner@gmx.net writes:
- as wineserver doesn't detach from the process it will get the next SIGTRAP signal and appearently it seems to ignore that signal
- [as nobody cares about the SIGTRAP signal the process get killed]
At this point the server does a PTRACE_CONT which is supposed to let the process handle the signal. You should try to figure out why this doesn't happen.
Alexandre Julliard schrieb:
Peter Beutner p.beutner@gmx.net writes:
- as wineserver doesn't detach from the process it will get the next SIGTRAP signal and
appearently it seems to ignore that signal
- [as nobody cares about the SIGTRAP signal the process get killed]
At this point the server does a PTRACE_CONT which is supposed to let the process handle the signal. You should try to figure out why this doesn't happen.
hmm it appearently is a kernel/ptrace issue as Marcus Meissner supposed. I just tested it on x86 and it works fine, while on x86_64 it doesnt work. I will dig a bit further if time permits.
Just out of curiosity, why does the server continue ptracing after "the work is done", instead of just detaching?
Peter Beutner p.beutner@gmx.net writes:
hmm it appearently is a kernel/ptrace issue as Marcus Meissner supposed. I just tested it on x86 and it works fine, while on x86_64 it doesnt work. I will dig a bit further if time permits.
Just out of curiosity, why does the server continue ptracing after "the work is done", instead of just detaching?
It's mostly a performance thing. Though in the current design it may not make all that much difference anymore, it would be interesting to measure it.
On Sun, Oct 23, 2005 at 08:33:40PM +0200, Peter Beutner wrote:
Hi I've been trying to get an older game(Heart Of Darkness) to work with wine. Appearently the game uses some kind of copy-protection(securom i suppose) which let wine abort with the following error: "Trace/breakpoint trap". After some debugging sessions I managed to reproduce the problem. See the attached program(debug.c, simply compile with mingw).
The problem is the following(as far as I understand it):
- app sets up an exception handler
- enables single-step debugging -> exception handler get invoked
- in the exception handler:- re-enables single-step debugging - modifies some debug register in the CONTEXT struct -> because of that set_thread_context ist called and wineserver PTRACE_ATTACH to the process, modifies these registers, then resumes the process
- as wineserver doesn't detach from the process it will get the next SIGTRAP signal and appearently it seems to ignore that signal
- [as nobody cares about the SIGTRAP signal the process get killed]
I'm not completly sure about the last step and the fact what wineserver really does with the SIGTRAP signal.
Do you use a AMD64 machine? At least mine has some troubles with ptrace on kernel 2.6.13 :/
Ciao, Marcus
Marcus Meissner schrieb:
On Sun, Oct 23, 2005 at 08:33:40PM +0200, Peter Beutner wrote:
Hi I've been trying to get an older game(Heart Of Darkness) to work with wine. Appearently the game uses some kind of copy-protection(securom i suppose) which let wine abort with the following error: "Trace/breakpoint trap". After some debugging sessions I managed to reproduce the problem. See the attached program(debug.c, simply compile with mingw).
The problem is the following(as far as I understand it):
- app sets up an exception handler
- enables single-step debugging -> exception handler get invoked
- in the exception handler:- re-enables single-step debugging - modifies some debug register in the CONTEXT struct
-> because of that set_thread_context ist called and wineserver PTRACE_ATTACH to the process, modifies these registers, then resumes the process
- as wineserver doesn't detach from the process it will get the next SIGTRAP signal and
appearently it seems to ignore that signal
- [as nobody cares about the SIGTRAP signal the process get killed]
I'm not completly sure about the last step and the fact what wineserver really does with the SIGTRAP signal.
Do you use a AMD64 machine? At least mine has some troubles with ptrace on kernel 2.6.13 :/
yes, it is an amd64 with 64bit kernel and wine running in a 32bit chroot.
Ciao, Marcus