makefiles: Don't use standard libs for programs that specify -nodefaultlibs.
[wine/zf.git] / dlls / kernelbase / debug.c
blob60c9d09cf6eff2a880e16e2f367855ff02b786d2
1 /*
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
21 #include <stdio.h>
22 #include <string.h>
24 #include "ntstatus.h"
25 #define WIN32_NO_STATUS
26 #include "windef.h"
27 #include "winbase.h"
28 #include "winternl.h"
29 #include "wingdi.h"
30 #include "winuser.h"
32 #include "wine/exception.h"
33 #include "wine/server.h"
34 #include "wine/asm.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 )
53 DWORD_PTR port;
55 if (!process || !present)
57 SetLastError( ERROR_INVALID_PARAMETER );
58 return FALSE;
60 if (!set_ntstatus( NtQueryInformationProcess( process, ProcessDebugPort, &port, sizeof(port), NULL )))
61 return FALSE;
62 *present = !!port;
63 return TRUE;
67 /**********************************************************************
68 * ContinueDebugEvent (kernelbase.@)
70 BOOL WINAPI DECLSPEC_HOTPATCH ContinueDebugEvent( DWORD pid, DWORD tid, DWORD status )
72 BOOL ret;
73 SERVER_START_REQ( continue_debug_event )
75 req->pid = pid;
76 req->tid = tid;
77 req->status = status;
78 ret = !wine_server_call_err( req );
80 SERVER_END_REQ;
81 return ret;
85 /**********************************************************************
86 * DebugActiveProcess (kernelbase.@)
88 BOOL WINAPI DECLSPEC_HOTPATCH DebugActiveProcess( DWORD pid )
90 HANDLE process;
91 BOOL ret;
93 SERVER_START_REQ( debug_process )
95 req->pid = pid;
96 req->attach = 1;
97 ret = !wine_server_call_err( req );
99 SERVER_END_REQ;
100 if (!ret) return FALSE;
102 if (!(process = OpenProcess( PROCESS_CREATE_THREAD, FALSE, pid ))) return FALSE;
103 ret = set_ntstatus( DbgUiIssueRemoteBreakin( process ));
104 NtClose( process );
105 if (!ret) DebugActiveProcessStop( pid );
106 return ret;
110 /**********************************************************************
111 * DebugActiveProcessStop (kernelbase.@)
113 BOOL WINAPI DECLSPEC_HOTPATCH DebugActiveProcessStop( DWORD pid )
115 BOOL ret;
117 SERVER_START_REQ( debug_process )
119 req->pid = pid;
120 req->attach = 0;
121 ret = !wine_server_call_err( req );
123 SERVER_END_REQ;
124 return ret;
128 /***********************************************************************
129 * DebugBreak (kernelbase.@)
131 #if defined(__i386__) || defined(__x86_64__)
132 __ASM_STDCALL_FUNC( DebugBreak, 0, "jmp " __ASM_STDCALL("DbgBreakPoint", 0) )
133 #else
134 void WINAPI DebugBreak(void)
136 DbgBreakPoint();
138 #endif
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;
195 if (!str) str = "";
196 WARN( "%s\n", debugstr_a(str) );
198 /* raise exception, WaitForDebugEvent() will generate a corresponding debug event */
199 __TRY
201 ULONG_PTR args[2];
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;
210 __ENDTRY
211 if (caught_by_dbg) return;
213 /* send string to a system-wide monitor */
214 if (!mutex_inited)
216 /* first call to OutputDebugString, initialize mutex handle */
217 HANDLE mutex = CreateMutexExW( NULL, L"DBWinMutex", 0, SYNCHRONIZE );
218 if (mutex)
220 if (InterlockedCompareExchangePointer( &DBWinMutex, mutex, 0 ) != 0)
221 /* someone beat us here... */
222 CloseHandle( mutex );
224 mutex_inited = TRUE;
227 if (DBWinMutex)
229 HANDLE mapping;
231 mapping = OpenFileMappingW( FILE_MAP_WRITE, FALSE, L"DBWIN_BUFFER" );
232 if (mapping)
234 LPVOID 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 );
250 struct _mon_buffer_t
252 DWORD pid;
253 char buffer[1];
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 )
281 UNICODE_STRING strW;
282 STRING strA;
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;
304 if (count && args)
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 )
331 BOOL ret;
332 DWORD res;
333 int i;
335 for (;;)
337 HANDLE wait = 0;
338 debug_event_t data;
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 );
348 ret = FALSE;
349 goto done;
351 event->dwDebugEventCode = data.code;
352 event->dwProcessId = (DWORD)reply->pid;
353 event->dwThreadId = (DWORD)reply->tid;
354 switch (data.code)
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];
363 break;
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];
370 break;
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];
380 break;
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 );
385 break;
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;
397 break;
398 case EXIT_THREAD_DEBUG_EVENT:
399 event->u.ExitThread.dwExitCode = data.exit.exit_code;
400 break;
401 case EXIT_PROCESS_DEBUG_EVENT:
402 event->u.ExitProcess.dwExitCode = data.exit.exit_code;
403 break;
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;
411 break;
412 case UNLOAD_DLL_DEBUG_EVENT:
413 event->u.UnloadDll.lpBaseOfDll = wine_server_get_ptr( data.unload_dll.base );
414 break;
416 done:
417 /* nothing */ ;
419 SERVER_END_REQ;
420 if (ret) return TRUE;
421 if (!wait) break;
422 res = WaitForSingleObject( wait, timeout );
423 CloseHandle( wait );
424 if (res != STATUS_WAIT_0) break;
426 SetLastError( ERROR_SEM_TIMEOUT );
427 return FALSE;
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;
437 int len;
439 switch(rec->ExceptionCode)
441 case EXCEPTION_INT_DIVIDE_BY_ZERO:
442 len = snprintf( buffer, size, "Unhandled division by zero" );
443 break;
444 case EXCEPTION_INT_OVERFLOW:
445 len = snprintf( buffer, size, "Unhandled overflow" );
446 break;
447 case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
448 len = snprintf( buffer, size, "Unhandled array bounds" );
449 break;
450 case EXCEPTION_ILLEGAL_INSTRUCTION:
451 len = snprintf( buffer, size, "Unhandled illegal instruction" );
452 break;
453 case EXCEPTION_STACK_OVERFLOW:
454 len = snprintf( buffer, size, "Unhandled stack overflow" );
455 break;
456 case EXCEPTION_PRIV_INSTRUCTION:
457 len = snprintf( buffer, size, "Unhandled privileged instruction" );
458 break;
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]);
465 else
466 len = snprintf( buffer, size, "Unhandled page fault");
467 break;
468 case EXCEPTION_DATATYPE_MISALIGNMENT:
469 len = snprintf( buffer, size, "Unhandled alignment" );
470 break;
471 case CONTROL_C_EXIT:
472 len = snprintf( buffer, size, "Unhandled ^C");
473 break;
474 case STATUS_POSSIBLE_DEADLOCK:
475 len = snprintf( buffer, size, "Critical section %p wait failed",
476 (void *)rec->ExceptionInformation[0]);
477 break;
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] );
482 else
483 len = snprintf( buffer, size, "Unimplemented function %s.%ld called",
484 (char *)rec->ExceptionInformation[0], rec->ExceptionInformation[1] );
485 break;
486 case EXCEPTION_WINE_ASSERTION:
487 len = snprintf( buffer, size, "Assertion failed" );
488 break;
489 default:
490 len = snprintf( buffer, size, "Unhandled exception 0x%08x in thread %x",
491 rec->ExceptionCode, GetCurrentThreadId());
492 break;
494 if (len < 0 || len >= size) return;
495 snprintf( buffer + len, size - len, " at address %p", ptr->ExceptionRecord->ExceptionAddress );
499 /******************************************************************
500 * start_debugger
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;
509 HANDLE dbg_key;
510 DWORD autostart = TRUE;
511 PROCESS_INFORMATION info;
512 STARTUPINFOW startup;
513 BOOL ret = FALSE;
514 char buffer[256];
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;
522 attr.Attributes = 0;
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)
546 WCHAR *tmp;
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 );
552 format = tmp;
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 );
571 NtClose( dbg_key );
574 if (format)
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 );
581 else
583 cmdline = HeapAlloc( GetProcessHeap(), 0, 80 * sizeof(WCHAR) );
584 swprintf( cmdline, 80, L"winedbg --auto %ld %ld", (long)GetCurrentProcessId(), (long)HandleToLong(event) );
587 if (!autostart)
589 HMODULE mod = GetModuleHandleA( "user32.dll" );
590 MessageBoxA_funcptr pMessageBoxA = NULL;
592 if (mod) pMessageBoxA = (void *)GetProcAddress( mod, "MessageBoxA" );
593 if (pMessageBoxA)
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" );
602 goto exit;
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 );
619 break;
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 );
632 if (ret)
634 /* wait for debugger to come up... */
635 HANDLE handles[2];
636 CloseHandle( info.hThread );
637 handles[0] = event;
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() );
645 exit:
646 HeapFree(GetProcessHeap(), 0, cmdline);
647 return ret;
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 )
662 static HANDLE once;
664 if (once == 0)
666 OBJECT_ATTRIBUTES attr;
667 HANDLE event;
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 );
684 if (!ret)
686 /* so that the other threads won't be stuck */
687 NtSetEvent( once, NULL );
689 return ret;
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 */
701 return TRUE;
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 )
714 DWORD old_prot;
715 void *rsrc;
716 DWORD size;
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 )))
723 return FALSE;
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 );
727 return TRUE;
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;
745 break;
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 );
757 if (top_filter)
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;