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
27 #define NONAMELESSUNION
28 #define NONAMELESSSTRUCT
30 #define WIN32_NO_STATUS
33 #include "wine/exception.h"
34 #include "wine/server.h"
35 #include "wine/list.h"
36 #include "wine/debug.h"
38 #include "ntdll_misc.h"
40 WINE_DEFAULT_DEBUG_CHANNEL(seh
);
45 PVECTORED_EXCEPTION_HANDLER func
;
49 static struct list vectored_exception_handlers
= LIST_INIT(vectored_exception_handlers
);
50 static struct list vectored_continue_handlers
= LIST_INIT(vectored_continue_handlers
);
52 static RTL_CRITICAL_SECTION vectored_handlers_section
;
53 static RTL_CRITICAL_SECTION_DEBUG critsect_debug
=
55 0, 0, &vectored_handlers_section
,
56 { &critsect_debug
.ProcessLocksList
, &critsect_debug
.ProcessLocksList
},
57 0, 0, { (DWORD_PTR
)(__FILE__
": vectored_handlers_section") }
59 static RTL_CRITICAL_SECTION vectored_handlers_section
= { &critsect_debug
, -1, 0, 0, 0, 0 };
61 static PRTL_EXCEPTION_FILTER unhandled_exception_filter
;
64 static VECTORED_HANDLER
*add_vectored_handler( struct list
*handler_list
, ULONG first
,
65 PVECTORED_EXCEPTION_HANDLER func
)
67 VECTORED_HANDLER
*handler
= RtlAllocateHeap( GetProcessHeap(), 0, sizeof(*handler
) );
70 handler
->func
= RtlEncodePointer( func
);
72 RtlEnterCriticalSection( &vectored_handlers_section
);
73 if (first
) list_add_head( handler_list
, &handler
->entry
);
74 else list_add_tail( handler_list
, &handler
->entry
);
75 RtlLeaveCriticalSection( &vectored_handlers_section
);
81 static ULONG
remove_vectored_handler( struct list
*handler_list
, VECTORED_HANDLER
*handler
)
86 RtlEnterCriticalSection( &vectored_handlers_section
);
87 LIST_FOR_EACH( ptr
, handler_list
)
89 VECTORED_HANDLER
*curr_handler
= LIST_ENTRY( ptr
, VECTORED_HANDLER
, entry
);
90 if (curr_handler
== handler
)
92 if (!--curr_handler
->count
) list_remove( ptr
);
93 else handler
= NULL
; /* don't free it yet */
98 RtlLeaveCriticalSection( &vectored_handlers_section
);
99 if (ret
) RtlFreeHeap( GetProcessHeap(), 0, handler
);
104 /**********************************************************************
105 * call_vectored_handlers
107 * Call the vectored handlers chain.
109 LONG
call_vectored_handlers( EXCEPTION_RECORD
*rec
, CONTEXT
*context
)
112 LONG ret
= EXCEPTION_CONTINUE_SEARCH
;
113 EXCEPTION_POINTERS except_ptrs
;
114 PVECTORED_EXCEPTION_HANDLER func
;
115 VECTORED_HANDLER
*handler
, *to_free
= NULL
;
117 except_ptrs
.ExceptionRecord
= rec
;
118 except_ptrs
.ContextRecord
= context
;
120 RtlEnterCriticalSection( &vectored_handlers_section
);
121 ptr
= list_head( &vectored_exception_handlers
);
124 handler
= LIST_ENTRY( ptr
, VECTORED_HANDLER
, entry
);
126 func
= RtlDecodePointer( handler
->func
);
127 RtlLeaveCriticalSection( &vectored_handlers_section
);
128 RtlFreeHeap( GetProcessHeap(), 0, to_free
);
131 TRACE( "calling handler at %p code=%x flags=%x\n",
132 func
, rec
->ExceptionCode
, rec
->ExceptionFlags
);
133 ret
= func( &except_ptrs
);
134 TRACE( "handler at %p returned %x\n", func
, ret
);
136 RtlEnterCriticalSection( &vectored_handlers_section
);
137 ptr
= list_next( &vectored_exception_handlers
, ptr
);
138 if (!--handler
->count
) /* removed during execution */
140 list_remove( &handler
->entry
);
143 if (ret
== EXCEPTION_CONTINUE_EXECUTION
) break;
145 RtlLeaveCriticalSection( &vectored_handlers_section
);
146 RtlFreeHeap( GetProcessHeap(), 0, to_free
);
151 /*******************************************************************
154 * Implementation of RtlRaiseStatus with a specific exception record.
156 void raise_status( NTSTATUS status
, EXCEPTION_RECORD
*rec
)
158 EXCEPTION_RECORD ExceptionRec
;
160 ExceptionRec
.ExceptionCode
= status
;
161 ExceptionRec
.ExceptionFlags
= EH_NONCONTINUABLE
;
162 ExceptionRec
.ExceptionRecord
= rec
;
163 ExceptionRec
.NumberParameters
= 0;
164 for (;;) RtlRaiseException( &ExceptionRec
); /* never returns */
168 /***********************************************************************
169 * RtlRaiseStatus (NTDLL.@)
171 * Raise an exception with ExceptionCode = status
173 void WINAPI
RtlRaiseStatus( NTSTATUS status
)
175 raise_status( status
, NULL
);
179 /*******************************************************************
180 * KiRaiseUserExceptionDispatcher (NTDLL.@)
182 void WINAPI
KiRaiseUserExceptionDispatcher(void)
184 EXCEPTION_RECORD rec
= { NtCurrentTeb()->ExceptionCode
};
185 RtlRaiseException( &rec
);
189 /*******************************************************************
190 * RtlAddVectoredContinueHandler (NTDLL.@)
192 PVOID WINAPI
RtlAddVectoredContinueHandler( ULONG first
, PVECTORED_EXCEPTION_HANDLER func
)
194 return add_vectored_handler( &vectored_continue_handlers
, first
, func
);
198 /*******************************************************************
199 * RtlRemoveVectoredContinueHandler (NTDLL.@)
201 ULONG WINAPI
RtlRemoveVectoredContinueHandler( PVOID handler
)
203 return remove_vectored_handler( &vectored_continue_handlers
, handler
);
207 /*******************************************************************
208 * RtlAddVectoredExceptionHandler (NTDLL.@)
210 PVOID WINAPI DECLSPEC_HOTPATCH
RtlAddVectoredExceptionHandler( ULONG first
, PVECTORED_EXCEPTION_HANDLER func
)
212 return add_vectored_handler( &vectored_exception_handlers
, first
, func
);
216 /*******************************************************************
217 * RtlRemoveVectoredExceptionHandler (NTDLL.@)
219 ULONG WINAPI
RtlRemoveVectoredExceptionHandler( PVOID handler
)
221 return remove_vectored_handler( &vectored_exception_handlers
, handler
);
225 /*******************************************************************
226 * RtlSetUnhandledExceptionFilter (NTDLL.@)
228 void WINAPI
RtlSetUnhandledExceptionFilter( PRTL_EXCEPTION_FILTER filter
)
230 unhandled_exception_filter
= filter
;
234 /*******************************************************************
235 * call_unhandled_exception_filter
237 LONG WINAPI
call_unhandled_exception_filter( PEXCEPTION_POINTERS eptr
)
239 if (!unhandled_exception_filter
) return EXCEPTION_CONTINUE_SEARCH
;
240 return unhandled_exception_filter( eptr
);
244 #if defined(__x86_64__) || defined(__arm__) || defined(__aarch64__)
246 struct dynamic_unwind_entry
251 RUNTIME_FUNCTION
*table
;
254 PGET_RUNTIME_FUNCTION_CALLBACK callback
;
258 static struct list dynamic_unwind_list
= LIST_INIT(dynamic_unwind_list
);
260 static RTL_CRITICAL_SECTION dynamic_unwind_section
;
261 static RTL_CRITICAL_SECTION_DEBUG dynamic_unwind_debug
=
263 0, 0, &dynamic_unwind_section
,
264 { &dynamic_unwind_debug
.ProcessLocksList
, &dynamic_unwind_debug
.ProcessLocksList
},
265 0, 0, { (DWORD_PTR
)(__FILE__
": dynamic_unwind_section") }
267 static RTL_CRITICAL_SECTION dynamic_unwind_section
= { &dynamic_unwind_debug
, -1, 0, 0, 0, 0 };
269 static ULONG_PTR
get_runtime_function_end( RUNTIME_FUNCTION
*func
, ULONG_PTR addr
)
272 return func
->EndAddress
;
273 #elif defined(__arm__)
274 if (func
->u
.s
.Flag
) return func
->BeginAddress
+ func
->u
.s
.FunctionLength
* 2;
279 DWORD function_length
: 18;
286 } *info
= (struct unwind_info
*)(addr
+ func
->u
.UnwindData
);
287 return func
->BeginAddress
+ info
->function_length
* 2;
289 #else /* __aarch64__ */
290 if (func
->u
.s
.Flag
) return func
->BeginAddress
+ func
->u
.s
.FunctionLength
* 4;
295 DWORD function_length
: 18;
301 } *info
= (struct unwind_info
*)(addr
+ func
->u
.UnwindData
);
302 return func
->BeginAddress
+ info
->function_length
* 4;
307 /**********************************************************************
308 * RtlAddFunctionTable (NTDLL.@)
310 BOOLEAN CDECL
RtlAddFunctionTable( RUNTIME_FUNCTION
*table
, DWORD count
, ULONG_PTR addr
)
312 struct dynamic_unwind_entry
*entry
;
314 TRACE( "%p %u %lx\n", table
, count
, addr
);
316 /* NOTE: Windows doesn't check if table is aligned or a NULL pointer */
318 entry
= RtlAllocateHeap( GetProcessHeap(), 0, sizeof(*entry
) );
323 entry
->end
= addr
+ (count
? get_runtime_function_end( &table
[count
- 1], addr
) : 0);
324 entry
->table
= table
;
325 entry
->count
= count
;
326 entry
->max_count
= 0;
327 entry
->callback
= NULL
;
328 entry
->context
= NULL
;
330 RtlEnterCriticalSection( &dynamic_unwind_section
);
331 list_add_tail( &dynamic_unwind_list
, &entry
->entry
);
332 RtlLeaveCriticalSection( &dynamic_unwind_section
);
337 /**********************************************************************
338 * RtlInstallFunctionTableCallback (NTDLL.@)
340 BOOLEAN CDECL
RtlInstallFunctionTableCallback( ULONG_PTR table
, ULONG_PTR base
, DWORD length
,
341 PGET_RUNTIME_FUNCTION_CALLBACK callback
, PVOID context
,
344 struct dynamic_unwind_entry
*entry
;
346 TRACE( "%lx %lx %d %p %p %s\n", table
, base
, length
, callback
, context
, wine_dbgstr_w(dll
) );
348 /* NOTE: Windows doesn't check if the provided callback is a NULL pointer */
350 /* both low-order bits must be set */
351 if ((table
& 0x3) != 0x3)
354 entry
= RtlAllocateHeap( GetProcessHeap(), 0, sizeof(*entry
) );
359 entry
->end
= base
+ length
;
360 entry
->table
= (RUNTIME_FUNCTION
*)table
;
362 entry
->max_count
= 0;
363 entry
->callback
= callback
;
364 entry
->context
= context
;
366 RtlEnterCriticalSection( &dynamic_unwind_section
);
367 list_add_tail( &dynamic_unwind_list
, &entry
->entry
);
368 RtlLeaveCriticalSection( &dynamic_unwind_section
);
374 /*************************************************************************
375 * RtlAddGrowableFunctionTable (NTDLL.@)
377 DWORD WINAPI
RtlAddGrowableFunctionTable( void **table
, RUNTIME_FUNCTION
*functions
, DWORD count
,
378 DWORD max_count
, ULONG_PTR base
, ULONG_PTR end
)
380 struct dynamic_unwind_entry
*entry
;
382 TRACE( "%p, %p, %u, %u, %lx, %lx\n", table
, functions
, count
, max_count
, base
, end
);
384 entry
= RtlAllocateHeap( GetProcessHeap(), 0, sizeof(*entry
) );
386 return STATUS_NO_MEMORY
;
390 entry
->table
= functions
;
391 entry
->count
= count
;
392 entry
->max_count
= max_count
;
393 entry
->callback
= NULL
;
394 entry
->context
= NULL
;
396 RtlEnterCriticalSection( &dynamic_unwind_section
);
397 list_add_tail( &dynamic_unwind_list
, &entry
->entry
);
398 RtlLeaveCriticalSection( &dynamic_unwind_section
);
402 return STATUS_SUCCESS
;
406 /*************************************************************************
407 * RtlGrowFunctionTable (NTDLL.@)
409 void WINAPI
RtlGrowFunctionTable( void *table
, DWORD count
)
411 struct dynamic_unwind_entry
*entry
;
413 TRACE( "%p, %u\n", table
, count
);
415 RtlEnterCriticalSection( &dynamic_unwind_section
);
416 LIST_FOR_EACH_ENTRY( entry
, &dynamic_unwind_list
, struct dynamic_unwind_entry
, entry
)
420 if (count
> entry
->count
&& count
<= entry
->max_count
)
421 entry
->count
= count
;
425 RtlLeaveCriticalSection( &dynamic_unwind_section
);
429 /*************************************************************************
430 * RtlDeleteGrowableFunctionTable (NTDLL.@)
432 void WINAPI
RtlDeleteGrowableFunctionTable( void *table
)
434 struct dynamic_unwind_entry
*entry
, *to_free
= NULL
;
436 TRACE( "%p\n", table
);
438 RtlEnterCriticalSection( &dynamic_unwind_section
);
439 LIST_FOR_EACH_ENTRY( entry
, &dynamic_unwind_list
, struct dynamic_unwind_entry
, entry
)
444 list_remove( &entry
->entry
);
448 RtlLeaveCriticalSection( &dynamic_unwind_section
);
450 RtlFreeHeap( GetProcessHeap(), 0, to_free
);
454 /**********************************************************************
455 * RtlDeleteFunctionTable (NTDLL.@)
457 BOOLEAN CDECL
RtlDeleteFunctionTable( RUNTIME_FUNCTION
*table
)
459 struct dynamic_unwind_entry
*entry
, *to_free
= NULL
;
461 TRACE( "%p\n", table
);
463 RtlEnterCriticalSection( &dynamic_unwind_section
);
464 LIST_FOR_EACH_ENTRY( entry
, &dynamic_unwind_list
, struct dynamic_unwind_entry
, entry
)
466 if (entry
->table
== table
)
469 list_remove( &entry
->entry
);
473 RtlLeaveCriticalSection( &dynamic_unwind_section
);
475 if (!to_free
) return FALSE
;
477 RtlFreeHeap( GetProcessHeap(), 0, to_free
);
482 /* helper for lookup_function_info() */
483 static RUNTIME_FUNCTION
*find_function_info( ULONG_PTR pc
, ULONG_PTR base
,
484 RUNTIME_FUNCTION
*func
, ULONG size
)
492 int pos
= (min
+ max
) / 2;
493 if (pc
< base
+ func
[pos
].BeginAddress
) max
= pos
- 1;
494 else if (pc
>= base
+ func
[pos
].EndAddress
) min
= pos
+ 1;
498 while (func
->UnwindData
& 1) /* follow chained entry */
499 func
= (RUNTIME_FUNCTION
*)(base
+ (func
->UnwindData
& ~1));
502 #elif defined(__arm__)
503 int pos
= (min
+ max
) / 2;
504 if (pc
< base
+ (func
[pos
].BeginAddress
& ~1)) max
= pos
- 1;
505 else if (pc
>= base
+ get_runtime_function_end( &func
[pos
], base
)) min
= pos
+ 1;
506 else return func
+ pos
;
507 #else /* __aarch64__ */
508 int pos
= (min
+ max
) / 2;
509 if (pc
< base
+ func
[pos
].BeginAddress
) max
= pos
- 1;
510 else if (pc
>= base
+ get_runtime_function_end( &func
[pos
], base
)) min
= pos
+ 1;
511 else return func
+ pos
;
517 /**********************************************************************
518 * lookup_function_info
520 RUNTIME_FUNCTION
*lookup_function_info( ULONG_PTR pc
, ULONG_PTR
*base
, LDR_DATA_TABLE_ENTRY
**module
)
522 RUNTIME_FUNCTION
*func
= NULL
;
523 struct dynamic_unwind_entry
*entry
;
526 /* PE module or wine module */
527 if (!LdrFindEntryForAddress( (void *)pc
, module
))
529 *base
= (ULONG_PTR
)(*module
)->DllBase
;
530 if ((func
= RtlImageDirectoryEntryToData( (*module
)->DllBase
, TRUE
,
531 IMAGE_DIRECTORY_ENTRY_EXCEPTION
, &size
)))
533 /* lookup in function table */
534 func
= find_function_info( pc
, (ULONG_PTR
)(*module
)->DllBase
, func
, size
/sizeof(*func
) );
541 RtlEnterCriticalSection( &dynamic_unwind_section
);
542 LIST_FOR_EACH_ENTRY( entry
, &dynamic_unwind_list
, struct dynamic_unwind_entry
, entry
)
544 if (pc
>= entry
->base
&& pc
< entry
->end
)
547 /* use callback or lookup in function table */
549 func
= entry
->callback( pc
, entry
->context
);
551 func
= find_function_info( pc
, entry
->base
, entry
->table
, entry
->count
);
555 RtlLeaveCriticalSection( &dynamic_unwind_section
);
561 /**********************************************************************
562 * RtlLookupFunctionEntry (NTDLL.@)
564 PRUNTIME_FUNCTION WINAPI
RtlLookupFunctionEntry( ULONG_PTR pc
, ULONG_PTR
*base
,
565 UNWIND_HISTORY_TABLE
*table
)
567 LDR_DATA_TABLE_ENTRY
*module
;
568 RUNTIME_FUNCTION
*func
;
570 /* FIXME: should use the history table to make things faster */
572 if (!(func
= lookup_function_info( pc
, base
, &module
)))
575 WARN( "no exception table found for %lx\n", pc
);
580 #endif /* __x86_64__ || __arm__ || __aarch64__ */
583 /*************************************************************
586 void __cdecl
_assert( const char *str
, const char *file
, unsigned int line
)
588 ERR( "%s:%u: Assertion failed %s\n", file
, line
, debugstr_a(str
) );
589 RtlRaiseStatus( EXCEPTION_WINE_ASSERTION
);
593 /*************************************************************
594 * __wine_spec_unimplemented_stub
596 * ntdll-specific implementation to avoid depending on kernel functions.
597 * Can be removed once ntdll.spec no longer contains stubs.
599 void __cdecl
__wine_spec_unimplemented_stub( const char *module
, const char *function
)
601 EXCEPTION_RECORD record
;
603 record
.ExceptionCode
= EXCEPTION_WINE_STUB
;
604 record
.ExceptionFlags
= EH_NONCONTINUABLE
;
605 record
.ExceptionRecord
= NULL
;
606 record
.ExceptionAddress
= __wine_spec_unimplemented_stub
;
607 record
.NumberParameters
= 2;
608 record
.ExceptionInformation
[0] = (ULONG_PTR
)module
;
609 record
.ExceptionInformation
[1] = (ULONG_PTR
)function
;
610 for (;;) RtlRaiseException( &record
);
614 /*************************************************************
617 * IsBadStringPtrA replacement for ntdll, to catch exception in debug traces.
619 BOOL WINAPI
IsBadStringPtrA( LPCSTR str
, UINT_PTR max
)
621 if (!str
) return TRUE
;
624 volatile const char *p
= str
;
625 while (p
!= str
+ max
) if (!*p
++) break;
636 /*************************************************************
639 * IsBadStringPtrW replacement for ntdll, to catch exception in debug traces.
641 BOOL WINAPI
IsBadStringPtrW( LPCWSTR str
, UINT_PTR max
)
643 if (!str
) return TRUE
;
646 volatile const WCHAR
*p
= str
;
647 while (p
!= str
+ max
) if (!*p
++) break;