ntdll: Reimplement DbgPrint* using DBG_PRINTEXCEPTION_C.
[wine.git] / dlls / ntdll / exception.c
bloba6b4b6a7baafa0e91796d0c9342307b38acace71
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 <assert.h>
23 #include <errno.h>
24 #include <signal.h>
25 #include <stdarg.h>
27 #define NONAMELESSUNION
28 #define NONAMELESSSTRUCT
29 #include "ntstatus.h"
30 #define WIN32_NO_STATUS
31 #include "windef.h"
32 #include "winternl.h"
33 #include "ddk/wdm.h"
34 #include "wine/exception.h"
35 #include "wine/server.h"
36 #include "wine/list.h"
37 #include "wine/debug.h"
38 #include "excpt.h"
39 #include "ntdll_misc.h"
41 WINE_DEFAULT_DEBUG_CHANNEL(seh);
43 typedef struct
45 struct list entry;
46 PVECTORED_EXCEPTION_HANDLER func;
47 ULONG count;
48 } VECTORED_HANDLER;
50 static struct list vectored_exception_handlers = LIST_INIT(vectored_exception_handlers);
51 static struct list vectored_continue_handlers = LIST_INIT(vectored_continue_handlers);
53 static RTL_CRITICAL_SECTION vectored_handlers_section;
54 static RTL_CRITICAL_SECTION_DEBUG critsect_debug =
56 0, 0, &vectored_handlers_section,
57 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
58 0, 0, { (DWORD_PTR)(__FILE__ ": vectored_handlers_section") }
60 static RTL_CRITICAL_SECTION vectored_handlers_section = { &critsect_debug, -1, 0, 0, 0, 0 };
62 static PRTL_EXCEPTION_FILTER unhandled_exception_filter;
65 static VECTORED_HANDLER *add_vectored_handler( struct list *handler_list, ULONG first,
66 PVECTORED_EXCEPTION_HANDLER func )
68 VECTORED_HANDLER *handler = RtlAllocateHeap( GetProcessHeap(), 0, sizeof(*handler) );
69 if (handler)
71 handler->func = RtlEncodePointer( func );
72 handler->count = 1;
73 RtlEnterCriticalSection( &vectored_handlers_section );
74 if (first) list_add_head( handler_list, &handler->entry );
75 else list_add_tail( handler_list, &handler->entry );
76 RtlLeaveCriticalSection( &vectored_handlers_section );
78 return handler;
82 static ULONG remove_vectored_handler( struct list *handler_list, VECTORED_HANDLER *handler )
84 struct list *ptr;
85 ULONG ret = FALSE;
87 RtlEnterCriticalSection( &vectored_handlers_section );
88 LIST_FOR_EACH( ptr, handler_list )
90 VECTORED_HANDLER *curr_handler = LIST_ENTRY( ptr, VECTORED_HANDLER, entry );
91 if (curr_handler == handler)
93 if (!--curr_handler->count) list_remove( ptr );
94 else handler = NULL; /* don't free it yet */
95 ret = TRUE;
96 break;
99 RtlLeaveCriticalSection( &vectored_handlers_section );
100 if (ret) RtlFreeHeap( GetProcessHeap(), 0, handler );
101 return ret;
105 /**********************************************************************
106 * call_vectored_handlers
108 * Call the vectored handlers chain.
110 LONG call_vectored_handlers( EXCEPTION_RECORD *rec, CONTEXT *context )
112 struct list *ptr;
113 LONG ret = EXCEPTION_CONTINUE_SEARCH;
114 EXCEPTION_POINTERS except_ptrs;
115 PVECTORED_EXCEPTION_HANDLER func;
116 VECTORED_HANDLER *handler, *to_free = NULL;
118 except_ptrs.ExceptionRecord = rec;
119 except_ptrs.ContextRecord = context;
121 RtlEnterCriticalSection( &vectored_handlers_section );
122 ptr = list_head( &vectored_exception_handlers );
123 while (ptr)
125 handler = LIST_ENTRY( ptr, VECTORED_HANDLER, entry );
126 handler->count++;
127 func = RtlDecodePointer( handler->func );
128 RtlLeaveCriticalSection( &vectored_handlers_section );
129 RtlFreeHeap( GetProcessHeap(), 0, to_free );
130 to_free = NULL;
132 TRACE( "calling handler at %p code=%x flags=%x\n",
133 func, rec->ExceptionCode, rec->ExceptionFlags );
134 ret = func( &except_ptrs );
135 TRACE( "handler at %p returned %x\n", func, ret );
137 RtlEnterCriticalSection( &vectored_handlers_section );
138 ptr = list_next( &vectored_exception_handlers, ptr );
139 if (!--handler->count) /* removed during execution */
141 list_remove( &handler->entry );
142 to_free = handler;
144 if (ret == EXCEPTION_CONTINUE_EXECUTION) break;
146 RtlLeaveCriticalSection( &vectored_handlers_section );
147 RtlFreeHeap( GetProcessHeap(), 0, to_free );
148 return ret;
152 /*******************************************************************
153 * raise_status
155 * Implementation of RtlRaiseStatus with a specific exception record.
157 void raise_status( NTSTATUS status, EXCEPTION_RECORD *rec )
159 EXCEPTION_RECORD ExceptionRec;
161 ExceptionRec.ExceptionCode = status;
162 ExceptionRec.ExceptionFlags = EH_NONCONTINUABLE;
163 ExceptionRec.ExceptionRecord = rec;
164 ExceptionRec.NumberParameters = 0;
165 for (;;) RtlRaiseException( &ExceptionRec ); /* never returns */
169 /***********************************************************************
170 * RtlRaiseStatus (NTDLL.@)
172 * Raise an exception with ExceptionCode = status
174 void WINAPI RtlRaiseStatus( NTSTATUS status )
176 raise_status( status, NULL );
180 /*******************************************************************
181 * KiRaiseUserExceptionDispatcher (NTDLL.@)
183 NTSTATUS WINAPI KiRaiseUserExceptionDispatcher(void)
185 DWORD code = NtCurrentTeb()->ExceptionCode;
186 EXCEPTION_RECORD rec = { code };
187 RtlRaiseException( &rec );
188 return code;
192 /*******************************************************************
193 * RtlAddVectoredContinueHandler (NTDLL.@)
195 PVOID WINAPI RtlAddVectoredContinueHandler( ULONG first, PVECTORED_EXCEPTION_HANDLER func )
197 return add_vectored_handler( &vectored_continue_handlers, first, func );
201 /*******************************************************************
202 * RtlRemoveVectoredContinueHandler (NTDLL.@)
204 ULONG WINAPI RtlRemoveVectoredContinueHandler( PVOID handler )
206 return remove_vectored_handler( &vectored_continue_handlers, handler );
210 /*******************************************************************
211 * RtlAddVectoredExceptionHandler (NTDLL.@)
213 PVOID WINAPI DECLSPEC_HOTPATCH RtlAddVectoredExceptionHandler( ULONG first, PVECTORED_EXCEPTION_HANDLER func )
215 return add_vectored_handler( &vectored_exception_handlers, first, func );
219 /*******************************************************************
220 * RtlRemoveVectoredExceptionHandler (NTDLL.@)
222 ULONG WINAPI RtlRemoveVectoredExceptionHandler( PVOID handler )
224 return remove_vectored_handler( &vectored_exception_handlers, handler );
228 /*******************************************************************
229 * RtlSetUnhandledExceptionFilter (NTDLL.@)
231 void WINAPI RtlSetUnhandledExceptionFilter( PRTL_EXCEPTION_FILTER filter )
233 unhandled_exception_filter = filter;
237 /*******************************************************************
238 * call_unhandled_exception_filter
240 LONG WINAPI call_unhandled_exception_filter( PEXCEPTION_POINTERS eptr )
242 if (!unhandled_exception_filter) return EXCEPTION_CONTINUE_SEARCH;
243 return unhandled_exception_filter( eptr );
247 #if defined(__x86_64__) || defined(__arm__) || defined(__aarch64__)
249 struct dynamic_unwind_entry
251 struct list entry;
252 ULONG_PTR base;
253 ULONG_PTR end;
254 RUNTIME_FUNCTION *table;
255 DWORD count;
256 DWORD max_count;
257 PGET_RUNTIME_FUNCTION_CALLBACK callback;
258 PVOID context;
261 static struct list dynamic_unwind_list = LIST_INIT(dynamic_unwind_list);
263 static RTL_CRITICAL_SECTION dynamic_unwind_section;
264 static RTL_CRITICAL_SECTION_DEBUG dynamic_unwind_debug =
266 0, 0, &dynamic_unwind_section,
267 { &dynamic_unwind_debug.ProcessLocksList, &dynamic_unwind_debug.ProcessLocksList },
268 0, 0, { (DWORD_PTR)(__FILE__ ": dynamic_unwind_section") }
270 static RTL_CRITICAL_SECTION dynamic_unwind_section = { &dynamic_unwind_debug, -1, 0, 0, 0, 0 };
272 static ULONG_PTR get_runtime_function_end( RUNTIME_FUNCTION *func, ULONG_PTR addr )
274 #ifdef __x86_64__
275 return func->EndAddress;
276 #elif defined(__arm__)
277 if (func->u.s.Flag) return func->BeginAddress + func->u.s.FunctionLength * 2;
278 else
280 struct unwind_info
282 DWORD function_length : 18;
283 DWORD version : 2;
284 DWORD x : 1;
285 DWORD e : 1;
286 DWORD f : 1;
287 DWORD count : 5;
288 DWORD words : 4;
289 } *info = (struct unwind_info *)(addr + func->u.UnwindData);
290 return func->BeginAddress + info->function_length * 2;
292 #else /* __aarch64__ */
293 if (func->u.s.Flag) return func->BeginAddress + func->u.s.FunctionLength * 4;
294 else
296 struct unwind_info
298 DWORD function_length : 18;
299 DWORD version : 2;
300 DWORD x : 1;
301 DWORD e : 1;
302 DWORD epilog : 5;
303 DWORD codes : 5;
304 } *info = (struct unwind_info *)(addr + func->u.UnwindData);
305 return func->BeginAddress + info->function_length * 4;
307 #endif
310 /**********************************************************************
311 * RtlAddFunctionTable (NTDLL.@)
313 BOOLEAN CDECL RtlAddFunctionTable( RUNTIME_FUNCTION *table, DWORD count, ULONG_PTR addr )
315 struct dynamic_unwind_entry *entry;
317 TRACE( "%p %u %lx\n", table, count, addr );
319 /* NOTE: Windows doesn't check if table is aligned or a NULL pointer */
321 entry = RtlAllocateHeap( GetProcessHeap(), 0, sizeof(*entry) );
322 if (!entry)
323 return FALSE;
325 entry->base = addr;
326 entry->end = addr + (count ? get_runtime_function_end( &table[count - 1], addr ) : 0);
327 entry->table = table;
328 entry->count = count;
329 entry->max_count = 0;
330 entry->callback = NULL;
331 entry->context = NULL;
333 RtlEnterCriticalSection( &dynamic_unwind_section );
334 list_add_tail( &dynamic_unwind_list, &entry->entry );
335 RtlLeaveCriticalSection( &dynamic_unwind_section );
336 return TRUE;
340 /**********************************************************************
341 * RtlInstallFunctionTableCallback (NTDLL.@)
343 BOOLEAN CDECL RtlInstallFunctionTableCallback( ULONG_PTR table, ULONG_PTR base, DWORD length,
344 PGET_RUNTIME_FUNCTION_CALLBACK callback, PVOID context,
345 PCWSTR dll )
347 struct dynamic_unwind_entry *entry;
349 TRACE( "%lx %lx %d %p %p %s\n", table, base, length, callback, context, wine_dbgstr_w(dll) );
351 /* NOTE: Windows doesn't check if the provided callback is a NULL pointer */
353 /* both low-order bits must be set */
354 if ((table & 0x3) != 0x3)
355 return FALSE;
357 entry = RtlAllocateHeap( GetProcessHeap(), 0, sizeof(*entry) );
358 if (!entry)
359 return FALSE;
361 entry->base = base;
362 entry->end = base + length;
363 entry->table = (RUNTIME_FUNCTION *)table;
364 entry->count = 0;
365 entry->max_count = 0;
366 entry->callback = callback;
367 entry->context = context;
369 RtlEnterCriticalSection( &dynamic_unwind_section );
370 list_add_tail( &dynamic_unwind_list, &entry->entry );
371 RtlLeaveCriticalSection( &dynamic_unwind_section );
373 return TRUE;
377 /*************************************************************************
378 * RtlAddGrowableFunctionTable (NTDLL.@)
380 DWORD WINAPI RtlAddGrowableFunctionTable( void **table, RUNTIME_FUNCTION *functions, DWORD count,
381 DWORD max_count, ULONG_PTR base, ULONG_PTR end )
383 struct dynamic_unwind_entry *entry;
385 TRACE( "%p, %p, %u, %u, %lx, %lx\n", table, functions, count, max_count, base, end );
387 entry = RtlAllocateHeap( GetProcessHeap(), 0, sizeof(*entry) );
388 if (!entry)
389 return STATUS_NO_MEMORY;
391 entry->base = base;
392 entry->end = end;
393 entry->table = functions;
394 entry->count = count;
395 entry->max_count = max_count;
396 entry->callback = NULL;
397 entry->context = NULL;
399 RtlEnterCriticalSection( &dynamic_unwind_section );
400 list_add_tail( &dynamic_unwind_list, &entry->entry );
401 RtlLeaveCriticalSection( &dynamic_unwind_section );
403 *table = entry;
405 return STATUS_SUCCESS;
409 /*************************************************************************
410 * RtlGrowFunctionTable (NTDLL.@)
412 void WINAPI RtlGrowFunctionTable( void *table, DWORD count )
414 struct dynamic_unwind_entry *entry;
416 TRACE( "%p, %u\n", table, count );
418 RtlEnterCriticalSection( &dynamic_unwind_section );
419 LIST_FOR_EACH_ENTRY( entry, &dynamic_unwind_list, struct dynamic_unwind_entry, entry )
421 if (entry == table)
423 if (count > entry->count && count <= entry->max_count)
424 entry->count = count;
425 break;
428 RtlLeaveCriticalSection( &dynamic_unwind_section );
432 /*************************************************************************
433 * RtlDeleteGrowableFunctionTable (NTDLL.@)
435 void WINAPI RtlDeleteGrowableFunctionTable( void *table )
437 struct dynamic_unwind_entry *entry, *to_free = NULL;
439 TRACE( "%p\n", table );
441 RtlEnterCriticalSection( &dynamic_unwind_section );
442 LIST_FOR_EACH_ENTRY( entry, &dynamic_unwind_list, struct dynamic_unwind_entry, entry )
444 if (entry == table)
446 to_free = entry;
447 list_remove( &entry->entry );
448 break;
451 RtlLeaveCriticalSection( &dynamic_unwind_section );
453 RtlFreeHeap( GetProcessHeap(), 0, to_free );
457 /**********************************************************************
458 * RtlDeleteFunctionTable (NTDLL.@)
460 BOOLEAN CDECL RtlDeleteFunctionTable( RUNTIME_FUNCTION *table )
462 struct dynamic_unwind_entry *entry, *to_free = NULL;
464 TRACE( "%p\n", table );
466 RtlEnterCriticalSection( &dynamic_unwind_section );
467 LIST_FOR_EACH_ENTRY( entry, &dynamic_unwind_list, struct dynamic_unwind_entry, entry )
469 if (entry->table == table)
471 to_free = entry;
472 list_remove( &entry->entry );
473 break;
476 RtlLeaveCriticalSection( &dynamic_unwind_section );
478 if (!to_free) return FALSE;
480 RtlFreeHeap( GetProcessHeap(), 0, to_free );
481 return TRUE;
485 /* helper for lookup_function_info() */
486 static RUNTIME_FUNCTION *find_function_info( ULONG_PTR pc, ULONG_PTR base,
487 RUNTIME_FUNCTION *func, ULONG size )
489 int min = 0;
490 int max = size - 1;
492 while (min <= max)
494 #ifdef __x86_64__
495 int pos = (min + max) / 2;
496 if (pc < base + func[pos].BeginAddress) max = pos - 1;
497 else if (pc >= base + func[pos].EndAddress) min = pos + 1;
498 else
500 func += pos;
501 while (func->UnwindData & 1) /* follow chained entry */
502 func = (RUNTIME_FUNCTION *)(base + (func->UnwindData & ~1));
503 return func;
505 #elif defined(__arm__)
506 int pos = (min + max) / 2;
507 if (pc < base + (func[pos].BeginAddress & ~1)) max = pos - 1;
508 else if (pc >= base + get_runtime_function_end( &func[pos], base )) min = pos + 1;
509 else return func + pos;
510 #else /* __aarch64__ */
511 int pos = (min + max) / 2;
512 if (pc < base + func[pos].BeginAddress) max = pos - 1;
513 else if (pc >= base + get_runtime_function_end( &func[pos], base )) min = pos + 1;
514 else return func + pos;
515 #endif
517 return NULL;
520 /**********************************************************************
521 * lookup_function_info
523 RUNTIME_FUNCTION *lookup_function_info( ULONG_PTR pc, ULONG_PTR *base, LDR_DATA_TABLE_ENTRY **module )
525 RUNTIME_FUNCTION *func = NULL;
526 struct dynamic_unwind_entry *entry;
527 ULONG size;
529 /* PE module or wine module */
530 if (!LdrFindEntryForAddress( (void *)pc, module ))
532 *base = (ULONG_PTR)(*module)->DllBase;
533 if ((func = RtlImageDirectoryEntryToData( (*module)->DllBase, TRUE,
534 IMAGE_DIRECTORY_ENTRY_EXCEPTION, &size )))
536 /* lookup in function table */
537 func = find_function_info( pc, (ULONG_PTR)(*module)->DllBase, func, size/sizeof(*func) );
540 else
542 *module = NULL;
544 RtlEnterCriticalSection( &dynamic_unwind_section );
545 LIST_FOR_EACH_ENTRY( entry, &dynamic_unwind_list, struct dynamic_unwind_entry, entry )
547 if (pc >= entry->base && pc < entry->end)
549 *base = entry->base;
550 /* use callback or lookup in function table */
551 if (entry->callback)
552 func = entry->callback( pc, entry->context );
553 else
554 func = find_function_info( pc, entry->base, entry->table, entry->count );
555 break;
558 RtlLeaveCriticalSection( &dynamic_unwind_section );
561 return func;
564 /**********************************************************************
565 * RtlLookupFunctionEntry (NTDLL.@)
567 PRUNTIME_FUNCTION WINAPI RtlLookupFunctionEntry( ULONG_PTR pc, ULONG_PTR *base,
568 UNWIND_HISTORY_TABLE *table )
570 LDR_DATA_TABLE_ENTRY *module;
571 RUNTIME_FUNCTION *func;
573 /* FIXME: should use the history table to make things faster */
575 if (!(func = lookup_function_info( pc, base, &module )))
577 *base = 0;
578 WARN( "no exception table found for %lx\n", pc );
580 return func;
583 #endif /* __x86_64__ || __arm__ || __aarch64__ */
586 /*************************************************************
587 * _assert
589 void __cdecl _assert( const char *str, const char *file, unsigned int line )
591 ERR( "%s:%u: Assertion failed %s\n", file, line, debugstr_a(str) );
592 RtlRaiseStatus( EXCEPTION_WINE_ASSERTION );
596 /*************************************************************
597 * __wine_spec_unimplemented_stub
599 * ntdll-specific implementation to avoid depending on kernel functions.
600 * Can be removed once ntdll.spec no longer contains stubs.
602 void __cdecl __wine_spec_unimplemented_stub( const char *module, const char *function )
604 EXCEPTION_RECORD record;
606 record.ExceptionCode = EXCEPTION_WINE_STUB;
607 record.ExceptionFlags = EH_NONCONTINUABLE;
608 record.ExceptionRecord = NULL;
609 record.ExceptionAddress = __wine_spec_unimplemented_stub;
610 record.NumberParameters = 2;
611 record.ExceptionInformation[0] = (ULONG_PTR)module;
612 record.ExceptionInformation[1] = (ULONG_PTR)function;
613 for (;;) RtlRaiseException( &record );
617 /*************************************************************
618 * IsBadStringPtrA
620 * IsBadStringPtrA replacement for ntdll, to catch exception in debug traces.
622 BOOL WINAPI IsBadStringPtrA( LPCSTR str, UINT_PTR max )
624 if (!str) return TRUE;
625 __TRY
627 volatile const char *p = str;
628 while (p != str + max) if (!*p++) break;
630 __EXCEPT_PAGE_FAULT
632 return TRUE;
634 __ENDTRY
635 return FALSE;
639 /*************************************************************
640 * IsBadStringPtrW
642 * IsBadStringPtrW replacement for ntdll, to catch exception in debug traces.
644 BOOL WINAPI IsBadStringPtrW( LPCWSTR str, UINT_PTR max )
646 if (!str) return TRUE;
647 __TRY
649 volatile const WCHAR *p = str;
650 while (p != str + max) if (!*p++) break;
652 __EXCEPT_PAGE_FAULT
654 return TRUE;
656 __ENDTRY
657 return FALSE;
661 /**********************************************************************
662 * RtlGetEnabledExtendedFeatures (NTDLL.@)
664 ULONG64 WINAPI RtlGetEnabledExtendedFeatures(ULONG64 feature_mask)
666 return user_shared_data->XState.EnabledFeatures & feature_mask;
669 struct context_copy_range
671 ULONG start;
672 ULONG flag;
675 static const struct context_copy_range copy_ranges_amd64[] =
677 {0x38, 0x1}, {0x3a, 0x4}, { 0x42, 0x1}, { 0x48, 0x10}, { 0x78, 0x2}, { 0x98, 0x1},
678 {0xa0, 0x2}, {0xf8, 0x1}, {0x100, 0x8}, {0x2a0, 0}, {0x4b0, 0x10}, {0x4d0, 0}
681 static const struct context_copy_range copy_ranges_x86[] =
683 { 0x4, 0x10}, {0x1c, 0x8}, {0x8c, 0x4}, {0x9c, 0x2}, {0xb4, 0x1}, {0xcc, 0x20}, {0x1ec, 0},
684 {0x2cc, 0},
687 static const struct context_parameters
689 ULONG arch_flag;
690 ULONG supported_flags;
691 ULONG context_size; /* sizeof(CONTEXT) */
692 ULONG legacy_size; /* Legacy context size */
693 ULONG context_ex_size; /* sizeof(CONTEXT_EX) */
694 ULONG alignment; /* Used when computing size of context. */
695 ULONG true_alignment; /* Used for actual alignment. */
696 ULONG flags_offset;
697 const struct context_copy_range *copy_ranges;
699 arch_context_parameters[] =
701 {0x00100000, 0xd810005f, 0x4d0, 0x4d0, 0x20, 7, 0xf, 0x30, copy_ranges_amd64},
702 {0x00010000, 0xd801007f, 0x2cc, 0xcc, 0x18, 3, 0x3, 0, copy_ranges_x86},
705 static const struct context_parameters *context_get_parameters( ULONG context_flags )
707 unsigned int i;
709 for (i = 0; i < ARRAY_SIZE(arch_context_parameters); ++i)
711 if (context_flags & arch_context_parameters[i].arch_flag)
712 return context_flags & ~arch_context_parameters[i].supported_flags ? NULL : &arch_context_parameters[i];
714 return NULL;
718 /**********************************************************************
719 * RtlGetExtendedContextLength2 (NTDLL.@)
721 NTSTATUS WINAPI RtlGetExtendedContextLength2( ULONG context_flags, ULONG *length, ULONG64 compaction_mask )
723 const struct context_parameters *p;
724 ULONG64 supported_mask;
725 ULONG64 size;
727 TRACE( "context_flags %#x, length %p, compaction_mask %s.\n", context_flags, length,
728 wine_dbgstr_longlong(compaction_mask) );
730 if (!(p = context_get_parameters( context_flags )))
731 return STATUS_INVALID_PARAMETER;
733 if (!(context_flags & 0x40))
735 *length = p->context_size + p->context_ex_size + p->alignment;
736 return STATUS_SUCCESS;
739 if (!(supported_mask = RtlGetEnabledExtendedFeatures( ~(ULONG64)0) ))
740 return STATUS_NOT_SUPPORTED;
742 compaction_mask &= supported_mask;
744 size = p->context_size + p->context_ex_size + offsetof(XSTATE, YmmContext) + 63;
746 if (compaction_mask & supported_mask & (1 << XSTATE_AVX))
747 size += sizeof(YMMCONTEXT);
749 *length = size;
750 return STATUS_SUCCESS;
754 /**********************************************************************
755 * RtlGetExtendedContextLength (NTDLL.@)
757 NTSTATUS WINAPI RtlGetExtendedContextLength( ULONG context_flags, ULONG *length )
759 return RtlGetExtendedContextLength2( context_flags, length, ~(ULONG64)0 );
763 /**********************************************************************
764 * RtlInitializeExtendedContext2 (NTDLL.@)
766 NTSTATUS WINAPI RtlInitializeExtendedContext2( void *context, ULONG context_flags, CONTEXT_EX **context_ex,
767 ULONG64 compaction_mask )
769 const struct context_parameters *p;
770 ULONG64 supported_mask = 0;
771 CONTEXT_EX *c_ex;
773 TRACE( "context %p, context_flags %#x, context_ex %p, compaction_mask %s.\n",
774 context, context_flags, context_ex, wine_dbgstr_longlong(compaction_mask));
776 if (!(p = context_get_parameters( context_flags )))
777 return STATUS_INVALID_PARAMETER;
779 if ((context_flags & 0x40) && !(supported_mask = RtlGetEnabledExtendedFeatures( ~(ULONG64)0 )))
780 return STATUS_NOT_SUPPORTED;
782 context = (void *)(((ULONG_PTR)context + p->true_alignment) & ~p->true_alignment);
783 *(ULONG *)((BYTE *)context + p->flags_offset) = context_flags;
785 *context_ex = c_ex = (CONTEXT_EX *)((BYTE *)context + p->context_size);
786 c_ex->Legacy.Offset = c_ex->All.Offset = -(LONG)p->context_size;
787 c_ex->Legacy.Length = context_flags & 0x20 ? p->context_size : p->legacy_size;
789 if (context_flags & 0x40)
791 XSTATE *xs;
793 compaction_mask &= supported_mask;
795 xs = (XSTATE *)(((ULONG_PTR)c_ex + p->context_ex_size + 63) & ~(ULONG_PTR)63);
797 c_ex->XState.Offset = (ULONG_PTR)xs - (ULONG_PTR)c_ex;
798 c_ex->XState.Length = offsetof(XSTATE, YmmContext);
799 compaction_mask &= supported_mask;
801 if (compaction_mask & (1 << XSTATE_AVX))
802 c_ex->XState.Length += sizeof(YMMCONTEXT);
804 memset( xs, 0, c_ex->XState.Length );
805 if (user_shared_data->XState.CompactionEnabled)
806 xs->CompactionMask = ((ULONG64)1 << 63) | compaction_mask;
808 c_ex->All.Length = p->context_size + c_ex->XState.Offset + c_ex->XState.Length;
810 else
812 c_ex->XState.Offset = 25; /* According to the tests, it is just 25 if CONTEXT_XSTATE is not specified. */
813 c_ex->XState.Length = 0;
814 c_ex->All.Length = p->context_size + 24; /* sizeof(CONTEXT_EX) minus 8 alignment bytes on x64. */
817 return STATUS_SUCCESS;
821 /**********************************************************************
822 * RtlInitializeExtendedContext (NTDLL.@)
824 NTSTATUS WINAPI RtlInitializeExtendedContext( void *context, ULONG context_flags, CONTEXT_EX **context_ex )
826 return RtlInitializeExtendedContext2( context, context_flags, context_ex, ~(ULONG64)0 );
830 /**********************************************************************
831 * RtlLocateExtendedFeature2 (NTDLL.@)
833 void * WINAPI RtlLocateExtendedFeature2( CONTEXT_EX *context_ex, ULONG feature_id,
834 XSTATE_CONFIGURATION *xstate_config, ULONG *length )
836 TRACE( "context_ex %p, feature_id %u, xstate_config %p, length %p.\n",
837 context_ex, feature_id, xstate_config, length );
839 if (!xstate_config)
841 FIXME( "NULL xstate_config.\n" );
842 return NULL;
845 if (xstate_config != &user_shared_data->XState)
847 FIXME( "Custom xstate configuration is not supported.\n" );
848 return NULL;
851 if (feature_id != XSTATE_AVX)
852 return NULL;
854 if (length)
855 *length = sizeof(YMMCONTEXT);
857 if (context_ex->XState.Length < sizeof(XSTATE))
858 return NULL;
860 return (BYTE *)context_ex + context_ex->XState.Offset + offsetof(XSTATE, YmmContext);
864 /**********************************************************************
865 * RtlLocateExtendedFeature (NTDLL.@)
867 void * WINAPI RtlLocateExtendedFeature( CONTEXT_EX *context_ex, ULONG feature_id,
868 ULONG *length )
870 return RtlLocateExtendedFeature2( context_ex, feature_id, &user_shared_data->XState, length );
873 /**********************************************************************
874 * RtlLocateLegacyContext (NTDLL.@)
876 void * WINAPI RtlLocateLegacyContext( CONTEXT_EX *context_ex, ULONG *length )
878 if (length)
879 *length = context_ex->Legacy.Length;
881 return (BYTE *)context_ex + context_ex->Legacy.Offset;
884 /**********************************************************************
885 * RtlSetExtendedFeaturesMask (NTDLL.@)
887 void WINAPI RtlSetExtendedFeaturesMask( CONTEXT_EX *context_ex, ULONG64 feature_mask )
889 XSTATE *xs = (XSTATE *)((BYTE *)context_ex + context_ex->XState.Offset);
891 xs->Mask = RtlGetEnabledExtendedFeatures( feature_mask ) & ~(ULONG64)3;
895 /**********************************************************************
896 * RtlGetExtendedFeaturesMask (NTDLL.@)
898 ULONG64 WINAPI RtlGetExtendedFeaturesMask( CONTEXT_EX *context_ex )
900 XSTATE *xs = (XSTATE *)((BYTE *)context_ex + context_ex->XState.Offset);
902 return xs->Mask & ~(ULONG64)3;
906 /**********************************************************************
907 * RtlCopyExtendedContext (NTDLL.@)
909 NTSTATUS WINAPI RtlCopyExtendedContext( CONTEXT_EX *dst, ULONG context_flags, CONTEXT_EX *src )
911 const struct context_copy_range *range;
912 const struct context_parameters *p;
913 XSTATE *dst_xs, *src_xs;
914 ULONG64 feature_mask;
915 unsigned int start;
916 BYTE *d, *s;
918 TRACE( "dst %p, context_flags %#x, src %p.\n", dst, context_flags, src );
920 if (!(p = context_get_parameters( context_flags )))
921 return STATUS_INVALID_PARAMETER;
923 if (!(feature_mask = RtlGetEnabledExtendedFeatures( ~(ULONG64)0 )) && context_flags & 0x40)
924 return STATUS_NOT_SUPPORTED;
926 d = RtlLocateLegacyContext( dst, NULL );
927 s = RtlLocateLegacyContext( src, NULL );
929 *((ULONG *)(d + p->flags_offset)) |= context_flags;
931 start = 0;
932 range = p->copy_ranges;
935 if (range->flag & context_flags)
937 if (!start)
938 start = range->start;
940 else if (start)
942 memcpy( d + start, s + start, range->start - start );
943 start = 0;
946 while (range++->start != p->context_size);
948 if (!(context_flags & 0x40))
949 return STATUS_SUCCESS;
951 if (dst->XState.Length < offsetof(XSTATE, YmmContext))
952 return STATUS_BUFFER_OVERFLOW;
954 dst_xs = (XSTATE *)((BYTE *)dst + dst->XState.Offset);
955 src_xs = (XSTATE *)((BYTE *)src + src->XState.Offset);
957 memset(dst_xs, 0, offsetof(XSTATE, YmmContext));
958 dst_xs->Mask = (src_xs->Mask & ~(ULONG64)3) & feature_mask;
959 dst_xs->CompactionMask = user_shared_data->XState.CompactionEnabled
960 ? ((ULONG64)1 << 63) | (src_xs->CompactionMask & feature_mask) : 0;
962 if (dst_xs->Mask & 4 && src->XState.Length >= sizeof(XSTATE) && dst->XState.Length >= sizeof(XSTATE))
963 memcpy( &dst_xs->YmmContext, &src_xs->YmmContext, sizeof(dst_xs->YmmContext) );
964 return STATUS_SUCCESS;