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 )
{