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
23 #include "wine/port.h"
30 #define NONAMELESSUNION
31 #define NONAMELESSSTRUCT
33 #define WIN32_NO_STATUS
36 #include "wine/exception.h"
37 #include "wine/server.h"
38 #include "wine/list.h"
39 #include "wine/debug.h"
41 #include "ntdll_misc.h"
43 WINE_DEFAULT_DEBUG_CHANNEL(seh
);
48 PVECTORED_EXCEPTION_HANDLER func
;
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
) );
73 handler
->func
= RtlEncodePointer( func
);
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
);
84 static ULONG
remove_vectored_handler( struct list
*handler_list
, VECTORED_HANDLER
*handler
)
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 */
101 RtlLeaveCriticalSection( &vectored_handlers_section
);
102 if (ret
) RtlFreeHeap( GetProcessHeap(), 0, handler
);
107 /**********************************************************************
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
);
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 */
150 /**********************************************************************
153 * Send an EXCEPTION_DEBUG_EVENT event to the debugger.
155 NTSTATUS
send_debug_event( EXCEPTION_RECORD
*rec
, int first_chance
, CONTEXT
*context
)
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
;
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
);
197 if (ret
>= 0) context_from_server( context
, &server_context
);
202 /**********************************************************************
203 * call_vectored_handlers
205 * Call the vectored handlers chain.
207 LONG
call_vectored_handlers( EXCEPTION_RECORD
*rec
, CONTEXT
*context
)
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
);
222 handler
= LIST_ENTRY( ptr
, VECTORED_HANDLER
, entry
);
224 func
= RtlDecodePointer( handler
->func
);
225 RtlLeaveCriticalSection( &vectored_handlers_section
);
226 RtlFreeHeap( GetProcessHeap(), 0, to_free
);
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
);
241 if (ret
== EXCEPTION_CONTINUE_EXECUTION
) break;
243 RtlLeaveCriticalSection( &vectored_handlers_section
);
244 RtlFreeHeap( GetProcessHeap(), 0, to_free
);
249 /*******************************************************************
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
339 RUNTIME_FUNCTION
*table
;
342 PGET_RUNTIME_FUNCTION_CALLBACK callback
;
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
)
360 return func
->EndAddress
;
361 #elif defined(__arm__)
362 if (func
->u
.s
.Flag
) return func
->BeginAddress
+ func
->u
.s
.FunctionLength
* 2;
367 DWORD function_length
: 18;
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;
383 DWORD function_length
: 18;
389 } *info
= (struct unwind_info
*)(addr
+ func
->u
.UnwindData
);
390 return func
->BeginAddress
+ info
->function_length
* 4;
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
) );
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
);
425 /**********************************************************************
426 * RtlInstallFunctionTableCallback (NTDLL.@)
428 BOOLEAN CDECL
RtlInstallFunctionTableCallback( ULONG_PTR table
, ULONG_PTR base
, DWORD length
,
429 PGET_RUNTIME_FUNCTION_CALLBACK callback
, PVOID context
,
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)
442 entry
= RtlAllocateHeap( GetProcessHeap(), 0, sizeof(*entry
) );
447 entry
->end
= base
+ length
;
448 entry
->table
= (RUNTIME_FUNCTION
*)table
;
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
);
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
) );
474 return STATUS_NO_MEMORY
;
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
);
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
)
508 if (count
> entry
->count
&& count
<= entry
->max_count
)
509 entry
->count
= count
;
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
)
532 list_remove( &entry
->entry
);
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
)
557 list_remove( &entry
->entry
);
561 RtlLeaveCriticalSection( &dynamic_unwind_section
);
563 if (!to_free
) return FALSE
;
565 RtlFreeHeap( GetProcessHeap(), 0, to_free
);
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
)
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;
586 while (func
->UnwindData
& 1) /* follow chained entry */
587 func
= (RUNTIME_FUNCTION
*)(base
+ (func
->UnwindData
& ~1));
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
;
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
;
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
) );
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
)
635 /* use callback or lookup in function table */
637 func
= entry
->callback( pc
, entry
->context
);
639 func
= find_function_info( pc
, entry
->base
, entry
->table
, entry
->count
);
643 RtlLeaveCriticalSection( &dynamic_unwind_section
);
649 /**********************************************************************
650 * RtlLookupFunctionEntry (NTDLL.@)
652 PRUNTIME_FUNCTION WINAPI
RtlLookupFunctionEntry( ULONG_PTR pc
, ULONG_PTR
*base
,
653 UNWIND_HISTORY_TABLE
*table
)
656 RUNTIME_FUNCTION
*func
;
658 /* FIXME: should use the history table to make things faster */
660 if (!(func
= lookup_function_info( pc
, base
, &module
)))
663 WARN( "no exception table found for %lx\n", pc
);
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 /*************************************************************
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
;
701 volatile const char *p
= str
;
702 while (p
!= str
+ max
) if (!*p
++) break;
713 /*************************************************************
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
;
723 volatile const WCHAR
*p
= str
;
724 while (p
!= str
+ max
) if (!*p
++) break;