wine ptrace trouble

Peter Beutner p.beutner at gmx.net
Sun Oct 23 13:33:40 CDT 2005


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
-------------- next part --------------
#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;
}
-------------- next part --------------
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 )
 {


More information about the wine-devel mailing list