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
28 #define WIN32_NO_STATUS
32 #include "wine/exception.h"
33 #include "wine/list.h"
34 #include "wine/debug.h"
36 #include "ntdll_misc.h"
38 WINE_DEFAULT_DEBUG_CHANNEL(seh
);
43 PVECTORED_EXCEPTION_HANDLER func
;
47 static struct list vectored_exception_handlers
= LIST_INIT(vectored_exception_handlers
);
48 static struct list vectored_continue_handlers
= LIST_INIT(vectored_continue_handlers
);
50 static RTL_CRITICAL_SECTION vectored_handlers_section
;
51 static RTL_CRITICAL_SECTION_DEBUG critsect_debug
=
53 0, 0, &vectored_handlers_section
,
54 { &critsect_debug
.ProcessLocksList
, &critsect_debug
.ProcessLocksList
},
55 0, 0, { (DWORD_PTR
)(__FILE__
": vectored_handlers_section") }
57 static RTL_CRITICAL_SECTION vectored_handlers_section
= { &critsect_debug
, -1, 0, 0, 0, 0 };
59 static PRTL_EXCEPTION_FILTER unhandled_exception_filter
;
61 const char *debugstr_exception_code( DWORD code
)
65 case CONTROL_C_EXIT
: return "CONTROL_C_EXIT";
66 case DBG_CONTROL_C
: return "DBG_CONTROL_C";
67 case DBG_PRINTEXCEPTION_C
: return "DBG_PRINTEXCEPTION_C";
68 case DBG_PRINTEXCEPTION_WIDE_C
: return "DBG_PRINTEXCEPTION_WIDE_C";
69 case EXCEPTION_ACCESS_VIOLATION
: return "EXCEPTION_ACCESS_VIOLATION";
70 case EXCEPTION_ARRAY_BOUNDS_EXCEEDED
: return "EXCEPTION_ARRAY_BOUNDS_EXCEEDED";
71 case EXCEPTION_BREAKPOINT
: return "EXCEPTION_BREAKPOINT";
72 case EXCEPTION_DATATYPE_MISALIGNMENT
: return "EXCEPTION_DATATYPE_MISALIGNMENT";
73 case EXCEPTION_FLT_DENORMAL_OPERAND
: return "EXCEPTION_FLT_DENORMAL_OPERAND";
74 case EXCEPTION_FLT_DIVIDE_BY_ZERO
: return "EXCEPTION_FLT_DIVIDE_BY_ZERO";
75 case EXCEPTION_FLT_INEXACT_RESULT
: return "EXCEPTION_FLT_INEXACT_RESULT";
76 case EXCEPTION_FLT_INVALID_OPERATION
: return "EXCEPTION_FLT_INVALID_OPERATION";
77 case EXCEPTION_FLT_OVERFLOW
: return "EXCEPTION_FLT_OVERFLOW";
78 case EXCEPTION_FLT_STACK_CHECK
: return "EXCEPTION_FLT_STACK_CHECK";
79 case EXCEPTION_FLT_UNDERFLOW
: return "EXCEPTION_FLT_UNDERFLOW";
80 case EXCEPTION_GUARD_PAGE
: return "EXCEPTION_GUARD_PAGE";
81 case EXCEPTION_ILLEGAL_INSTRUCTION
: return "EXCEPTION_ILLEGAL_INSTRUCTION";
82 case EXCEPTION_IN_PAGE_ERROR
: return "EXCEPTION_IN_PAGE_ERROR";
83 case EXCEPTION_INT_DIVIDE_BY_ZERO
: return "EXCEPTION_INT_DIVIDE_BY_ZERO";
84 case EXCEPTION_INT_OVERFLOW
: return "EXCEPTION_INT_OVERFLOW";
85 case EXCEPTION_INVALID_DISPOSITION
: return "EXCEPTION_INVALID_DISPOSITION";
86 case EXCEPTION_INVALID_HANDLE
: return "EXCEPTION_INVALID_HANDLE";
87 case EXCEPTION_NONCONTINUABLE_EXCEPTION
: return "EXCEPTION_NONCONTINUABLE_EXCEPTION";
88 case EXCEPTION_PRIV_INSTRUCTION
: return "EXCEPTION_PRIV_INSTRUCTION";
89 case EXCEPTION_SINGLE_STEP
: return "EXCEPTION_SINGLE_STEP";
90 case EXCEPTION_STACK_OVERFLOW
: return "EXCEPTION_STACK_OVERFLOW";
91 case EXCEPTION_WINE_ASSERTION
: return "EXCEPTION_WINE_ASSERTION";
92 case EXCEPTION_WINE_CXX_EXCEPTION
: return "EXCEPTION_WINE_CXX_EXCEPTION";
93 case EXCEPTION_WINE_NAME_THREAD
: return "EXCEPTION_WINE_NAME_THREAD";
94 case EXCEPTION_WINE_STUB
: return "EXCEPTION_WINE_STUB";
95 case RPC_S_SERVER_UNAVAILABLE
: return "RPC_S_SERVER_UNAVAILABLE";
101 static VECTORED_HANDLER
*add_vectored_handler( struct list
*handler_list
, ULONG first
,
102 PVECTORED_EXCEPTION_HANDLER func
)
104 VECTORED_HANDLER
*handler
= RtlAllocateHeap( GetProcessHeap(), 0, sizeof(*handler
) );
107 handler
->func
= RtlEncodePointer( func
);
109 RtlEnterCriticalSection( &vectored_handlers_section
);
110 if (first
) list_add_head( handler_list
, &handler
->entry
);
111 else list_add_tail( handler_list
, &handler
->entry
);
112 RtlLeaveCriticalSection( &vectored_handlers_section
);
118 static ULONG
remove_vectored_handler( struct list
*handler_list
, VECTORED_HANDLER
*handler
)
123 RtlEnterCriticalSection( &vectored_handlers_section
);
124 LIST_FOR_EACH( ptr
, handler_list
)
126 VECTORED_HANDLER
*curr_handler
= LIST_ENTRY( ptr
, VECTORED_HANDLER
, entry
);
127 if (curr_handler
== handler
)
129 if (!--curr_handler
->count
) list_remove( ptr
);
130 else handler
= NULL
; /* don't free it yet */
135 RtlLeaveCriticalSection( &vectored_handlers_section
);
136 if (ret
) RtlFreeHeap( GetProcessHeap(), 0, handler
);
141 /**********************************************************************
142 * call_vectored_handlers
144 * Call the vectored handlers chain.
146 LONG
call_vectored_handlers( EXCEPTION_RECORD
*rec
, CONTEXT
*context
)
149 LONG ret
= EXCEPTION_CONTINUE_SEARCH
;
150 EXCEPTION_POINTERS except_ptrs
;
151 PVECTORED_EXCEPTION_HANDLER func
;
152 VECTORED_HANDLER
*handler
, *to_free
= NULL
;
154 except_ptrs
.ExceptionRecord
= rec
;
155 except_ptrs
.ContextRecord
= context
;
157 RtlEnterCriticalSection( &vectored_handlers_section
);
158 ptr
= list_head( &vectored_exception_handlers
);
161 handler
= LIST_ENTRY( ptr
, VECTORED_HANDLER
, entry
);
163 func
= RtlDecodePointer( handler
->func
);
164 RtlLeaveCriticalSection( &vectored_handlers_section
);
165 RtlFreeHeap( GetProcessHeap(), 0, to_free
);
168 TRACE( "calling handler at %p code=%lx flags=%lx\n",
169 func
, rec
->ExceptionCode
, rec
->ExceptionFlags
);
170 ret
= func( &except_ptrs
);
171 TRACE( "handler at %p returned %lx\n", func
, ret
);
173 RtlEnterCriticalSection( &vectored_handlers_section
);
174 ptr
= list_next( &vectored_exception_handlers
, ptr
);
175 if (!--handler
->count
) /* removed during execution */
177 list_remove( &handler
->entry
);
180 if (ret
== EXCEPTION_CONTINUE_EXECUTION
) break;
182 RtlLeaveCriticalSection( &vectored_handlers_section
);
183 RtlFreeHeap( GetProcessHeap(), 0, to_free
);
188 /*******************************************************************
191 * Implementation of RtlRaiseStatus with a specific exception record.
193 void DECLSPEC_NORETURN
raise_status( NTSTATUS status
, EXCEPTION_RECORD
*rec
)
195 EXCEPTION_RECORD ExceptionRec
;
197 ExceptionRec
.ExceptionCode
= status
;
198 ExceptionRec
.ExceptionFlags
= EH_NONCONTINUABLE
;
199 ExceptionRec
.ExceptionRecord
= rec
;
200 ExceptionRec
.NumberParameters
= 0;
201 for (;;) RtlRaiseException( &ExceptionRec
); /* never returns */
205 /***********************************************************************
206 * RtlRaiseStatus (NTDLL.@)
208 * Raise an exception with ExceptionCode = status
210 void DECLSPEC_NORETURN WINAPI
RtlRaiseStatus( NTSTATUS status
)
212 raise_status( status
, NULL
);
216 /*******************************************************************
217 * KiRaiseUserExceptionDispatcher (NTDLL.@)
219 NTSTATUS WINAPI
KiRaiseUserExceptionDispatcher(void)
221 DWORD code
= NtCurrentTeb()->ExceptionCode
;
222 EXCEPTION_RECORD rec
= { code
};
223 RtlRaiseException( &rec
);
228 /*******************************************************************
229 * RtlAddVectoredContinueHandler (NTDLL.@)
231 PVOID WINAPI
RtlAddVectoredContinueHandler( ULONG first
, PVECTORED_EXCEPTION_HANDLER func
)
233 return add_vectored_handler( &vectored_continue_handlers
, first
, func
);
237 /*******************************************************************
238 * RtlRemoveVectoredContinueHandler (NTDLL.@)
240 ULONG WINAPI
RtlRemoveVectoredContinueHandler( PVOID handler
)
242 return remove_vectored_handler( &vectored_continue_handlers
, handler
);
246 /*******************************************************************
247 * RtlAddVectoredExceptionHandler (NTDLL.@)
249 PVOID WINAPI DECLSPEC_HOTPATCH
RtlAddVectoredExceptionHandler( ULONG first
, PVECTORED_EXCEPTION_HANDLER func
)
251 return add_vectored_handler( &vectored_exception_handlers
, first
, func
);
255 /*******************************************************************
256 * RtlRemoveVectoredExceptionHandler (NTDLL.@)
258 ULONG WINAPI
RtlRemoveVectoredExceptionHandler( PVOID handler
)
260 return remove_vectored_handler( &vectored_exception_handlers
, handler
);
264 /*******************************************************************
265 * RtlSetUnhandledExceptionFilter (NTDLL.@)
267 void WINAPI
RtlSetUnhandledExceptionFilter( PRTL_EXCEPTION_FILTER filter
)
269 unhandled_exception_filter
= filter
;
273 /*******************************************************************
274 * call_unhandled_exception_filter
276 LONG WINAPI
call_unhandled_exception_filter( PEXCEPTION_POINTERS eptr
)
278 if (!unhandled_exception_filter
) return EXCEPTION_CONTINUE_SEARCH
;
279 return unhandled_exception_filter( eptr
);
283 #if defined(__x86_64__) || defined(__arm__) || defined(__aarch64__)
285 struct dynamic_unwind_entry
290 RUNTIME_FUNCTION
*table
;
293 PGET_RUNTIME_FUNCTION_CALLBACK callback
;
297 static struct list dynamic_unwind_list
= LIST_INIT(dynamic_unwind_list
);
299 static RTL_CRITICAL_SECTION dynamic_unwind_section
;
300 static RTL_CRITICAL_SECTION_DEBUG dynamic_unwind_debug
=
302 0, 0, &dynamic_unwind_section
,
303 { &dynamic_unwind_debug
.ProcessLocksList
, &dynamic_unwind_debug
.ProcessLocksList
},
304 0, 0, { (DWORD_PTR
)(__FILE__
": dynamic_unwind_section") }
306 static RTL_CRITICAL_SECTION dynamic_unwind_section
= { &dynamic_unwind_debug
, -1, 0, 0, 0, 0 };
308 static ULONG_PTR
get_runtime_function_end( RUNTIME_FUNCTION
*func
, ULONG_PTR addr
)
311 return func
->EndAddress
;
312 #elif defined(__arm__)
313 if (func
->Flag
) return func
->BeginAddress
+ func
->FunctionLength
* 2;
318 DWORD function_length
: 18;
325 } *info
= (struct unwind_info
*)(addr
+ func
->UnwindData
);
326 return func
->BeginAddress
+ info
->function_length
* 2;
328 #else /* __aarch64__ */
329 if (func
->Flag
) return func
->BeginAddress
+ func
->FunctionLength
* 4;
334 DWORD function_length
: 18;
340 } *info
= (struct unwind_info
*)(addr
+ func
->UnwindData
);
341 return func
->BeginAddress
+ info
->function_length
* 4;
346 /**********************************************************************
347 * RtlAddFunctionTable (NTDLL.@)
349 BOOLEAN CDECL
RtlAddFunctionTable( RUNTIME_FUNCTION
*table
, DWORD count
, ULONG_PTR addr
)
351 struct dynamic_unwind_entry
*entry
;
353 TRACE( "%p %lu %Ix\n", table
, count
, addr
);
355 /* NOTE: Windows doesn't check if table is aligned or a NULL pointer */
357 entry
= RtlAllocateHeap( GetProcessHeap(), 0, sizeof(*entry
) );
362 entry
->end
= addr
+ (count
? get_runtime_function_end( &table
[count
- 1], addr
) : 0);
363 entry
->table
= table
;
364 entry
->count
= count
;
365 entry
->max_count
= 0;
366 entry
->callback
= NULL
;
367 entry
->context
= NULL
;
369 RtlEnterCriticalSection( &dynamic_unwind_section
);
370 list_add_tail( &dynamic_unwind_list
, &entry
->entry
);
371 RtlLeaveCriticalSection( &dynamic_unwind_section
);
376 /**********************************************************************
377 * RtlInstallFunctionTableCallback (NTDLL.@)
379 BOOLEAN CDECL
RtlInstallFunctionTableCallback( ULONG_PTR table
, ULONG_PTR base
, DWORD length
,
380 PGET_RUNTIME_FUNCTION_CALLBACK callback
, PVOID context
,
383 struct dynamic_unwind_entry
*entry
;
385 TRACE( "%Ix %Ix %ld %p %p %s\n", table
, base
, length
, callback
, context
, wine_dbgstr_w(dll
) );
387 /* NOTE: Windows doesn't check if the provided callback is a NULL pointer */
389 /* both low-order bits must be set */
390 if ((table
& 0x3) != 0x3)
393 entry
= RtlAllocateHeap( GetProcessHeap(), 0, sizeof(*entry
) );
398 entry
->end
= base
+ length
;
399 entry
->table
= (RUNTIME_FUNCTION
*)table
;
401 entry
->max_count
= 0;
402 entry
->callback
= callback
;
403 entry
->context
= context
;
405 RtlEnterCriticalSection( &dynamic_unwind_section
);
406 list_add_tail( &dynamic_unwind_list
, &entry
->entry
);
407 RtlLeaveCriticalSection( &dynamic_unwind_section
);
413 /*************************************************************************
414 * RtlAddGrowableFunctionTable (NTDLL.@)
416 DWORD WINAPI
RtlAddGrowableFunctionTable( void **table
, RUNTIME_FUNCTION
*functions
, DWORD count
,
417 DWORD max_count
, ULONG_PTR base
, ULONG_PTR end
)
419 struct dynamic_unwind_entry
*entry
;
421 TRACE( "%p, %p, %lu, %lu, %Ix, %Ix\n", table
, functions
, count
, max_count
, base
, end
);
423 entry
= RtlAllocateHeap( GetProcessHeap(), 0, sizeof(*entry
) );
425 return STATUS_NO_MEMORY
;
429 entry
->table
= functions
;
430 entry
->count
= count
;
431 entry
->max_count
= max_count
;
432 entry
->callback
= NULL
;
433 entry
->context
= NULL
;
435 RtlEnterCriticalSection( &dynamic_unwind_section
);
436 list_add_tail( &dynamic_unwind_list
, &entry
->entry
);
437 RtlLeaveCriticalSection( &dynamic_unwind_section
);
441 return STATUS_SUCCESS
;
445 /*************************************************************************
446 * RtlGrowFunctionTable (NTDLL.@)
448 void WINAPI
RtlGrowFunctionTable( void *table
, DWORD count
)
450 struct dynamic_unwind_entry
*entry
;
452 TRACE( "%p, %lu\n", table
, count
);
454 RtlEnterCriticalSection( &dynamic_unwind_section
);
455 LIST_FOR_EACH_ENTRY( entry
, &dynamic_unwind_list
, struct dynamic_unwind_entry
, entry
)
459 if (count
> entry
->count
&& count
<= entry
->max_count
)
460 entry
->count
= count
;
464 RtlLeaveCriticalSection( &dynamic_unwind_section
);
468 /*************************************************************************
469 * RtlDeleteGrowableFunctionTable (NTDLL.@)
471 void WINAPI
RtlDeleteGrowableFunctionTable( void *table
)
473 struct dynamic_unwind_entry
*entry
, *to_free
= NULL
;
475 TRACE( "%p\n", table
);
477 RtlEnterCriticalSection( &dynamic_unwind_section
);
478 LIST_FOR_EACH_ENTRY( entry
, &dynamic_unwind_list
, struct dynamic_unwind_entry
, entry
)
483 list_remove( &entry
->entry
);
487 RtlLeaveCriticalSection( &dynamic_unwind_section
);
489 RtlFreeHeap( GetProcessHeap(), 0, to_free
);
493 /**********************************************************************
494 * RtlDeleteFunctionTable (NTDLL.@)
496 BOOLEAN CDECL
RtlDeleteFunctionTable( RUNTIME_FUNCTION
*table
)
498 struct dynamic_unwind_entry
*entry
, *to_free
= NULL
;
500 TRACE( "%p\n", table
);
502 RtlEnterCriticalSection( &dynamic_unwind_section
);
503 LIST_FOR_EACH_ENTRY( entry
, &dynamic_unwind_list
, struct dynamic_unwind_entry
, entry
)
505 if (entry
->table
== table
)
508 list_remove( &entry
->entry
);
512 RtlLeaveCriticalSection( &dynamic_unwind_section
);
514 if (!to_free
) return FALSE
;
516 RtlFreeHeap( GetProcessHeap(), 0, to_free
);
521 /* helper for lookup_function_info() */
522 static RUNTIME_FUNCTION
*find_function_info( ULONG_PTR pc
, ULONG_PTR base
,
523 RUNTIME_FUNCTION
*func
, ULONG size
)
531 int pos
= (min
+ max
) / 2;
532 if (pc
< base
+ func
[pos
].BeginAddress
) max
= pos
- 1;
533 else if (pc
>= base
+ func
[pos
].EndAddress
) min
= pos
+ 1;
537 while (func
->UnwindData
& 1) /* follow chained entry */
538 func
= (RUNTIME_FUNCTION
*)(base
+ (func
->UnwindData
& ~1));
541 #elif defined(__arm__)
542 int pos
= (min
+ max
) / 2;
543 if (pc
< base
+ (func
[pos
].BeginAddress
& ~1)) max
= pos
- 1;
544 else if (pc
>= base
+ get_runtime_function_end( &func
[pos
], base
)) min
= pos
+ 1;
545 else return func
+ pos
;
546 #else /* __aarch64__ */
547 int pos
= (min
+ max
) / 2;
548 if (pc
< base
+ func
[pos
].BeginAddress
) max
= pos
- 1;
549 else if (pc
>= base
+ get_runtime_function_end( &func
[pos
], base
)) min
= pos
+ 1;
550 else return func
+ pos
;
556 /**********************************************************************
557 * lookup_function_info
559 RUNTIME_FUNCTION
*lookup_function_info( ULONG_PTR pc
, ULONG_PTR
*base
, LDR_DATA_TABLE_ENTRY
**module
)
561 RUNTIME_FUNCTION
*func
= NULL
;
562 struct dynamic_unwind_entry
*entry
;
565 /* PE module or wine module */
566 if (!LdrFindEntryForAddress( (void *)pc
, module
))
568 *base
= (ULONG_PTR
)(*module
)->DllBase
;
569 if ((func
= RtlImageDirectoryEntryToData( (*module
)->DllBase
, TRUE
,
570 IMAGE_DIRECTORY_ENTRY_EXCEPTION
, &size
)))
572 /* lookup in function table */
573 func
= find_function_info( pc
, (ULONG_PTR
)(*module
)->DllBase
, func
, size
/sizeof(*func
) );
580 RtlEnterCriticalSection( &dynamic_unwind_section
);
581 LIST_FOR_EACH_ENTRY( entry
, &dynamic_unwind_list
, struct dynamic_unwind_entry
, entry
)
583 if (pc
>= entry
->base
&& pc
< entry
->end
)
586 /* use callback or lookup in function table */
588 func
= entry
->callback( pc
, entry
->context
);
590 func
= find_function_info( pc
, entry
->base
, entry
->table
, entry
->count
);
594 RtlLeaveCriticalSection( &dynamic_unwind_section
);
600 /**********************************************************************
601 * RtlLookupFunctionEntry (NTDLL.@)
603 PRUNTIME_FUNCTION WINAPI
RtlLookupFunctionEntry( ULONG_PTR pc
, ULONG_PTR
*base
,
604 UNWIND_HISTORY_TABLE
*table
)
606 LDR_DATA_TABLE_ENTRY
*module
;
607 RUNTIME_FUNCTION
*func
;
609 /* FIXME: should use the history table to make things faster */
611 if (!(func
= lookup_function_info( pc
, base
, &module
)))
614 WARN( "no exception table found for %Ix\n", pc
);
619 #endif /* __x86_64__ || __arm__ || __aarch64__ */
622 /*************************************************************
625 void DECLSPEC_NORETURN __cdecl
_assert( const char *str
, const char *file
, unsigned int line
)
627 ERR( "%s:%u: Assertion failed %s\n", file
, line
, debugstr_a(str
) );
628 RtlRaiseStatus( EXCEPTION_WINE_ASSERTION
);
632 /*************************************************************
633 * __wine_spec_unimplemented_stub
635 * ntdll-specific implementation to avoid depending on kernel functions.
636 * Can be removed once ntdll.spec no longer contains stubs.
638 void __cdecl
__wine_spec_unimplemented_stub( const char *module
, const char *function
)
640 EXCEPTION_RECORD record
;
642 record
.ExceptionCode
= EXCEPTION_WINE_STUB
;
643 record
.ExceptionFlags
= EH_NONCONTINUABLE
;
644 record
.ExceptionRecord
= NULL
;
645 record
.ExceptionAddress
= __wine_spec_unimplemented_stub
;
646 record
.NumberParameters
= 2;
647 record
.ExceptionInformation
[0] = (ULONG_PTR
)module
;
648 record
.ExceptionInformation
[1] = (ULONG_PTR
)function
;
649 for (;;) RtlRaiseException( &record
);
653 /*************************************************************
656 * IsBadStringPtrA replacement for ntdll, to catch exception in debug traces.
658 BOOL WINAPI
IsBadStringPtrA( LPCSTR str
, UINT_PTR max
)
660 if (!str
) return TRUE
;
663 volatile const char *p
= str
;
664 while (p
!= str
+ max
) if (!*p
++) break;
674 /*************************************************************
677 * IsBadStringPtrW replacement for ntdll, to catch exception in debug traces.
679 BOOL WINAPI
IsBadStringPtrW( LPCWSTR str
, UINT_PTR max
)
681 if (!str
) return TRUE
;
684 volatile const WCHAR
*p
= str
;
685 while (p
!= str
+ max
) if (!*p
++) break;
696 __ASM_STDCALL_IMPORT(IsBadStringPtrA
,8)
697 __ASM_STDCALL_IMPORT(IsBadStringPtrW
,8)
699 __ASM_GLOBAL_IMPORT(IsBadStringPtrA
)
700 __ASM_GLOBAL_IMPORT(IsBadStringPtrW
)
703 /**********************************************************************
704 * RtlGetEnabledExtendedFeatures (NTDLL.@)
706 ULONG64 WINAPI
RtlGetEnabledExtendedFeatures(ULONG64 feature_mask
)
708 return user_shared_data
->XState
.EnabledFeatures
& feature_mask
;
711 struct context_copy_range
717 static const struct context_copy_range copy_ranges_amd64
[] =
719 {0x38, 0x1}, {0x3a, 0x4}, { 0x42, 0x1}, { 0x48, 0x10}, { 0x78, 0x2}, { 0x98, 0x1},
720 {0xa0, 0x2}, {0xf8, 0x1}, {0x100, 0x8}, {0x2a0, 0}, {0x4b0, 0x10}, {0x4d0, 0}
723 static const struct context_copy_range copy_ranges_x86
[] =
725 { 0x4, 0x10}, {0x1c, 0x8}, {0x8c, 0x4}, {0x9c, 0x2}, {0xb4, 0x1}, {0xcc, 0x20}, {0x1ec, 0},
729 static const struct context_parameters
732 ULONG supported_flags
;
733 ULONG context_size
; /* sizeof(CONTEXT) */
734 ULONG legacy_size
; /* Legacy context size */
735 ULONG context_ex_size
; /* sizeof(CONTEXT_EX) */
736 ULONG alignment
; /* Used when computing size of context. */
737 ULONG true_alignment
; /* Used for actual alignment. */
739 const struct context_copy_range
*copy_ranges
;
741 arch_context_parameters
[] =
745 0xd8000000 | CONTEXT_AMD64_ALL
| CONTEXT_AMD64_XSTATE
,
746 sizeof(AMD64_CONTEXT
),
747 sizeof(AMD64_CONTEXT
),
750 TYPE_ALIGNMENT(AMD64_CONTEXT
) - 1,
751 offsetof(AMD64_CONTEXT
,ContextFlags
),
756 0xd8000000 | CONTEXT_I386_ALL
| CONTEXT_I386_XSTATE
,
757 sizeof(I386_CONTEXT
),
758 offsetof(I386_CONTEXT
,ExtendedRegisters
),
761 TYPE_ALIGNMENT(I386_CONTEXT
) - 1,
762 offsetof(I386_CONTEXT
,ContextFlags
),
767 static const struct context_parameters
*context_get_parameters( ULONG context_flags
)
771 for (i
= 0; i
< ARRAY_SIZE(arch_context_parameters
); ++i
)
773 if (context_flags
& arch_context_parameters
[i
].arch_flag
)
774 return context_flags
& ~arch_context_parameters
[i
].supported_flags
? NULL
: &arch_context_parameters
[i
];
780 /**********************************************************************
781 * RtlGetExtendedContextLength2 (NTDLL.@)
783 NTSTATUS WINAPI
RtlGetExtendedContextLength2( ULONG context_flags
, ULONG
*length
, ULONG64 compaction_mask
)
785 const struct context_parameters
*p
;
786 ULONG64 supported_mask
;
789 TRACE( "context_flags %#lx, length %p, compaction_mask %s.\n", context_flags
, length
,
790 wine_dbgstr_longlong(compaction_mask
) );
792 if (!(p
= context_get_parameters( context_flags
)))
793 return STATUS_INVALID_PARAMETER
;
795 if (!(context_flags
& 0x40))
797 *length
= p
->context_size
+ p
->context_ex_size
+ p
->alignment
;
798 return STATUS_SUCCESS
;
801 if (!(supported_mask
= RtlGetEnabledExtendedFeatures( ~(ULONG64
)0) ))
802 return STATUS_NOT_SUPPORTED
;
804 compaction_mask
&= supported_mask
;
806 size
= p
->context_size
+ p
->context_ex_size
+ offsetof(XSTATE
, YmmContext
) + 63;
808 if (compaction_mask
& supported_mask
& (1 << XSTATE_AVX
))
809 size
+= sizeof(YMMCONTEXT
);
812 return STATUS_SUCCESS
;
816 /**********************************************************************
817 * RtlGetExtendedContextLength (NTDLL.@)
819 NTSTATUS WINAPI
RtlGetExtendedContextLength( ULONG context_flags
, ULONG
*length
)
821 return RtlGetExtendedContextLength2( context_flags
, length
, ~(ULONG64
)0 );
825 /**********************************************************************
826 * RtlInitializeExtendedContext2 (NTDLL.@)
828 NTSTATUS WINAPI
RtlInitializeExtendedContext2( void *context
, ULONG context_flags
, CONTEXT_EX
**context_ex
,
829 ULONG64 compaction_mask
)
831 const struct context_parameters
*p
;
832 ULONG64 supported_mask
= 0;
835 TRACE( "context %p, context_flags %#lx, context_ex %p, compaction_mask %s.\n",
836 context
, context_flags
, context_ex
, wine_dbgstr_longlong(compaction_mask
));
838 if (!(p
= context_get_parameters( context_flags
)))
839 return STATUS_INVALID_PARAMETER
;
841 if ((context_flags
& 0x40) && !(supported_mask
= RtlGetEnabledExtendedFeatures( ~(ULONG64
)0 )))
842 return STATUS_NOT_SUPPORTED
;
844 context
= (void *)(((ULONG_PTR
)context
+ p
->true_alignment
) & ~(ULONG_PTR
)p
->true_alignment
);
845 *(ULONG
*)((BYTE
*)context
+ p
->flags_offset
) = context_flags
;
847 *context_ex
= c_ex
= (CONTEXT_EX
*)((BYTE
*)context
+ p
->context_size
);
848 c_ex
->Legacy
.Offset
= c_ex
->All
.Offset
= -(LONG
)p
->context_size
;
849 c_ex
->Legacy
.Length
= context_flags
& 0x20 ? p
->context_size
: p
->legacy_size
;
851 if (context_flags
& 0x40)
855 compaction_mask
&= supported_mask
;
857 xs
= (XSTATE
*)(((ULONG_PTR
)c_ex
+ p
->context_ex_size
+ 63) & ~(ULONG_PTR
)63);
859 c_ex
->XState
.Offset
= (ULONG_PTR
)xs
- (ULONG_PTR
)c_ex
;
860 c_ex
->XState
.Length
= offsetof(XSTATE
, YmmContext
);
861 compaction_mask
&= supported_mask
;
863 if (compaction_mask
& (1 << XSTATE_AVX
))
864 c_ex
->XState
.Length
+= sizeof(YMMCONTEXT
);
866 memset( xs
, 0, c_ex
->XState
.Length
);
867 if (user_shared_data
->XState
.CompactionEnabled
)
868 xs
->CompactionMask
= ((ULONG64
)1 << 63) | compaction_mask
;
870 c_ex
->All
.Length
= p
->context_size
+ c_ex
->XState
.Offset
+ c_ex
->XState
.Length
;
874 c_ex
->XState
.Offset
= 25; /* According to the tests, it is just 25 if CONTEXT_XSTATE is not specified. */
875 c_ex
->XState
.Length
= 0;
876 c_ex
->All
.Length
= p
->context_size
+ 24; /* sizeof(CONTEXT_EX) minus 8 alignment bytes on x64. */
879 return STATUS_SUCCESS
;
883 /**********************************************************************
884 * RtlInitializeExtendedContext (NTDLL.@)
886 NTSTATUS WINAPI
RtlInitializeExtendedContext( void *context
, ULONG context_flags
, CONTEXT_EX
**context_ex
)
888 return RtlInitializeExtendedContext2( context
, context_flags
, context_ex
, ~(ULONG64
)0 );
892 /**********************************************************************
893 * RtlLocateExtendedFeature2 (NTDLL.@)
895 void * WINAPI
RtlLocateExtendedFeature2( CONTEXT_EX
*context_ex
, ULONG feature_id
,
896 XSTATE_CONFIGURATION
*xstate_config
, ULONG
*length
)
898 TRACE( "context_ex %p, feature_id %lu, xstate_config %p, length %p.\n",
899 context_ex
, feature_id
, xstate_config
, length
);
903 FIXME( "NULL xstate_config.\n" );
907 if (xstate_config
!= &user_shared_data
->XState
)
909 FIXME( "Custom xstate configuration is not supported.\n" );
913 if (feature_id
!= XSTATE_AVX
)
917 *length
= sizeof(YMMCONTEXT
);
919 if (context_ex
->XState
.Length
< sizeof(XSTATE
))
922 return (BYTE
*)context_ex
+ context_ex
->XState
.Offset
+ offsetof(XSTATE
, YmmContext
);
926 /**********************************************************************
927 * RtlLocateExtendedFeature (NTDLL.@)
929 void * WINAPI
RtlLocateExtendedFeature( CONTEXT_EX
*context_ex
, ULONG feature_id
,
932 return RtlLocateExtendedFeature2( context_ex
, feature_id
, &user_shared_data
->XState
, length
);
935 /**********************************************************************
936 * RtlLocateLegacyContext (NTDLL.@)
938 void * WINAPI
RtlLocateLegacyContext( CONTEXT_EX
*context_ex
, ULONG
*length
)
941 *length
= context_ex
->Legacy
.Length
;
943 return (BYTE
*)context_ex
+ context_ex
->Legacy
.Offset
;
946 /**********************************************************************
947 * RtlSetExtendedFeaturesMask (NTDLL.@)
949 void WINAPI
RtlSetExtendedFeaturesMask( CONTEXT_EX
*context_ex
, ULONG64 feature_mask
)
951 XSTATE
*xs
= (XSTATE
*)((BYTE
*)context_ex
+ context_ex
->XState
.Offset
);
953 xs
->Mask
= RtlGetEnabledExtendedFeatures( feature_mask
) & ~(ULONG64
)3;
957 /**********************************************************************
958 * RtlGetExtendedFeaturesMask (NTDLL.@)
960 ULONG64 WINAPI
RtlGetExtendedFeaturesMask( CONTEXT_EX
*context_ex
)
962 XSTATE
*xs
= (XSTATE
*)((BYTE
*)context_ex
+ context_ex
->XState
.Offset
);
964 return xs
->Mask
& ~(ULONG64
)3;
968 static void context_copy_ranges( BYTE
*d
, DWORD context_flags
, BYTE
*s
, const struct context_parameters
*p
)
970 const struct context_copy_range
*range
;
973 *((ULONG
*)(d
+ p
->flags_offset
)) |= context_flags
;
976 range
= p
->copy_ranges
;
979 if (range
->flag
& context_flags
)
982 start
= range
->start
;
986 memcpy( d
+ start
, s
+ start
, range
->start
- start
);
990 while (range
++->start
!= p
->context_size
);
994 /***********************************************************************
995 * RtlCopyContext (NTDLL.@)
997 NTSTATUS WINAPI
RtlCopyContext( CONTEXT
*dst
, DWORD context_flags
, CONTEXT
*src
)
999 DWORD context_size
, arch_flag
, flags_offset
, dst_flags
, src_flags
;
1000 static const DWORD arch_mask
= CONTEXT_i386
| CONTEXT_AMD64
;
1001 const struct context_parameters
*p
;
1004 TRACE("dst %p, context_flags %#lx, src %p.\n", dst
, context_flags
, src
);
1006 if (context_flags
& 0x40 && !RtlGetEnabledExtendedFeatures( ~(ULONG64
)0 )) return STATUS_NOT_SUPPORTED
;
1008 arch_flag
= context_flags
& arch_mask
;
1012 context_size
= sizeof( I386_CONTEXT
);
1013 flags_offset
= offsetof( I386_CONTEXT
, ContextFlags
);
1016 context_size
= sizeof( AMD64_CONTEXT
);
1017 flags_offset
= offsetof( AMD64_CONTEXT
, ContextFlags
);
1020 return STATUS_INVALID_PARAMETER
;
1025 dst_flags
= *(DWORD
*)(d
+ flags_offset
);
1026 src_flags
= *(DWORD
*)(s
+ flags_offset
);
1028 if ((dst_flags
& arch_mask
) != arch_flag
|| (src_flags
& arch_mask
) != arch_flag
)
1029 return STATUS_INVALID_PARAMETER
;
1031 context_flags
&= src_flags
;
1032 if (context_flags
& ~dst_flags
& 0x40) return STATUS_BUFFER_OVERFLOW
;
1034 if (context_flags
& 0x40)
1035 return RtlCopyExtendedContext( (CONTEXT_EX
*)(d
+ context_size
), context_flags
,
1036 (CONTEXT_EX
*)(s
+ context_size
) );
1038 if (!(p
= context_get_parameters( context_flags
)))
1039 return STATUS_INVALID_PARAMETER
;
1041 context_copy_ranges( d
, context_flags
, s
, p
);
1042 return STATUS_SUCCESS
;
1046 /**********************************************************************
1047 * RtlCopyExtendedContext (NTDLL.@)
1049 NTSTATUS WINAPI
RtlCopyExtendedContext( CONTEXT_EX
*dst
, ULONG context_flags
, CONTEXT_EX
*src
)
1051 const struct context_parameters
*p
;
1052 XSTATE
*dst_xs
, *src_xs
;
1053 ULONG64 feature_mask
;
1055 TRACE( "dst %p, context_flags %#lx, src %p.\n", dst
, context_flags
, src
);
1057 if (!(p
= context_get_parameters( context_flags
)))
1058 return STATUS_INVALID_PARAMETER
;
1060 if (!(feature_mask
= RtlGetEnabledExtendedFeatures( ~(ULONG64
)0 )) && context_flags
& 0x40)
1061 return STATUS_NOT_SUPPORTED
;
1063 context_copy_ranges( RtlLocateLegacyContext( dst
, NULL
), context_flags
, RtlLocateLegacyContext( src
, NULL
), p
);
1065 if (!(context_flags
& 0x40))
1066 return STATUS_SUCCESS
;
1068 if (dst
->XState
.Length
< offsetof(XSTATE
, YmmContext
))
1069 return STATUS_BUFFER_OVERFLOW
;
1071 dst_xs
= (XSTATE
*)((BYTE
*)dst
+ dst
->XState
.Offset
);
1072 src_xs
= (XSTATE
*)((BYTE
*)src
+ src
->XState
.Offset
);
1074 memset(dst_xs
, 0, offsetof(XSTATE
, YmmContext
));
1075 dst_xs
->Mask
= (src_xs
->Mask
& ~(ULONG64
)3) & feature_mask
;
1076 dst_xs
->CompactionMask
= user_shared_data
->XState
.CompactionEnabled
1077 ? ((ULONG64
)1 << 63) | (src_xs
->CompactionMask
& feature_mask
) : 0;
1079 if (dst_xs
->Mask
& 4 && src
->XState
.Length
>= sizeof(XSTATE
) && dst
->XState
.Length
>= sizeof(XSTATE
))
1080 memcpy( &dst_xs
->YmmContext
, &src_xs
->YmmContext
, sizeof(dst_xs
->YmmContext
) );
1081 return STATUS_SUCCESS
;