2 * Win32 debugger functions
4 * Copyright (C) 1999 Alexandre Julliard
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
25 #define WIN32_NO_STATUS
32 #include "wine/exception.h"
33 #include "wine/server.h"
35 #include "kernelbase.h"
36 #include "wine/debug.h"
38 WINE_DEFAULT_DEBUG_CHANNEL(seh
);
39 WINE_DECLARE_DEBUG_CHANNEL(winedbg
);
41 typedef INT (WINAPI
*MessageBoxA_funcptr
)(HWND
,LPCSTR
,LPCSTR
,UINT
);
42 typedef INT (WINAPI
*MessageBoxW_funcptr
)(HWND
,LPCWSTR
,LPCWSTR
,UINT
);
44 static PTOP_LEVEL_EXCEPTION_FILTER top_filter
;
46 void *dummy
= RtlUnwind
; /* force importing RtlUnwind from ntdll */
48 /***********************************************************************
49 * CheckRemoteDebuggerPresent (kernelbase.@)
51 BOOL WINAPI DECLSPEC_HOTPATCH
CheckRemoteDebuggerPresent( HANDLE process
, BOOL
*present
)
55 if (!process
|| !present
)
57 SetLastError( ERROR_INVALID_PARAMETER
);
60 if (!set_ntstatus( NtQueryInformationProcess( process
, ProcessDebugPort
, &port
, sizeof(port
), NULL
)))
67 /**********************************************************************
68 * ContinueDebugEvent (kernelbase.@)
70 BOOL WINAPI DECLSPEC_HOTPATCH
ContinueDebugEvent( DWORD pid
, DWORD tid
, DWORD status
)
73 SERVER_START_REQ( continue_debug_event
)
78 ret
= !wine_server_call_err( req
);
85 /**********************************************************************
86 * DebugActiveProcess (kernelbase.@)
88 BOOL WINAPI DECLSPEC_HOTPATCH
DebugActiveProcess( DWORD pid
)
93 SERVER_START_REQ( debug_process
)
97 ret
= !wine_server_call_err( req
);
100 if (!ret
) return FALSE
;
102 if (!(process
= OpenProcess( PROCESS_CREATE_THREAD
, FALSE
, pid
))) return FALSE
;
103 ret
= set_ntstatus( DbgUiIssueRemoteBreakin( process
));
105 if (!ret
) DebugActiveProcessStop( pid
);
110 /**********************************************************************
111 * DebugActiveProcessStop (kernelbase.@)
113 BOOL WINAPI DECLSPEC_HOTPATCH
DebugActiveProcessStop( DWORD pid
)
117 SERVER_START_REQ( debug_process
)
121 ret
= !wine_server_call_err( req
);
128 /***********************************************************************
129 * DebugBreak (kernelbase.@)
131 #if defined(__i386__) || defined(__x86_64__)
132 __ASM_STDCALL_FUNC( DebugBreak
, 0, "jmp " __ASM_STDCALL("DbgBreakPoint", 0) )
134 void WINAPI
DebugBreak(void)
141 /**************************************************************************
142 * FatalAppExitA (kernelbase.@)
144 void WINAPI DECLSPEC_HOTPATCH
FatalAppExitA( UINT action
, LPCSTR str
)
146 HMODULE mod
= GetModuleHandleA( "user32.dll" );
147 MessageBoxA_funcptr pMessageBoxA
= NULL
;
149 if (mod
) pMessageBoxA
= (MessageBoxA_funcptr
)GetProcAddress( mod
, "MessageBoxA" );
150 if (pMessageBoxA
) pMessageBoxA( 0, str
, NULL
, MB_SYSTEMMODAL
| MB_OK
);
151 else ERR( "%s\n", debugstr_a(str
) );
152 RtlExitUserProcess( 1 );
156 /**************************************************************************
157 * FatalAppExitW (kernelbase.@)
159 void WINAPI DECLSPEC_HOTPATCH
FatalAppExitW( UINT action
, LPCWSTR str
)
161 HMODULE mod
= GetModuleHandleW( L
"user32.dll" );
162 MessageBoxW_funcptr pMessageBoxW
= NULL
;
164 if (mod
) pMessageBoxW
= (MessageBoxW_funcptr
)GetProcAddress( mod
, "MessageBoxW" );
165 if (pMessageBoxW
) pMessageBoxW( 0, str
, NULL
, MB_SYSTEMMODAL
| MB_OK
);
166 else ERR( "%s\n", debugstr_w(str
) );
167 RtlExitUserProcess( 1 );
171 /***********************************************************************
172 * IsDebuggerPresent (kernelbase.@)
174 BOOL WINAPI
IsDebuggerPresent(void)
176 return NtCurrentTeb()->Peb
->BeingDebugged
;
180 static LONG WINAPI
debug_exception_handler( EXCEPTION_POINTERS
*eptr
)
182 EXCEPTION_RECORD
*rec
= eptr
->ExceptionRecord
;
183 return (rec
->ExceptionCode
== DBG_PRINTEXCEPTION_C
) ? EXCEPTION_EXECUTE_HANDLER
: EXCEPTION_CONTINUE_SEARCH
;
186 /***********************************************************************
187 * OutputDebugStringA (kernelbase.@)
189 void WINAPI DECLSPEC_HOTPATCH
OutputDebugStringA( LPCSTR str
)
191 static HANDLE DBWinMutex
= NULL
;
192 static BOOL mutex_inited
= FALSE
;
193 BOOL caught_by_dbg
= TRUE
;
196 WARN( "%s\n", debugstr_a(str
) );
198 /* raise exception, WaitForDebugEvent() will generate a corresponding debug event */
202 args
[0] = strlen(str
) + 1;
203 args
[1] = (ULONG_PTR
)str
;
204 RaiseException( DBG_PRINTEXCEPTION_C
, 0, 2, args
);
206 __EXCEPT(debug_exception_handler
)
208 caught_by_dbg
= FALSE
;
211 if (caught_by_dbg
) return;
213 /* send string to a system-wide monitor */
216 /* first call to OutputDebugString, initialize mutex handle */
217 HANDLE mutex
= CreateMutexExW( NULL
, L
"DBWinMutex", 0, SYNCHRONIZE
);
220 if (InterlockedCompareExchangePointer( &DBWinMutex
, mutex
, 0 ) != 0)
221 /* someone beat us here... */
222 CloseHandle( mutex
);
231 mapping
= OpenFileMappingW( FILE_MAP_WRITE
, FALSE
, L
"DBWIN_BUFFER" );
235 HANDLE eventbuffer
, eventdata
;
237 buffer
= MapViewOfFile( mapping
, FILE_MAP_WRITE
, 0, 0, 0 );
238 eventbuffer
= OpenEventW( SYNCHRONIZE
, FALSE
, L
"DBWIN_BUFFER_READY" );
239 eventdata
= OpenEventW( EVENT_MODIFY_STATE
, FALSE
, L
"DBWIN_DATA_READY" );
241 if (buffer
&& eventbuffer
&& eventdata
)
243 /* monitor is present, synchronize with other OutputDebugString invocations */
244 WaitForSingleObject( DBWinMutex
, INFINITE
);
246 /* acquire control over the buffer */
247 if (WaitForSingleObject( eventbuffer
, 10000 ) == WAIT_OBJECT_0
)
249 int str_len
= strlen( str
);
254 } *mon_buffer
= (struct _mon_buffer_t
*) buffer
;
256 if (str_len
> (4096 - sizeof(DWORD
) - 1)) str_len
= 4096 - sizeof(DWORD
) - 1;
257 mon_buffer
->pid
= GetCurrentProcessId();
258 memcpy( mon_buffer
->buffer
, str
, str_len
);
259 mon_buffer
->buffer
[str_len
] = 0;
261 /* signal data ready */
262 SetEvent( eventdata
);
264 ReleaseMutex( DBWinMutex
);
267 if (buffer
) UnmapViewOfFile( buffer
);
268 if (eventbuffer
) CloseHandle( eventbuffer
);
269 if (eventdata
) CloseHandle( eventdata
);
270 CloseHandle( mapping
);
276 /***********************************************************************
277 * OutputDebugStringW (kernelbase.@)
279 void WINAPI DECLSPEC_HOTPATCH
OutputDebugStringW( LPCWSTR str
)
284 RtlInitUnicodeString( &strW
, str
);
285 if (!RtlUnicodeStringToAnsiString( &strA
, &strW
, TRUE
))
287 OutputDebugStringA( strA
.Buffer
);
288 RtlFreeAnsiString( &strA
);
293 /*******************************************************************
294 * RaiseException (kernelbase.@)
296 void WINAPI DECLSPEC_HOTPATCH
RaiseException( DWORD code
, DWORD flags
, DWORD count
, const ULONG_PTR
*args
)
298 EXCEPTION_RECORD record
;
300 record
.ExceptionCode
= code
;
301 record
.ExceptionFlags
= flags
& EH_NONCONTINUABLE
;
302 record
.ExceptionRecord
= NULL
;
303 record
.ExceptionAddress
= RaiseException
;
306 if (count
> EXCEPTION_MAXIMUM_PARAMETERS
) count
= EXCEPTION_MAXIMUM_PARAMETERS
;
307 record
.NumberParameters
= count
;
308 memcpy( record
.ExceptionInformation
, args
, count
* sizeof(*args
) );
310 else record
.NumberParameters
= 0;
312 RtlRaiseException( &record
);
316 /***********************************************************************
317 * SetUnhandledExceptionFilter (kernelbase.@)
319 LPTOP_LEVEL_EXCEPTION_FILTER WINAPI DECLSPEC_HOTPATCH
SetUnhandledExceptionFilter(
320 LPTOP_LEVEL_EXCEPTION_FILTER filter
)
322 return InterlockedExchangePointer( (void **)&top_filter
, filter
);
326 /******************************************************************************
327 * WaitForDebugEvent (kernelbase.@)
329 BOOL WINAPI DECLSPEC_HOTPATCH
WaitForDebugEvent( DEBUG_EVENT
*event
, DWORD timeout
)
339 SERVER_START_REQ( wait_debug_event
)
341 req
->get_handle
= (timeout
!= 0);
342 wine_server_set_reply( req
, &data
, sizeof(data
) );
343 if (!(ret
= !wine_server_call_err( req
))) goto done
;
345 if (!wine_server_reply_size( reply
)) /* timeout */
347 wait
= wine_server_ptr_handle( reply
->wait
);
351 event
->dwDebugEventCode
= data
.code
;
352 event
->dwProcessId
= (DWORD
)reply
->pid
;
353 event
->dwThreadId
= (DWORD
)reply
->tid
;
356 case EXCEPTION_DEBUG_EVENT
:
357 if (data
.exception
.exc_code
== DBG_PRINTEXCEPTION_C
&& data
.exception
.nb_params
>= 2)
359 event
->dwDebugEventCode
= OUTPUT_DEBUG_STRING_EVENT
;
360 event
->u
.DebugString
.lpDebugStringData
= wine_server_get_ptr( data
.exception
.params
[1] );
361 event
->u
.DebugString
.fUnicode
= FALSE
;
362 event
->u
.DebugString
.nDebugStringLength
= data
.exception
.params
[0];
365 else if (data
.exception
.exc_code
== DBG_RIPEXCEPTION
&& data
.exception
.nb_params
>= 2)
367 event
->dwDebugEventCode
= RIP_EVENT
;
368 event
->u
.RipInfo
.dwError
= data
.exception
.params
[0];
369 event
->u
.RipInfo
.dwType
= data
.exception
.params
[1];
372 event
->u
.Exception
.dwFirstChance
= data
.exception
.first
;
373 event
->u
.Exception
.ExceptionRecord
.ExceptionCode
= data
.exception
.exc_code
;
374 event
->u
.Exception
.ExceptionRecord
.ExceptionFlags
= data
.exception
.flags
;
375 event
->u
.Exception
.ExceptionRecord
.ExceptionRecord
= wine_server_get_ptr( data
.exception
.record
);
376 event
->u
.Exception
.ExceptionRecord
.ExceptionAddress
= wine_server_get_ptr( data
.exception
.address
);
377 event
->u
.Exception
.ExceptionRecord
.NumberParameters
= data
.exception
.nb_params
;
378 for (i
= 0; i
< data
.exception
.nb_params
; i
++)
379 event
->u
.Exception
.ExceptionRecord
.ExceptionInformation
[i
] = data
.exception
.params
[i
];
381 case CREATE_THREAD_DEBUG_EVENT
:
382 event
->u
.CreateThread
.hThread
= wine_server_ptr_handle( data
.create_thread
.handle
);
383 event
->u
.CreateThread
.lpThreadLocalBase
= wine_server_get_ptr( data
.create_thread
.teb
);
384 event
->u
.CreateThread
.lpStartAddress
= wine_server_get_ptr( data
.create_thread
.start
);
386 case CREATE_PROCESS_DEBUG_EVENT
:
387 event
->u
.CreateProcessInfo
.hFile
= wine_server_ptr_handle( data
.create_process
.file
);
388 event
->u
.CreateProcessInfo
.hProcess
= wine_server_ptr_handle( data
.create_process
.process
);
389 event
->u
.CreateProcessInfo
.hThread
= wine_server_ptr_handle( data
.create_process
.thread
);
390 event
->u
.CreateProcessInfo
.lpBaseOfImage
= wine_server_get_ptr( data
.create_process
.base
);
391 event
->u
.CreateProcessInfo
.dwDebugInfoFileOffset
= data
.create_process
.dbg_offset
;
392 event
->u
.CreateProcessInfo
.nDebugInfoSize
= data
.create_process
.dbg_size
;
393 event
->u
.CreateProcessInfo
.lpThreadLocalBase
= wine_server_get_ptr( data
.create_process
.teb
);
394 event
->u
.CreateProcessInfo
.lpStartAddress
= wine_server_get_ptr( data
.create_process
.start
);
395 event
->u
.CreateProcessInfo
.lpImageName
= wine_server_get_ptr( data
.create_process
.name
);
396 event
->u
.CreateProcessInfo
.fUnicode
= data
.create_process
.unicode
;
398 case EXIT_THREAD_DEBUG_EVENT
:
399 event
->u
.ExitThread
.dwExitCode
= data
.exit
.exit_code
;
401 case EXIT_PROCESS_DEBUG_EVENT
:
402 event
->u
.ExitProcess
.dwExitCode
= data
.exit
.exit_code
;
404 case LOAD_DLL_DEBUG_EVENT
:
405 event
->u
.LoadDll
.hFile
= wine_server_ptr_handle( data
.load_dll
.handle
);
406 event
->u
.LoadDll
.lpBaseOfDll
= wine_server_get_ptr( data
.load_dll
.base
);
407 event
->u
.LoadDll
.dwDebugInfoFileOffset
= data
.load_dll
.dbg_offset
;
408 event
->u
.LoadDll
.nDebugInfoSize
= data
.load_dll
.dbg_size
;
409 event
->u
.LoadDll
.lpImageName
= wine_server_get_ptr( data
.load_dll
.name
);
410 event
->u
.LoadDll
.fUnicode
= data
.load_dll
.unicode
;
412 case UNLOAD_DLL_DEBUG_EVENT
:
413 event
->u
.UnloadDll
.lpBaseOfDll
= wine_server_get_ptr( data
.unload_dll
.base
);
420 if (ret
) return TRUE
;
422 res
= WaitForSingleObject( wait
, timeout
);
424 if (res
!= STATUS_WAIT_0
) break;
426 SetLastError( ERROR_SEM_TIMEOUT
);
431 /*******************************************************************
432 * format_exception_msg
434 static void format_exception_msg( const EXCEPTION_POINTERS
*ptr
, char *buffer
, int size
)
436 const EXCEPTION_RECORD
*rec
= ptr
->ExceptionRecord
;
439 switch(rec
->ExceptionCode
)
441 case EXCEPTION_INT_DIVIDE_BY_ZERO
:
442 len
= snprintf( buffer
, size
, "Unhandled division by zero" );
444 case EXCEPTION_INT_OVERFLOW
:
445 len
= snprintf( buffer
, size
, "Unhandled overflow" );
447 case EXCEPTION_ARRAY_BOUNDS_EXCEEDED
:
448 len
= snprintf( buffer
, size
, "Unhandled array bounds" );
450 case EXCEPTION_ILLEGAL_INSTRUCTION
:
451 len
= snprintf( buffer
, size
, "Unhandled illegal instruction" );
453 case EXCEPTION_STACK_OVERFLOW
:
454 len
= snprintf( buffer
, size
, "Unhandled stack overflow" );
456 case EXCEPTION_PRIV_INSTRUCTION
:
457 len
= snprintf( buffer
, size
, "Unhandled privileged instruction" );
459 case EXCEPTION_ACCESS_VIOLATION
:
460 if (rec
->NumberParameters
== 2)
461 len
= snprintf( buffer
, size
, "Unhandled page fault on %s access to %p",
462 rec
->ExceptionInformation
[0] == EXCEPTION_WRITE_FAULT
? "write" :
463 rec
->ExceptionInformation
[0] == EXCEPTION_EXECUTE_FAULT
? "execute" : "read",
464 (void *)rec
->ExceptionInformation
[1]);
466 len
= snprintf( buffer
, size
, "Unhandled page fault");
468 case EXCEPTION_DATATYPE_MISALIGNMENT
:
469 len
= snprintf( buffer
, size
, "Unhandled alignment" );
472 len
= snprintf( buffer
, size
, "Unhandled ^C");
474 case STATUS_POSSIBLE_DEADLOCK
:
475 len
= snprintf( buffer
, size
, "Critical section %p wait failed",
476 (void *)rec
->ExceptionInformation
[0]);
478 case EXCEPTION_WINE_STUB
:
479 if ((ULONG_PTR
)rec
->ExceptionInformation
[1] >> 16)
480 len
= snprintf( buffer
, size
, "Unimplemented function %s.%s called",
481 (char *)rec
->ExceptionInformation
[0], (char *)rec
->ExceptionInformation
[1] );
483 len
= snprintf( buffer
, size
, "Unimplemented function %s.%ld called",
484 (char *)rec
->ExceptionInformation
[0], rec
->ExceptionInformation
[1] );
486 case EXCEPTION_WINE_ASSERTION
:
487 len
= snprintf( buffer
, size
, "Assertion failed" );
490 len
= snprintf( buffer
, size
, "Unhandled exception 0x%08x in thread %x",
491 rec
->ExceptionCode
, GetCurrentThreadId());
494 if (len
< 0 || len
>= size
) return;
495 snprintf( buffer
+ len
, size
- len
, " at address %p", ptr
->ExceptionRecord
->ExceptionAddress
);
499 /******************************************************************
502 * Does the effective debugger startup according to 'format'
504 static BOOL
start_debugger( EXCEPTION_POINTERS
*epointers
, HANDLE event
)
506 OBJECT_ATTRIBUTES attr
;
507 UNICODE_STRING nameW
;
508 WCHAR
*cmdline
, *env
, *p
, *format
= NULL
;
510 DWORD autostart
= TRUE
;
511 PROCESS_INFORMATION info
;
512 STARTUPINFOW startup
;
516 format_exception_msg( epointers
, buffer
, sizeof(buffer
) );
517 MESSAGE( "wine: %s (thread %04x), starting debugger...\n", buffer
, GetCurrentThreadId() );
519 attr
.Length
= sizeof(attr
);
520 attr
.RootDirectory
= 0;
521 attr
.ObjectName
= &nameW
;
523 attr
.SecurityDescriptor
= NULL
;
524 attr
.SecurityQualityOfService
= NULL
;
525 RtlInitUnicodeString( &nameW
, L
"\\Registry\\Machine\\Software\\Microsoft\\Windows NT\\CurrentVersion\\AeDebug" );
527 if (!NtOpenKey( &dbg_key
, KEY_READ
, &attr
))
529 KEY_VALUE_PARTIAL_INFORMATION
*info
;
530 DWORD format_size
= 0;
532 RtlInitUnicodeString( &nameW
, L
"Debugger" );
533 if (NtQueryValueKey( dbg_key
, &nameW
, KeyValuePartialInformation
,
534 NULL
, 0, &format_size
) == STATUS_BUFFER_TOO_SMALL
)
536 char *data
= HeapAlloc( GetProcessHeap(), 0, format_size
);
537 NtQueryValueKey( dbg_key
, &nameW
, KeyValuePartialInformation
,
538 data
, format_size
, &format_size
);
539 info
= (KEY_VALUE_PARTIAL_INFORMATION
*)data
;
540 format
= HeapAlloc( GetProcessHeap(), 0, info
->DataLength
+ sizeof(WCHAR
) );
541 memcpy( format
, info
->Data
, info
->DataLength
);
542 format
[info
->DataLength
/ sizeof(WCHAR
)] = 0;
544 if (info
->Type
== REG_EXPAND_SZ
)
548 format_size
= ExpandEnvironmentStringsW( format
, NULL
, 0 );
549 tmp
= HeapAlloc( GetProcessHeap(), 0, format_size
* sizeof(WCHAR
));
550 ExpandEnvironmentStringsW( format
, tmp
, format_size
);
551 HeapFree( GetProcessHeap(), 0, format
);
554 HeapFree( GetProcessHeap(), 0, data
);
557 RtlInitUnicodeString( &nameW
, L
"Auto" );
558 if (!NtQueryValueKey( dbg_key
, &nameW
, KeyValuePartialInformation
,
559 buffer
, sizeof(buffer
)-sizeof(WCHAR
), &format_size
))
561 info
= (KEY_VALUE_PARTIAL_INFORMATION
*)buffer
;
562 if (info
->Type
== REG_DWORD
) memcpy( &autostart
, info
->Data
, sizeof(DWORD
) );
563 else if (info
->Type
== REG_SZ
)
565 WCHAR
*str
= (WCHAR
*)info
->Data
;
566 str
[info
->DataLength
/sizeof(WCHAR
)] = 0;
567 autostart
= wcstol( str
, NULL
, 10 );
576 size_t format_size
= lstrlenW( format
) + 2*20;
577 cmdline
= HeapAlloc( GetProcessHeap(), 0, format_size
* sizeof(WCHAR
) );
578 swprintf( cmdline
, format_size
, format
, (long)GetCurrentProcessId(), (long)HandleToLong(event
) );
579 HeapFree( GetProcessHeap(), 0, format
);
583 cmdline
= HeapAlloc( GetProcessHeap(), 0, 80 * sizeof(WCHAR
) );
584 swprintf( cmdline
, 80, L
"winedbg --auto %ld %ld", (long)GetCurrentProcessId(), (long)HandleToLong(event
) );
589 HMODULE mod
= GetModuleHandleA( "user32.dll" );
590 MessageBoxA_funcptr pMessageBoxA
= NULL
;
592 if (mod
) pMessageBoxA
= (void *)GetProcAddress( mod
, "MessageBoxA" );
595 static const char msg
[] = ".\nDo you wish to debug it?";
597 format_exception_msg( epointers
, buffer
, sizeof(buffer
) - sizeof(msg
) );
598 strcat( buffer
, msg
);
599 if (pMessageBoxA( 0, buffer
, "Exception raised", MB_YESNO
| MB_ICONHAND
) == IDNO
)
601 TRACE( "Killing process\n" );
607 /* make WINEDEBUG empty in the environment */
608 env
= GetEnvironmentStringsW();
609 if (!TRACE_ON(winedbg
))
611 for (p
= env
; *p
; p
+= lstrlenW(p
) + 1)
613 if (!wcsncmp( p
, L
"WINEDEBUG=", 10 ))
615 WCHAR
*next
= p
+ lstrlenW(p
);
616 WCHAR
*end
= next
+ 1;
617 while (*end
) end
+= lstrlenW(end
) + 1;
618 memmove( p
+ 10, next
, end
+ 1 - next
);
624 TRACE( "Starting debugger %s\n", debugstr_w(cmdline
) );
625 memset( &startup
, 0, sizeof(startup
) );
626 startup
.cb
= sizeof(startup
);
627 startup
.dwFlags
= STARTF_USESHOWWINDOW
;
628 startup
.wShowWindow
= SW_SHOWNORMAL
;
629 ret
= CreateProcessW( NULL
, cmdline
, NULL
, NULL
, TRUE
, 0, env
, NULL
, &startup
, &info
);
630 FreeEnvironmentStringsW( env
);
634 /* wait for debugger to come up... */
636 CloseHandle( info
.hThread
);
638 handles
[1] = info
.hProcess
;
639 WaitForMultipleObjects( 2, handles
, FALSE
, INFINITE
);
640 CloseHandle( info
.hProcess
);
642 else ERR( "Couldn't start debugger %s (%d)\n"
643 "Read the Wine Developers Guide on how to set up winedbg or another debugger\n",
644 debugstr_w(cmdline
), GetLastError() );
646 HeapFree(GetProcessHeap(), 0, cmdline
);
650 /******************************************************************
651 * start_debugger_atomic
653 * starts the debugger in an atomic way:
654 * - either the debugger is not started and it is started
655 * - or the debugger has already been started by another thread
656 * - or the debugger couldn't be started
658 * returns TRUE for the two first conditions, FALSE for the last
660 static BOOL
start_debugger_atomic( EXCEPTION_POINTERS
*epointers
)
666 OBJECT_ATTRIBUTES attr
;
669 attr
.Length
= sizeof(attr
);
670 attr
.RootDirectory
= 0;
671 attr
.Attributes
= OBJ_INHERIT
;
672 attr
.ObjectName
= NULL
;
673 attr
.SecurityDescriptor
= NULL
;
674 attr
.SecurityQualityOfService
= NULL
;
676 /* ask for manual reset, so that once the debugger is started,
677 * every thread will know it */
678 NtCreateEvent( &event
, EVENT_ALL_ACCESS
, &attr
, NotificationEvent
, FALSE
);
679 if (InterlockedCompareExchangePointer( &once
, event
, 0 ) == 0)
681 /* ok, our event has been set... we're the winning thread */
682 BOOL ret
= start_debugger( epointers
, once
);
686 /* so that the other threads won't be stuck */
687 NtSetEvent( once
, NULL
);
692 /* someone beat us here... */
693 CloseHandle( event
);
696 /* and wait for the winner to have actually created the debugger */
697 WaitForSingleObject( once
, INFINITE
);
698 /* in fact, here, we only know that someone has tried to start the debugger,
699 * we'll know by reposting the exception if it has actually attached
700 * to the current process */
705 /*******************************************************************
706 * check_resource_write
708 * Check if the exception is a write attempt to the resource data.
709 * If yes, we unprotect the resources to let broken apps continue
710 * (Windows does this too).
712 static BOOL
check_resource_write( void *addr
)
717 MEMORY_BASIC_INFORMATION info
;
719 if (!VirtualQuery( addr
, &info
, sizeof(info
) )) return FALSE
;
720 if (info
.State
== MEM_FREE
|| !(info
.Type
& MEM_IMAGE
)) return FALSE
;
721 if (!(rsrc
= RtlImageDirectoryEntryToData( info
.AllocationBase
, TRUE
,
722 IMAGE_DIRECTORY_ENTRY_RESOURCE
, &size
)))
724 if (addr
< rsrc
|| (char *)addr
>= (char *)rsrc
+ size
) return FALSE
;
725 TRACE( "Broken app is writing to the resource data, enabling work-around\n" );
726 VirtualProtect( rsrc
, size
, PAGE_READWRITE
, &old_prot
);
731 /*******************************************************************
732 * UnhandledExceptionFilter (kernelbase.@)
734 LONG WINAPI
UnhandledExceptionFilter( EXCEPTION_POINTERS
*epointers
)
736 const EXCEPTION_RECORD
*rec
= epointers
->ExceptionRecord
;
738 if (rec
->ExceptionCode
== EXCEPTION_ACCESS_VIOLATION
&& rec
->NumberParameters
>= 2)
740 switch (rec
->ExceptionInformation
[0])
742 case EXCEPTION_WRITE_FAULT
:
743 if (check_resource_write( (void *)rec
->ExceptionInformation
[1] ))
744 return EXCEPTION_CONTINUE_EXECUTION
;
749 if (!NtCurrentTeb()->Peb
->BeingDebugged
)
751 if (rec
->ExceptionCode
== CONTROL_C_EXIT
)
753 /* do not launch the debugger on ^C, simply terminate the process */
754 TerminateProcess( GetCurrentProcess(), 1 );
759 LONG ret
= top_filter( epointers
);
760 if (ret
!= EXCEPTION_CONTINUE_SEARCH
) return ret
;
763 /* FIXME: Should check the current error mode */
765 if (!start_debugger_atomic( epointers
) || !NtCurrentTeb()->Peb
->BeingDebugged
)
766 return EXCEPTION_EXECUTE_HANDLER
;
768 return EXCEPTION_CONTINUE_SEARCH
;