ptrace single-stepping change breaks Wine

Eric Pouech pouech-eric at
Mon Nov 22 14:52:54 CST 2004

Jesse Allen a écrit :
> On Fri, Nov 19, 2004 at 01:53:38PM -0800, Linus Torvalds wrote:
>>On Fri, 19 Nov 2004, Daniel Jacobowitz wrote:
>>>I'm getting the feeling that the question of whether to step into
>>>signal handlers is orthogonal to single-stepping; maybe it should be a
>>>separate ptrace operation.
>>I really don't see why. If a controlling process is asking for 
>>single-stepping, then it damn well should get it. It it doesn't want to 
>>single-step through a signal handler, then it could decide to just put a 
>>breakpoint on the return point (possibly by modifying the signal handler 
>>save area).
>>It's not like single-stepping into the signal handler in any way removes 
>>any information (while _not_ single-stepping into it clearly does).
>>With the patch I just posted (assuming it works for people), Wine should 
>>at least have the choice. The behaviour now should be:
>> - if the app sets TF on its own, it will cause a SIGTRAP which it can 
>>   catch.
>> - if the debugger sets TF with SINGLESTEP, it will single-step into a 
>>   signal handler.
>> - it the app sets TF _and_ you ptrace it, you the ptracer will see the 
>>   debug event and catch it. However, doing a "continue" at that point
>>   will remove the TF flag (and always has), the app will normally then
>>   never see the trap. You can do a "signal SIGTRAP" to actually force
>>   the trap handler to tun, but that one won't actually single-step (it's 
>>   a "continue" in all other senses).
>>It sounds like the third case is what wine wants.
>>		Linus
> So an app running through wine could set TF on it's own?  It would be a 
> good idea to find out what it is doing in the first place.  There has to be
> a reason why War3 is so picky after the original change was introduced and
> a reason why the latest changes don't seem to fix it. 
> I've built a kernel 2.6.10-rc2 with the new ptrace patch.  The program still
> says "please insert disc".  I haven't been able to get winedbg to tell me 
> anything useful -- the program never crashes anyways.  So I went ahead and I 
> captured a debug log.
> the command:
> WINEDEBUG=+all wine war3/Warcraft\ III.exe -opengl >& war3_and_2.6.10-rc2.log
> I scanned for the part right before it calls up to display the error.  I found
> that after loading advapi32.dll, the thread 000c creates a mutex and wakes up
> 000a.  Then 000c gets killed and right after that starts calling up the 
> resources for the "insert disc" message box.  I put the log up on my ftp, and 
> the interesting part in a seperate file:
> Any clue on what may be happening here?  Or maybe another idea on where else to search?
> Jesse
For the linux folks, here a small comparison of what happens in the working 
(old) case and in the non-working (new) case:

In both case

- Wine gets a first SIGTRAP (in it's sig_trap handler)
	+ Wine converts it into a Windows exception (w-exception in short).
	  This includes creating a context for the generic CPU registers
	+ This w-exception is sent to the w-exception handler the program
	  installed (this one can modifiy the all registers)
		o this handler touches one of the i386 debug registers
	+ as we need to update the debug registers values (and we don't do in
	  the signal handler return), this task is delegated to the Wine server
	  (our central process, which is in charge of synchronisation...)
		> the Wine server ptrace-attach:es to the process which handled
		  the SIGTRAP.
		> Wine server wait4:s on the SIGSTOP (after ptrace:attach)
		> modify (with ptrace) the debug registers
		> and resumes excution (ptrace: cont)
	+ wine terminates the sig trap handler and resumes the execution with
	  the modified basic registers (from the saved context), and the
	  modified debug registers (from the Wine server round trip)
- a second sig trap is generated
	> since the wine server is still ptrace:attached, it gets the signal.	

What differs then in both execution:
- in the working case, the sig trap handler is called on the client side, 
whereas it's never called in the non-working case. We do have a couple of 
protection (to avoid some misbehaving apps), but none of them get triggered. So 
it seems like the trap handler is not called (ugh).

A couple of notes:
- as the program tested is copy protected, and as it seems that the copy 
protection is what causes the harm, it can be interesting to know that the 
programe seems to set the TF flag (some copy protection schemes directly do an 
"int 1", but given the exception code we get, this isn't the case).
- in Windows trap handling, the TF is explictly reset before calling the windows 
exception handler (which is what Wine does, before calling the window exception 
handler). Of course the handler can set it back if it wants to continue single 


More information about the wine-devel mailing list