dbghelp: Use local declarations of r_debug and link_map structs.
[wine.git] / dlls / ntdll / exception.c
blobc35312fd1d18e85c2b7c0ddf75d5aa5e2ddac8ae
1 /*
2 * NT exception handling routines
4 * Copyright 1999 Turchanov Sergey
5 * Copyright 1999 Alexandre Julliard
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 #include "config.h"
23 #include "wine/port.h"
25 #include <assert.h>
26 #include <errno.h>
27 #include <signal.h>
28 #include <stdarg.h>
30 #define NONAMELESSUNION
31 #define NONAMELESSSTRUCT
32 #include "ntstatus.h"
33 #define WIN32_NO_STATUS
34 #include "windef.h"
35 #include "winternl.h"
36 #include "wine/exception.h"
37 #include "wine/server.h"
38 #include "wine/list.h"
39 #include "wine/debug.h"
40 #include "excpt.h"
41 #include "ntdll_misc.h"
43 WINE_DEFAULT_DEBUG_CHANNEL(seh);
45 typedef struct
47 struct list entry;
48 PVECTORED_EXCEPTION_HANDLER func;
49 ULONG count;
50 } VECTORED_HANDLER;
52 static struct list vectored_exception_handlers = LIST_INIT(vectored_exception_handlers);
53 static struct list vectored_continue_handlers = LIST_INIT(vectored_continue_handlers);
55 static RTL_CRITICAL_SECTION vectored_handlers_section;
56 static RTL_CRITICAL_SECTION_DEBUG critsect_debug =
58 0, 0, &vectored_handlers_section,
59 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
60 0, 0, { (DWORD_PTR)(__FILE__ ": vectored_handlers_section") }
62 static RTL_CRITICAL_SECTION vectored_handlers_section = { &critsect_debug, -1, 0, 0, 0, 0 };
64 static PRTL_EXCEPTION_FILTER unhandled_exception_filter;
67 static VECTORED_HANDLER *add_vectored_handler( struct list *handler_list, ULONG first,
68 PVECTORED_EXCEPTION_HANDLER func )
70 VECTORED_HANDLER *handler = RtlAllocateHeap( GetProcessHeap(), 0, sizeof(*handler) );
71 if (handler)
73 handler->func = RtlEncodePointer( func );
74 handler->count = 1;
75 RtlEnterCriticalSection( &vectored_handlers_section );
76 if (first) list_add_head( handler_list, &handler->entry );
77 else list_add_tail( handler_list, &handler->entry );
78 RtlLeaveCriticalSection( &vectored_handlers_section );
80 return handler;
84 static ULONG remove_vectored_handler( struct list *handler_list, VECTORED_HANDLER *handler )
86 struct list *ptr;
87 ULONG ret = FALSE;
89 RtlEnterCriticalSection( &vectored_handlers_section );
90 LIST_FOR_EACH( ptr, handler_list )
92 VECTORED_HANDLER *curr_handler = LIST_ENTRY( ptr, VECTORED_HANDLER, entry );
93 if (curr_handler == handler)
95 if (!--curr_handler->count) list_remove( ptr );
96 else handler = NULL; /* don't free it yet */
97 ret = TRUE;
98 break;
101 RtlLeaveCriticalSection( &vectored_handlers_section );
102 if (ret) RtlFreeHeap( GetProcessHeap(), 0, handler );
103 return ret;
107 /**********************************************************************
108 * wait_suspend
110 * Wait until the thread is no longer suspended.
112 void wait_suspend( CONTEXT *context )
114 LARGE_INTEGER timeout;
115 int saved_errno = errno;
116 context_t server_context;
117 DWORD flags = context->ContextFlags;
119 context_to_server( &server_context, context );
121 /* store the context we got at suspend time */
122 SERVER_START_REQ( set_suspend_context )
124 wine_server_add_data( req, &server_context, sizeof(server_context) );
125 wine_server_call( req );
127 SERVER_END_REQ;
129 /* wait with 0 timeout, will only return once the thread is no longer suspended */
130 timeout.QuadPart = 0;
131 server_select( NULL, 0, SELECT_INTERRUPTIBLE, &timeout );
133 /* retrieve the new context */
134 SERVER_START_REQ( get_suspend_context )
136 wine_server_set_reply( req, &server_context, sizeof(server_context) );
137 wine_server_call( req );
138 if (wine_server_reply_size( reply ))
140 context_from_server( context, &server_context );
141 context->ContextFlags |= flags; /* unchanged registers are still available */
144 SERVER_END_REQ;
146 errno = saved_errno;
150 /**********************************************************************
151 * send_debug_event
153 * Send an EXCEPTION_DEBUG_EVENT event to the debugger.
155 NTSTATUS send_debug_event( EXCEPTION_RECORD *rec, int first_chance, CONTEXT *context )
157 NTSTATUS ret;
158 DWORD i;
159 obj_handle_t handle = 0;
160 client_ptr_t params[EXCEPTION_MAXIMUM_PARAMETERS];
161 context_t server_context;
162 select_op_t select_op;
164 if (!NtCurrentTeb()->Peb->BeingDebugged) return 0; /* no debugger present */
166 for (i = 0; i < min( rec->NumberParameters, EXCEPTION_MAXIMUM_PARAMETERS ); i++)
167 params[i] = rec->ExceptionInformation[i];
169 context_to_server( &server_context, context );
171 SERVER_START_REQ( queue_exception_event )
173 req->first = first_chance;
174 req->code = rec->ExceptionCode;
175 req->flags = rec->ExceptionFlags;
176 req->record = wine_server_client_ptr( rec->ExceptionRecord );
177 req->address = wine_server_client_ptr( rec->ExceptionAddress );
178 req->len = i * sizeof(params[0]);
179 wine_server_add_data( req, params, req->len );
180 wine_server_add_data( req, &server_context, sizeof(server_context) );
181 if (!wine_server_call( req )) handle = reply->handle;
183 SERVER_END_REQ;
184 if (!handle) return 0;
186 select_op.wait.op = SELECT_WAIT;
187 select_op.wait.handles[0] = handle;
188 server_select( &select_op, offsetof( select_op_t, wait.handles[1] ), SELECT_INTERRUPTIBLE, NULL );
190 SERVER_START_REQ( get_exception_status )
192 req->handle = handle;
193 wine_server_set_reply( req, &server_context, sizeof(server_context) );
194 ret = wine_server_call( req );
196 SERVER_END_REQ;
197 if (ret >= 0) context_from_server( context, &server_context );
198 return ret;
202 /**********************************************************************
203 * call_vectored_handlers
205 * Call the vectored handlers chain.
207 LONG call_vectored_handlers( EXCEPTION_RECORD *rec, CONTEXT *context )
209 struct list *ptr;
210 LONG ret = EXCEPTION_CONTINUE_SEARCH;
211 EXCEPTION_POINTERS except_ptrs;
212 PVECTORED_EXCEPTION_HANDLER func;
213 VECTORED_HANDLER *handler, *to_free = NULL;
215 except_ptrs.ExceptionRecord = rec;
216 except_ptrs.ContextRecord = context;
218 RtlEnterCriticalSection( &vectored_handlers_section );
219 ptr = list_head( &vectored_exception_handlers );
220 while (ptr)
222 handler = LIST_ENTRY( ptr, VECTORED_HANDLER, entry );
223 handler->count++;
224 func = RtlDecodePointer( handler->func );
225 RtlLeaveCriticalSection( &vectored_handlers_section );
226 RtlFreeHeap( GetProcessHeap(), 0, to_free );
227 to_free = NULL;
229 TRACE( "calling handler at %p code=%x flags=%x\n",
230 func, rec->ExceptionCode, rec->ExceptionFlags );
231 ret = func( &except_ptrs );
232 TRACE( "handler at %p returned %x\n", func, ret );
234 RtlEnterCriticalSection( &vectored_handlers_section );
235 ptr = list_next( &vectored_exception_handlers, ptr );
236 if (!--handler->count) /* removed during execution */
238 list_remove( &handler->entry );
239 to_free = handler;
241 if (ret == EXCEPTION_CONTINUE_EXECUTION) break;
243 RtlLeaveCriticalSection( &vectored_handlers_section );
244 RtlFreeHeap( GetProcessHeap(), 0, to_free );
245 return ret;
249 /*******************************************************************
250 * raise_status
252 * Implementation of RtlRaiseStatus with a specific exception record.
254 void raise_status( NTSTATUS status, EXCEPTION_RECORD *rec )
256 EXCEPTION_RECORD ExceptionRec;
258 ExceptionRec.ExceptionCode = status;
259 ExceptionRec.ExceptionFlags = EH_NONCONTINUABLE;
260 ExceptionRec.ExceptionRecord = rec;
261 ExceptionRec.NumberParameters = 0;
262 for (;;) RtlRaiseException( &ExceptionRec ); /* never returns */
266 /***********************************************************************
267 * RtlRaiseStatus (NTDLL.@)
269 * Raise an exception with ExceptionCode = status
271 void WINAPI RtlRaiseStatus( NTSTATUS status )
273 raise_status( status, NULL );
277 /*******************************************************************
278 * RtlAddVectoredContinueHandler (NTDLL.@)
280 PVOID WINAPI RtlAddVectoredContinueHandler( ULONG first, PVECTORED_EXCEPTION_HANDLER func )
282 return add_vectored_handler( &vectored_continue_handlers, first, func );
286 /*******************************************************************
287 * RtlRemoveVectoredContinueHandler (NTDLL.@)
289 ULONG WINAPI RtlRemoveVectoredContinueHandler( PVOID handler )
291 return remove_vectored_handler( &vectored_continue_handlers, handler );
295 /*******************************************************************
296 * RtlAddVectoredExceptionHandler (NTDLL.@)
298 PVOID WINAPI DECLSPEC_HOTPATCH RtlAddVectoredExceptionHandler( ULONG first, PVECTORED_EXCEPTION_HANDLER func )
300 return add_vectored_handler( &vectored_exception_handlers, first, func );
304 /*******************************************************************
305 * RtlRemoveVectoredExceptionHandler (NTDLL.@)
307 ULONG WINAPI RtlRemoveVectoredExceptionHandler( PVOID handler )
309 return remove_vectored_handler( &vectored_exception_handlers, handler );
313 /*******************************************************************
314 * RtlSetUnhandledExceptionFilter (NTDLL.@)
316 void WINAPI RtlSetUnhandledExceptionFilter( PRTL_EXCEPTION_FILTER filter )
318 unhandled_exception_filter = filter;
322 /*******************************************************************
323 * call_unhandled_exception_filter
325 LONG WINAPI call_unhandled_exception_filter( PEXCEPTION_POINTERS eptr )
327 if (!unhandled_exception_filter) return EXCEPTION_CONTINUE_SEARCH;
328 return unhandled_exception_filter( eptr );
332 #if defined(__x86_64__) || defined(__arm__) || defined(__aarch64__)
334 struct dynamic_unwind_entry
336 struct list entry;
337 ULONG_PTR base;
338 ULONG_PTR end;
339 RUNTIME_FUNCTION *table;
340 DWORD count;
341 DWORD max_count;
342 PGET_RUNTIME_FUNCTION_CALLBACK callback;
343 PVOID context;
346 static struct list dynamic_unwind_list = LIST_INIT(dynamic_unwind_list);
348 static RTL_CRITICAL_SECTION dynamic_unwind_section;
349 static RTL_CRITICAL_SECTION_DEBUG dynamic_unwind_debug =
351 0, 0, &dynamic_unwind_section,
352 { &dynamic_unwind_debug.ProcessLocksList, &dynamic_unwind_debug.ProcessLocksList },
353 0, 0, { (DWORD_PTR)(__FILE__ ": dynamic_unwind_section") }
355 static RTL_CRITICAL_SECTION dynamic_unwind_section = { &dynamic_unwind_debug, -1, 0, 0, 0, 0 };
357 static ULONG_PTR get_runtime_function_end( RUNTIME_FUNCTION *func, ULONG_PTR addr )
359 #ifdef __x86_64__
360 return func->EndAddress;
361 #elif defined(__arm__)
362 if (func->u.s.Flag) return func->BeginAddress + func->u.s.FunctionLength * 2;
363 else
365 struct unwind_info
367 DWORD function_length : 18;
368 DWORD version : 2;
369 DWORD x : 1;
370 DWORD e : 1;
371 DWORD f : 1;
372 DWORD count : 5;
373 DWORD words : 4;
374 } *info = (struct unwind_info *)(addr + func->u.UnwindData);
375 return func->BeginAddress + info->function_length * 2;
377 #else /* __aarch64__ */
378 if (func->u.s.Flag) return func->BeginAddress + func->u.s.FunctionLength * 4;
379 else
381 struct unwind_info
383 DWORD function_length : 18;
384 DWORD version : 2;
385 DWORD x : 1;
386 DWORD e : 1;
387 DWORD epilog : 5;
388 DWORD codes : 5;
389 } *info = (struct unwind_info *)(addr + func->u.UnwindData);
390 return func->BeginAddress + info->function_length * 4;
392 #endif
395 /**********************************************************************
396 * RtlAddFunctionTable (NTDLL.@)
398 BOOLEAN CDECL RtlAddFunctionTable( RUNTIME_FUNCTION *table, DWORD count, ULONG_PTR addr )
400 struct dynamic_unwind_entry *entry;
402 TRACE( "%p %u %lx\n", table, count, addr );
404 /* NOTE: Windows doesn't check if table is aligned or a NULL pointer */
406 entry = RtlAllocateHeap( GetProcessHeap(), 0, sizeof(*entry) );
407 if (!entry)
408 return FALSE;
410 entry->base = addr;
411 entry->end = addr + (count ? get_runtime_function_end( &table[count - 1], addr ) : 0);
412 entry->table = table;
413 entry->count = count;
414 entry->max_count = 0;
415 entry->callback = NULL;
416 entry->context = NULL;
418 RtlEnterCriticalSection( &dynamic_unwind_section );
419 list_add_tail( &dynamic_unwind_list, &entry->entry );
420 RtlLeaveCriticalSection( &dynamic_unwind_section );
421 return TRUE;
425 /**********************************************************************
426 * RtlInstallFunctionTableCallback (NTDLL.@)
428 BOOLEAN CDECL RtlInstallFunctionTableCallback( ULONG_PTR table, ULONG_PTR base, DWORD length,
429 PGET_RUNTIME_FUNCTION_CALLBACK callback, PVOID context,
430 PCWSTR dll )
432 struct dynamic_unwind_entry *entry;
434 TRACE( "%lx %lx %d %p %p %s\n", table, base, length, callback, context, wine_dbgstr_w(dll) );
436 /* NOTE: Windows doesn't check if the provided callback is a NULL pointer */
438 /* both low-order bits must be set */
439 if ((table & 0x3) != 0x3)
440 return FALSE;
442 entry = RtlAllocateHeap( GetProcessHeap(), 0, sizeof(*entry) );
443 if (!entry)
444 return FALSE;
446 entry->base = base;
447 entry->end = base + length;
448 entry->table = (RUNTIME_FUNCTION *)table;
449 entry->count = 0;
450 entry->max_count = 0;
451 entry->callback = callback;
452 entry->context = context;
454 RtlEnterCriticalSection( &dynamic_unwind_section );
455 list_add_tail( &dynamic_unwind_list, &entry->entry );
456 RtlLeaveCriticalSection( &dynamic_unwind_section );
458 return TRUE;
462 /*************************************************************************
463 * RtlAddGrowableFunctionTable (NTDLL.@)
465 DWORD WINAPI RtlAddGrowableFunctionTable( void **table, RUNTIME_FUNCTION *functions, DWORD count,
466 DWORD max_count, ULONG_PTR base, ULONG_PTR end )
468 struct dynamic_unwind_entry *entry;
470 TRACE( "%p, %p, %u, %u, %lx, %lx\n", table, functions, count, max_count, base, end );
472 entry = RtlAllocateHeap( GetProcessHeap(), 0, sizeof(*entry) );
473 if (!entry)
474 return STATUS_NO_MEMORY;
476 entry->base = base;
477 entry->end = end;
478 entry->table = functions;
479 entry->count = count;
480 entry->max_count = max_count;
481 entry->callback = NULL;
482 entry->context = NULL;
484 RtlEnterCriticalSection( &dynamic_unwind_section );
485 list_add_tail( &dynamic_unwind_list, &entry->entry );
486 RtlLeaveCriticalSection( &dynamic_unwind_section );
488 *table = entry;
490 return STATUS_SUCCESS;
494 /*************************************************************************
495 * RtlGrowFunctionTable (NTDLL.@)
497 void WINAPI RtlGrowFunctionTable( void *table, DWORD count )
499 struct dynamic_unwind_entry *entry;
501 TRACE( "%p, %u\n", table, count );
503 RtlEnterCriticalSection( &dynamic_unwind_section );
504 LIST_FOR_EACH_ENTRY( entry, &dynamic_unwind_list, struct dynamic_unwind_entry, entry )
506 if (entry == table)
508 if (count > entry->count && count <= entry->max_count)
509 entry->count = count;
510 break;
513 RtlLeaveCriticalSection( &dynamic_unwind_section );
517 /*************************************************************************
518 * RtlDeleteGrowableFunctionTable (NTDLL.@)
520 void WINAPI RtlDeleteGrowableFunctionTable( void *table )
522 struct dynamic_unwind_entry *entry, *to_free = NULL;
524 TRACE( "%p\n", table );
526 RtlEnterCriticalSection( &dynamic_unwind_section );
527 LIST_FOR_EACH_ENTRY( entry, &dynamic_unwind_list, struct dynamic_unwind_entry, entry )
529 if (entry == table)
531 to_free = entry;
532 list_remove( &entry->entry );
533 break;
536 RtlLeaveCriticalSection( &dynamic_unwind_section );
538 RtlFreeHeap( GetProcessHeap(), 0, to_free );
542 /**********************************************************************
543 * RtlDeleteFunctionTable (NTDLL.@)
545 BOOLEAN CDECL RtlDeleteFunctionTable( RUNTIME_FUNCTION *table )
547 struct dynamic_unwind_entry *entry, *to_free = NULL;
549 TRACE( "%p\n", table );
551 RtlEnterCriticalSection( &dynamic_unwind_section );
552 LIST_FOR_EACH_ENTRY( entry, &dynamic_unwind_list, struct dynamic_unwind_entry, entry )
554 if (entry->table == table)
556 to_free = entry;
557 list_remove( &entry->entry );
558 break;
561 RtlLeaveCriticalSection( &dynamic_unwind_section );
563 if (!to_free) return FALSE;
565 RtlFreeHeap( GetProcessHeap(), 0, to_free );
566 return TRUE;
570 /* helper for lookup_function_info() */
571 static RUNTIME_FUNCTION *find_function_info( ULONG_PTR pc, ULONG_PTR base,
572 RUNTIME_FUNCTION *func, ULONG size )
574 int min = 0;
575 int max = size - 1;
577 while (min <= max)
579 #ifdef __x86_64__
580 int pos = (min + max) / 2;
581 if (pc < base + func[pos].BeginAddress) max = pos - 1;
582 else if (pc >= base + func[pos].EndAddress) min = pos + 1;
583 else
585 func += pos;
586 while (func->UnwindData & 1) /* follow chained entry */
587 func = (RUNTIME_FUNCTION *)(base + (func->UnwindData & ~1));
588 return func;
590 #elif defined(__arm__)
591 int pos = (min + max) / 2;
592 if (pc < base + (func[pos].BeginAddress & ~1)) max = pos - 1;
593 else if (pc >= base + get_runtime_function_end( &func[pos], base )) min = pos + 1;
594 else return func + pos;
595 #else /* __aarch64__ */
596 int pos = (min + max) / 2;
597 if (pc < base + func[pos].BeginAddress) max = pos - 1;
598 else if (pc >= base + get_runtime_function_end( &func[pos], base )) min = pos + 1;
599 else return func + pos;
600 #endif
602 return NULL;
605 /**********************************************************************
606 * lookup_function_info
608 RUNTIME_FUNCTION *lookup_function_info( ULONG_PTR pc, ULONG_PTR *base, LDR_MODULE **module )
610 RUNTIME_FUNCTION *func = NULL;
611 struct dynamic_unwind_entry *entry;
612 ULONG size;
614 /* PE module or wine module */
615 if (!LdrFindEntryForAddress( (void *)pc, module ))
617 *base = (ULONG_PTR)(*module)->BaseAddress;
618 if ((func = RtlImageDirectoryEntryToData( (*module)->BaseAddress, TRUE,
619 IMAGE_DIRECTORY_ENTRY_EXCEPTION, &size )))
621 /* lookup in function table */
622 func = find_function_info( pc, (ULONG_PTR)(*module)->BaseAddress, func, size/sizeof(*func) );
625 else
627 *module = NULL;
629 RtlEnterCriticalSection( &dynamic_unwind_section );
630 LIST_FOR_EACH_ENTRY( entry, &dynamic_unwind_list, struct dynamic_unwind_entry, entry )
632 if (pc >= entry->base && pc < entry->end)
634 *base = entry->base;
635 /* use callback or lookup in function table */
636 if (entry->callback)
637 func = entry->callback( pc, entry->context );
638 else
639 func = find_function_info( pc, entry->base, entry->table, entry->count );
640 break;
643 RtlLeaveCriticalSection( &dynamic_unwind_section );
646 return func;
649 /**********************************************************************
650 * RtlLookupFunctionEntry (NTDLL.@)
652 PRUNTIME_FUNCTION WINAPI RtlLookupFunctionEntry( ULONG_PTR pc, ULONG_PTR *base,
653 UNWIND_HISTORY_TABLE *table )
655 LDR_MODULE *module;
656 RUNTIME_FUNCTION *func;
658 /* FIXME: should use the history table to make things faster */
660 if (!(func = lookup_function_info( pc, base, &module )))
662 *base = 0;
663 WARN( "no exception table found for %lx\n", pc );
665 return func;
668 #endif /* __x86_64__ || __arm__ || __aarch64__ */
670 /*************************************************************
671 * __wine_spec_unimplemented_stub
673 * ntdll-specific implementation to avoid depending on kernel functions.
674 * Can be removed once ntdll.spec no longer contains stubs.
676 void __cdecl __wine_spec_unimplemented_stub( const char *module, const char *function )
678 EXCEPTION_RECORD record;
680 record.ExceptionCode = EXCEPTION_WINE_STUB;
681 record.ExceptionFlags = EH_NONCONTINUABLE;
682 record.ExceptionRecord = NULL;
683 record.ExceptionAddress = __wine_spec_unimplemented_stub;
684 record.NumberParameters = 2;
685 record.ExceptionInformation[0] = (ULONG_PTR)module;
686 record.ExceptionInformation[1] = (ULONG_PTR)function;
687 for (;;) RtlRaiseException( &record );
691 /*************************************************************
692 * IsBadStringPtrA
694 * IsBadStringPtrA replacement for ntdll, to catch exception in debug traces.
696 BOOL WINAPI IsBadStringPtrA( LPCSTR str, UINT_PTR max )
698 if (!str) return TRUE;
699 __TRY
701 volatile const char *p = str;
702 while (p != str + max) if (!*p++) break;
704 __EXCEPT_PAGE_FAULT
706 return TRUE;
708 __ENDTRY
709 return FALSE;
713 /*************************************************************
714 * IsBadStringPtrW
716 * IsBadStringPtrW replacement for ntdll, to catch exception in debug traces.
718 BOOL WINAPI IsBadStringPtrW( LPCWSTR str, UINT_PTR max )
720 if (!str) return TRUE;
721 __TRY
723 volatile const WCHAR *p = str;
724 while (p != str + max) if (!*p++) break;
726 __EXCEPT_PAGE_FAULT
728 return TRUE;
730 __ENDTRY
731 return FALSE;