ntdll: Use nameless unions/structs for register contexts.
[wine.git] / dlls / ntdll / signal_x86_64.c
blob84f6935c5ce9cb8ac36d14b547ce6ccb56680cf4
1 /*
2 * x86-64 signal handling routines
4 * Copyright 1999, 2005 Alexandre Julliard
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #ifdef __x86_64__
23 #include <stdlib.h>
24 #include <stdarg.h>
26 #include "ntstatus.h"
27 #define WIN32_NO_STATUS
28 #include "windef.h"
29 #include "winternl.h"
30 #include "wine/exception.h"
31 #include "wine/list.h"
32 #include "ntdll_misc.h"
33 #include "wine/debug.h"
35 WINE_DEFAULT_DEBUG_CHANNEL(unwind);
36 WINE_DECLARE_DEBUG_CHANNEL(seh);
37 WINE_DECLARE_DEBUG_CHANNEL(threadname);
39 typedef struct _SCOPE_TABLE
41 ULONG Count;
42 struct
44 ULONG BeginAddress;
45 ULONG EndAddress;
46 ULONG HandlerAddress;
47 ULONG JumpTarget;
48 } ScopeRecord[1];
49 } SCOPE_TABLE, *PSCOPE_TABLE;
52 /* layering violation: the setjmp buffer is defined in msvcrt, but used by RtlUnwindEx */
53 struct MSVCRT_JUMP_BUFFER
55 ULONG64 Frame;
56 ULONG64 Rbx;
57 ULONG64 Rsp;
58 ULONG64 Rbp;
59 ULONG64 Rsi;
60 ULONG64 Rdi;
61 ULONG64 R12;
62 ULONG64 R13;
63 ULONG64 R14;
64 ULONG64 R15;
65 ULONG64 Rip;
66 ULONG MxCsr;
67 USHORT FpCsr;
68 USHORT Spare;
69 M128A Xmm6;
70 M128A Xmm7;
71 M128A Xmm8;
72 M128A Xmm9;
73 M128A Xmm10;
74 M128A Xmm11;
75 M128A Xmm12;
76 M128A Xmm13;
77 M128A Xmm14;
78 M128A Xmm15;
81 /***********************************************************************
82 * Definitions for Win32 unwind tables
85 union handler_data
87 RUNTIME_FUNCTION chain;
88 ULONG handler;
91 struct opcode
93 BYTE offset;
94 BYTE code : 4;
95 BYTE info : 4;
98 struct UNWIND_INFO
100 BYTE version : 3;
101 BYTE flags : 5;
102 BYTE prolog;
103 BYTE count;
104 BYTE frame_reg : 4;
105 BYTE frame_offset : 4;
106 struct opcode opcodes[1]; /* info->count entries */
107 /* followed by handler_data */
110 #define UWOP_PUSH_NONVOL 0
111 #define UWOP_ALLOC_LARGE 1
112 #define UWOP_ALLOC_SMALL 2
113 #define UWOP_SET_FPREG 3
114 #define UWOP_SAVE_NONVOL 4
115 #define UWOP_SAVE_NONVOL_FAR 5
116 #define UWOP_EPILOG 6
117 #define UWOP_SAVE_XMM128 8
118 #define UWOP_SAVE_XMM128_FAR 9
119 #define UWOP_PUSH_MACHFRAME 10
121 static void dump_unwind_info( ULONG64 base, RUNTIME_FUNCTION *function )
123 static const char * const reg_names[16] =
124 { "rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi",
125 "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15" };
127 union handler_data *handler_data;
128 struct UNWIND_INFO *info;
129 unsigned int i, count;
131 TRACE( "**** func %lx-%lx\n", function->BeginAddress, function->EndAddress );
132 for (;;)
134 if (function->UnwindData & 1)
136 RUNTIME_FUNCTION *next = (RUNTIME_FUNCTION *)((char *)base + (function->UnwindData & ~1));
137 TRACE( "unwind info for function %p-%p chained to function %p-%p\n",
138 (char *)base + function->BeginAddress, (char *)base + function->EndAddress,
139 (char *)base + next->BeginAddress, (char *)base + next->EndAddress );
140 function = next;
141 continue;
143 info = (struct UNWIND_INFO *)((char *)base + function->UnwindData);
145 TRACE( "unwind info at %p flags %x prolog 0x%x bytes function %p-%p\n",
146 info, info->flags, info->prolog,
147 (char *)base + function->BeginAddress, (char *)base + function->EndAddress );
149 if (info->frame_reg)
150 TRACE( " frame register %s offset 0x%x(%%rsp)\n",
151 reg_names[info->frame_reg], info->frame_offset * 16 );
153 for (i = 0; i < info->count; i++)
155 TRACE( " 0x%x: ", info->opcodes[i].offset );
156 switch (info->opcodes[i].code)
158 case UWOP_PUSH_NONVOL:
159 TRACE( "pushq %%%s\n", reg_names[info->opcodes[i].info] );
160 break;
161 case UWOP_ALLOC_LARGE:
162 if (info->opcodes[i].info)
164 count = *(DWORD *)&info->opcodes[i+1];
165 i += 2;
167 else
169 count = *(USHORT *)&info->opcodes[i+1] * 8;
170 i++;
172 TRACE( "subq $0x%x,%%rsp\n", count );
173 break;
174 case UWOP_ALLOC_SMALL:
175 count = (info->opcodes[i].info + 1) * 8;
176 TRACE( "subq $0x%x,%%rsp\n", count );
177 break;
178 case UWOP_SET_FPREG:
179 TRACE( "leaq 0x%x(%%rsp),%s\n",
180 info->frame_offset * 16, reg_names[info->frame_reg] );
181 break;
182 case UWOP_SAVE_NONVOL:
183 count = *(USHORT *)&info->opcodes[i+1] * 8;
184 TRACE( "movq %%%s,0x%x(%%rsp)\n", reg_names[info->opcodes[i].info], count );
185 i++;
186 break;
187 case UWOP_SAVE_NONVOL_FAR:
188 count = *(DWORD *)&info->opcodes[i+1];
189 TRACE( "movq %%%s,0x%x(%%rsp)\n", reg_names[info->opcodes[i].info], count );
190 i += 2;
191 break;
192 case UWOP_SAVE_XMM128:
193 count = *(USHORT *)&info->opcodes[i+1] * 16;
194 TRACE( "movaps %%xmm%u,0x%x(%%rsp)\n", info->opcodes[i].info, count );
195 i++;
196 break;
197 case UWOP_SAVE_XMM128_FAR:
198 count = *(DWORD *)&info->opcodes[i+1];
199 TRACE( "movaps %%xmm%u,0x%x(%%rsp)\n", info->opcodes[i].info, count );
200 i += 2;
201 break;
202 case UWOP_PUSH_MACHFRAME:
203 TRACE( "PUSH_MACHFRAME %u\n", info->opcodes[i].info );
204 break;
205 case UWOP_EPILOG:
206 if (info->version == 2)
208 unsigned int offset;
209 if (info->opcodes[i].info)
210 offset = info->opcodes[i].offset;
211 else
212 offset = (info->opcodes[i+1].info << 8) + info->opcodes[i+1].offset;
213 TRACE("epilog %p-%p\n", (char *)base + function->EndAddress - offset,
214 (char *)base + function->EndAddress - offset + info->opcodes[i].offset );
215 i += 1;
216 break;
218 default:
219 FIXME( "unknown code %u\n", info->opcodes[i].code );
220 break;
224 handler_data = (union handler_data *)&info->opcodes[(info->count + 1) & ~1];
225 if (info->flags & UNW_FLAG_CHAININFO)
227 TRACE( " chained to function %p-%p\n",
228 (char *)base + handler_data->chain.BeginAddress,
229 (char *)base + handler_data->chain.EndAddress );
230 function = &handler_data->chain;
231 continue;
233 if (info->flags & (UNW_FLAG_EHANDLER | UNW_FLAG_UHANDLER))
234 TRACE( " handler %p data at %p\n",
235 (char *)base + handler_data->handler, &handler_data->handler + 1 );
236 break;
240 static void dump_scope_table( ULONG64 base, const SCOPE_TABLE *table )
242 unsigned int i;
244 TRACE( "scope table at %p\n", table );
245 for (i = 0; i < table->Count; i++)
246 TRACE( " %u: %p-%p handler %p target %p\n", i,
247 (char *)base + table->ScopeRecord[i].BeginAddress,
248 (char *)base + table->ScopeRecord[i].EndAddress,
249 (char *)base + table->ScopeRecord[i].HandlerAddress,
250 (char *)base + table->ScopeRecord[i].JumpTarget );
254 /***********************************************************************
255 * virtual_unwind
257 static NTSTATUS virtual_unwind( ULONG type, DISPATCHER_CONTEXT *dispatch, CONTEXT *context )
259 LDR_DATA_TABLE_ENTRY *module;
260 NTSTATUS status;
262 dispatch->ImageBase = 0;
263 dispatch->ScopeIndex = 0;
264 dispatch->ControlPc = context->Rip;
266 /* first look for PE exception information */
268 if ((dispatch->FunctionEntry = lookup_function_info( context->Rip, &dispatch->ImageBase, &module )))
270 dispatch->LanguageHandler = RtlVirtualUnwind( type, dispatch->ImageBase, context->Rip,
271 dispatch->FunctionEntry, context,
272 &dispatch->HandlerData, &dispatch->EstablisherFrame,
273 NULL );
274 return STATUS_SUCCESS;
277 /* then look for host system exception information */
279 if (!module || (module->Flags & LDR_WINE_INTERNAL))
281 struct unwind_builtin_dll_params params = { type, dispatch, context };
283 status = WINE_UNIX_CALL( unix_unwind_builtin_dll, &params );
284 if (!status && dispatch->LanguageHandler && !module)
286 FIXME( "calling personality routine in system library not supported yet\n" );
287 dispatch->LanguageHandler = NULL;
289 if (status != STATUS_UNSUCCESSFUL) return status;
291 else WARN( "exception data not found in %s\n", debugstr_w(module->BaseDllName.Buffer) );
293 /* no exception information, treat as a leaf function */
295 dispatch->EstablisherFrame = context->Rsp;
296 dispatch->LanguageHandler = NULL;
297 context->Rip = *(ULONG64 *)context->Rsp;
298 context->Rsp = context->Rsp + sizeof(ULONG64);
299 return STATUS_SUCCESS;
303 /**************************************************************************
304 * __chkstk (NTDLL.@)
306 * Supposed to touch all the stack pages, but we shouldn't need that.
308 __ASM_GLOBAL_FUNC( __chkstk, "ret" );
311 /***********************************************************************
312 * RtlCaptureContext (NTDLL.@)
314 __ASM_GLOBAL_FUNC( RtlCaptureContext,
315 "pushfq\n\t"
316 __ASM_CFI(".cfi_adjust_cfa_offset 8\n\t")
317 "movl $0x10000f,0x30(%rcx)\n\t" /* context->ContextFlags */
318 "stmxcsr 0x34(%rcx)\n\t" /* context->MxCsr */
319 "movw %cs,0x38(%rcx)\n\t" /* context->SegCs */
320 "movw %ds,0x3a(%rcx)\n\t" /* context->SegDs */
321 "movw %es,0x3c(%rcx)\n\t" /* context->SegEs */
322 "movw %fs,0x3e(%rcx)\n\t" /* context->SegFs */
323 "movw %gs,0x40(%rcx)\n\t" /* context->SegGs */
324 "movw %ss,0x42(%rcx)\n\t" /* context->SegSs */
325 "popq 0x44(%rcx)\n\t" /* context->Eflags */
326 __ASM_CFI(".cfi_adjust_cfa_offset -8\n\t")
327 "movq %rax,0x78(%rcx)\n\t" /* context->Rax */
328 "movq %rcx,0x80(%rcx)\n\t" /* context->Rcx */
329 "movq %rdx,0x88(%rcx)\n\t" /* context->Rdx */
330 "movq %rbx,0x90(%rcx)\n\t" /* context->Rbx */
331 "leaq 8(%rsp),%rax\n\t"
332 "movq %rax,0x98(%rcx)\n\t" /* context->Rsp */
333 "movq %rbp,0xa0(%rcx)\n\t" /* context->Rbp */
334 "movq %rsi,0xa8(%rcx)\n\t" /* context->Rsi */
335 "movq %rdi,0xb0(%rcx)\n\t" /* context->Rdi */
336 "movq %r8,0xb8(%rcx)\n\t" /* context->R8 */
337 "movq %r9,0xc0(%rcx)\n\t" /* context->R9 */
338 "movq %r10,0xc8(%rcx)\n\t" /* context->R10 */
339 "movq %r11,0xd0(%rcx)\n\t" /* context->R11 */
340 "movq %r12,0xd8(%rcx)\n\t" /* context->R12 */
341 "movq %r13,0xe0(%rcx)\n\t" /* context->R13 */
342 "movq %r14,0xe8(%rcx)\n\t" /* context->R14 */
343 "movq %r15,0xf0(%rcx)\n\t" /* context->R15 */
344 "movq (%rsp),%rax\n\t"
345 "movq %rax,0xf8(%rcx)\n\t" /* context->Rip */
346 "fxsave 0x100(%rcx)\n\t" /* context->FltSave */
347 "ret" );
349 static DWORD __cdecl nested_exception_handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
350 CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher )
352 if (!(rec->ExceptionFlags & (EH_UNWINDING | EH_EXIT_UNWIND)))
353 rec->ExceptionFlags |= EH_NESTED_CALL;
355 return ExceptionContinueSearch;
358 /**********************************************************************
359 * call_handler
361 * Call a single exception handler.
362 * FIXME: Handle nested exceptions.
364 static DWORD call_handler( EXCEPTION_RECORD *rec, CONTEXT *context, DISPATCHER_CONTEXT *dispatch )
366 EXCEPTION_REGISTRATION_RECORD frame;
367 DWORD res;
369 frame.Handler = nested_exception_handler;
370 __wine_push_frame( &frame );
372 TRACE_(seh)( "calling handler %p (rec=%p, frame=%p context=%p, dispatch=%p)\n",
373 dispatch->LanguageHandler, rec, (void *)dispatch->EstablisherFrame, dispatch->ContextRecord, dispatch );
374 res = dispatch->LanguageHandler( rec, (void *)dispatch->EstablisherFrame, context, dispatch );
375 TRACE_(seh)( "handler at %p returned %lu\n", dispatch->LanguageHandler, res );
377 rec->ExceptionFlags &= EH_NONCONTINUABLE;
378 __wine_pop_frame( &frame );
379 return res;
383 /**********************************************************************
384 * call_teb_handler
386 * Call a single exception handler from the TEB chain.
387 * FIXME: Handle nested exceptions.
389 static DWORD call_teb_handler( EXCEPTION_RECORD *rec, CONTEXT *context, DISPATCHER_CONTEXT *dispatch,
390 EXCEPTION_REGISTRATION_RECORD *teb_frame )
392 DWORD res;
394 TRACE_(seh)( "calling TEB handler %p (rec=%p, frame=%p context=%p, dispatch=%p)\n",
395 teb_frame->Handler, rec, teb_frame, dispatch->ContextRecord, dispatch );
396 res = teb_frame->Handler( rec, teb_frame, context, (EXCEPTION_REGISTRATION_RECORD**)dispatch );
397 TRACE_(seh)( "handler at %p returned %lu\n", teb_frame->Handler, res );
398 return res;
402 /**********************************************************************
403 * call_stack_handlers
405 * Call the stack handlers chain.
407 static NTSTATUS call_stack_handlers( EXCEPTION_RECORD *rec, CONTEXT *orig_context )
409 EXCEPTION_REGISTRATION_RECORD *teb_frame = NtCurrentTeb()->Tib.ExceptionList;
410 UNWIND_HISTORY_TABLE table;
411 DISPATCHER_CONTEXT dispatch;
412 CONTEXT context;
413 NTSTATUS status;
415 context = *orig_context;
416 context.ContextFlags &= ~0x40; /* Clear xstate flag. */
418 dispatch.TargetIp = 0;
419 dispatch.ContextRecord = &context;
420 dispatch.HistoryTable = &table;
421 for (;;)
423 status = virtual_unwind( UNW_FLAG_EHANDLER, &dispatch, &context );
424 if (status != STATUS_SUCCESS) return status;
426 unwind_done:
427 if (!dispatch.EstablisherFrame) break;
429 if ((dispatch.EstablisherFrame & 7) ||
430 dispatch.EstablisherFrame < (ULONG64)NtCurrentTeb()->Tib.StackLimit ||
431 dispatch.EstablisherFrame > (ULONG64)NtCurrentTeb()->Tib.StackBase)
433 ERR_(seh)( "invalid frame %p (%p-%p)\n", (void *)dispatch.EstablisherFrame,
434 NtCurrentTeb()->Tib.StackLimit, NtCurrentTeb()->Tib.StackBase );
435 rec->ExceptionFlags |= EH_STACK_INVALID;
436 break;
439 if (dispatch.LanguageHandler)
441 switch (call_handler( rec, orig_context, &dispatch ))
443 case ExceptionContinueExecution:
444 if (rec->ExceptionFlags & EH_NONCONTINUABLE) return STATUS_NONCONTINUABLE_EXCEPTION;
445 return STATUS_SUCCESS;
446 case ExceptionContinueSearch:
447 break;
448 case ExceptionNestedException:
449 FIXME_(seh)( "nested exception\n" );
450 break;
451 case ExceptionCollidedUnwind: {
452 ULONG64 frame;
454 context = *dispatch.ContextRecord;
455 dispatch.ContextRecord = &context;
456 RtlVirtualUnwind( UNW_FLAG_NHANDLER, dispatch.ImageBase,
457 dispatch.ControlPc, dispatch.FunctionEntry,
458 &context, NULL, &frame, NULL );
459 goto unwind_done;
461 default:
462 return STATUS_INVALID_DISPOSITION;
465 /* hack: call wine handlers registered in the tib list */
466 else while ((ULONG64)teb_frame < context.Rsp)
468 TRACE_(seh)( "found wine frame %p rsp %p handler %p\n",
469 teb_frame, (void *)context.Rsp, teb_frame->Handler );
470 dispatch.EstablisherFrame = (ULONG64)teb_frame;
471 switch (call_teb_handler( rec, orig_context, &dispatch, teb_frame ))
473 case ExceptionContinueExecution:
474 if (rec->ExceptionFlags & EH_NONCONTINUABLE) return STATUS_NONCONTINUABLE_EXCEPTION;
475 return STATUS_SUCCESS;
476 case ExceptionContinueSearch:
477 break;
478 case ExceptionNestedException:
479 FIXME_(seh)( "nested exception\n" );
480 break;
481 case ExceptionCollidedUnwind: {
482 ULONG64 frame;
484 context = *dispatch.ContextRecord;
485 dispatch.ContextRecord = &context;
486 RtlVirtualUnwind( UNW_FLAG_NHANDLER, dispatch.ImageBase,
487 dispatch.ControlPc, dispatch.FunctionEntry,
488 &context, NULL, &frame, NULL );
489 teb_frame = teb_frame->Prev;
490 goto unwind_done;
492 default:
493 return STATUS_INVALID_DISPOSITION;
495 teb_frame = teb_frame->Prev;
498 if (context.Rsp == (ULONG64)NtCurrentTeb()->Tib.StackBase) break;
500 return STATUS_UNHANDLED_EXCEPTION;
504 NTSTATUS WINAPI dispatch_exception( EXCEPTION_RECORD *rec, CONTEXT *context )
506 NTSTATUS status;
507 DWORD c;
509 if (pWow64PrepareForException) pWow64PrepareForException( rec, context );
511 TRACE_(seh)( "code=%lx flags=%lx addr=%p ip=%Ix\n",
512 rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress, context->Rip );
513 for (c = 0; c < min( EXCEPTION_MAXIMUM_PARAMETERS, rec->NumberParameters ); c++)
514 TRACE_(seh)( " info[%ld]=%016I64x\n", c, rec->ExceptionInformation[c] );
516 if (rec->ExceptionCode == EXCEPTION_WINE_STUB)
518 if (rec->ExceptionInformation[1] >> 16)
519 MESSAGE( "wine: Call from %p to unimplemented function %s.%s, aborting\n",
520 rec->ExceptionAddress,
521 (char*)rec->ExceptionInformation[0], (char*)rec->ExceptionInformation[1] );
522 else
523 MESSAGE( "wine: Call from %p to unimplemented function %s.%I64d, aborting\n",
524 rec->ExceptionAddress,
525 (char*)rec->ExceptionInformation[0], rec->ExceptionInformation[1] );
527 else if (rec->ExceptionCode == EXCEPTION_WINE_NAME_THREAD && rec->ExceptionInformation[0] == 0x1000)
529 if ((DWORD)rec->ExceptionInformation[2] == -1 || (DWORD)rec->ExceptionInformation[2] == GetCurrentThreadId())
530 WARN_(threadname)( "Thread renamed to %s\n", debugstr_a((char *)rec->ExceptionInformation[1]) );
531 else
532 WARN_(threadname)( "Thread ID %04lx renamed to %s\n", (DWORD)rec->ExceptionInformation[2],
533 debugstr_a((char *)rec->ExceptionInformation[1]) );
535 set_native_thread_name((DWORD)rec->ExceptionInformation[2], (char *)rec->ExceptionInformation[1]);
537 else if (rec->ExceptionCode == DBG_PRINTEXCEPTION_C)
539 WARN_(seh)( "%s\n", debugstr_an((char *)rec->ExceptionInformation[1], rec->ExceptionInformation[0] - 1) );
541 else if (rec->ExceptionCode == DBG_PRINTEXCEPTION_WIDE_C)
543 WARN_(seh)( "%s\n", debugstr_wn((WCHAR *)rec->ExceptionInformation[1], rec->ExceptionInformation[0] - 1) );
545 else
547 if (rec->ExceptionCode == STATUS_ASSERTION_FAILURE)
548 ERR_(seh)( "%s exception (code=%lx) raised\n", debugstr_exception_code(rec->ExceptionCode), rec->ExceptionCode );
549 else
550 WARN_(seh)( "%s exception (code=%lx) raised\n", debugstr_exception_code(rec->ExceptionCode), rec->ExceptionCode );
552 TRACE_(seh)( " rax=%016I64x rbx=%016I64x rcx=%016I64x rdx=%016I64x\n",
553 context->Rax, context->Rbx, context->Rcx, context->Rdx );
554 TRACE_(seh)( " rsi=%016I64x rdi=%016I64x rbp=%016I64x rsp=%016I64x\n",
555 context->Rsi, context->Rdi, context->Rbp, context->Rsp );
556 TRACE_(seh)( " r8=%016I64x r9=%016I64x r10=%016I64x r11=%016I64x\n",
557 context->R8, context->R9, context->R10, context->R11 );
558 TRACE_(seh)( " r12=%016I64x r13=%016I64x r14=%016I64x r15=%016I64x\n",
559 context->R12, context->R13, context->R14, context->R15 );
562 /* Legends of Runeterra depends on having SegDs == SegSs in an exception
563 * handler. */
564 context->SegDs = context->SegSs;
566 if (call_vectored_handlers( rec, context ) == EXCEPTION_CONTINUE_EXECUTION)
567 NtContinue( context, FALSE );
569 if ((status = call_stack_handlers( rec, context )) == STATUS_SUCCESS)
570 NtContinue( context, FALSE );
572 if (status != STATUS_UNHANDLED_EXCEPTION) RtlRaiseStatus( status );
573 return NtRaiseException( rec, context, FALSE );
577 NTSTATUS WINAPI dispatch_wow_exception( EXCEPTION_RECORD *rec_ptr, CONTEXT *context_ptr )
579 char buffer[sizeof(CONTEXT) + sizeof(CONTEXT_EX) + sizeof(XSTATE) + 128];
580 CONTEXT *context;
581 CONTEXT_EX *context_ex;
582 EXCEPTION_RECORD rec = *rec_ptr;
584 RtlInitializeExtendedContext( buffer, context_ptr->ContextFlags, &context_ex );
585 context = RtlLocateLegacyContext( context_ex, NULL );
586 RtlCopyContext( context, context_ptr->ContextFlags, context_ptr );
587 return dispatch_exception( &rec, context );
591 /*******************************************************************
592 * KiUserExceptionDispatcher (NTDLL.@)
594 __ASM_GLOBAL_FUNC( KiUserExceptionDispatcher,
595 "mov 0x98(%rsp),%rcx\n\t" /* context->Rsp */
596 "movw %cs,%ax\n\t"
597 "cmpw %ax,0x38(%rsp)\n\t" /* context->SegCs */
598 "je 1f\n\t"
599 "mov %rsp,%rdx\n\t" /* context */
600 "lea 0x4f0(%rsp),%rcx\n\t" /* rec */
601 "movq %r14,%rsp\n\t" /* switch to 64-bit stack */
602 "call " __ASM_NAME("dispatch_wow_exception") "\n\t"
603 "int3\n"
604 "1:\tmov 0xf8(%rsp),%rdx\n\t" /* context->Rip */
605 "mov %rdx,-0x8(%rcx)\n\t"
606 "mov %rbp,-0x10(%rcx)\n\t"
607 "mov %rdi,-0x18(%rcx)\n\t"
608 "mov %rsi,-0x20(%rcx)\n\t"
609 "lea -0x20(%rcx),%rbp\n\t"
610 "mov %rsp,%rdx\n\t" /* context */
611 "lea 0x4f0(%rsp),%rcx\n\t" /* rec */
612 __ASM_SEH(".seh_pushreg %rbp\n\t")
613 __ASM_SEH(".seh_pushreg %rdi\n\t")
614 __ASM_SEH(".seh_pushreg %rsi\n\t")
615 __ASM_SEH(".seh_setframe %rbp,0\n\t")
616 __ASM_SEH(".seh_endprologue\n\t")
618 __ASM_CFI(".cfi_signal_frame\n\t")
619 __ASM_CFI(".cfi_adjust_cfa_offset 0x20\n\t")
620 __ASM_CFI(".cfi_def_cfa %rbp,0x20\n\t")
621 __ASM_CFI(".cfi_rel_offset %rip,0x18\n\t")
622 __ASM_CFI(".cfi_rel_offset %rbp,0x10\n\t")
623 __ASM_CFI(".cfi_rel_offset %rdi,0x8\n\t")
624 __ASM_CFI(".cfi_rel_offset %rsi,0\n\t")
625 "call " __ASM_NAME("dispatch_exception") "\n\t"
626 "int3")
629 /*******************************************************************
630 * KiUserApcDispatcher (NTDLL.@)
632 void WINAPI dispatch_apc( CONTEXT *context, ULONG_PTR arg1, ULONG_PTR arg2, ULONG_PTR arg3,
633 void (CALLBACK *func)(ULONG_PTR,ULONG_PTR,ULONG_PTR,CONTEXT*) )
635 func( arg1, arg2, arg3, context );
636 NtContinue( context, TRUE );
639 __ASM_GLOBAL_FUNC( KiUserApcDispatcher,
640 "addq $0x8,%rsp\n\t"
641 "mov 0x98(%rcx),%r10\n\t" /* context->Rsp */
642 "mov 0xf8(%rcx),%r11\n\t" /* context->Rip */
643 "mov %r11,-0x8(%r10)\n\t"
644 "mov %rbp,-0x10(%r10)\n\t"
645 "lea -0x10(%r10),%rbp\n\t"
646 __ASM_SEH(".seh_pushreg %rbp\n\t")
647 __ASM_SEH(".seh_setframe %rbp,0\n\t")
648 __ASM_SEH(".seh_endprologue\n\t")
649 __ASM_CFI(".cfi_signal_frame\n\t")
650 __ASM_CFI(".cfi_adjust_cfa_offset 0x10\n\t")
651 __ASM_CFI(".cfi_def_cfa %rbp,0x10\n\t")
652 __ASM_CFI(".cfi_rel_offset %rip,0x8\n\t")
653 __ASM_CFI(".cfi_rel_offset %rbp,0\n\t")
654 "call " __ASM_NAME("dispatch_apc") "\n\t"
655 "int3")
658 /*******************************************************************
659 * KiUserCallbackDispatcher (NTDLL.@)
661 * FIXME: not binary compatible
663 void WINAPI KiUserCallbackDispatcher( ULONG id, void *args, ULONG len )
665 NTSTATUS status;
667 __TRY
669 NTSTATUS (WINAPI *func)(void *, ULONG) = ((void **)NtCurrentTeb()->Peb->KernelCallbackTable)[id];
670 status = NtCallbackReturn( NULL, 0, func( args, len ));
672 __EXCEPT_ALL
674 ERR_(seh)( "ignoring exception\n" );
675 status = NtCallbackReturn( 0, 0, 0 );
677 __ENDTRY
679 RtlRaiseStatus( status );
683 static ULONG64 get_int_reg( CONTEXT *context, int reg )
685 return *(&context->Rax + reg);
688 static void set_int_reg( CONTEXT *context, KNONVOLATILE_CONTEXT_POINTERS *ctx_ptr, int reg, ULONG64 *val )
690 *(&context->Rax + reg) = *val;
691 if (ctx_ptr) ctx_ptr->IntegerContext[reg] = val;
694 static void set_float_reg( CONTEXT *context, KNONVOLATILE_CONTEXT_POINTERS *ctx_ptr, int reg, M128A *val )
696 /* Use a memcpy() to avoid issues if val is misaligned. */
697 memcpy(&context->Xmm0 + reg, val, sizeof(*val));
698 if (ctx_ptr) ctx_ptr->FloatingContext[reg] = val;
701 static int get_opcode_size( struct opcode op )
703 switch (op.code)
705 case UWOP_ALLOC_LARGE:
706 return 2 + (op.info != 0);
707 case UWOP_SAVE_NONVOL:
708 case UWOP_SAVE_XMM128:
709 case UWOP_EPILOG:
710 return 2;
711 case UWOP_SAVE_NONVOL_FAR:
712 case UWOP_SAVE_XMM128_FAR:
713 return 3;
714 default:
715 return 1;
719 static BOOL is_inside_epilog( BYTE *pc, ULONG64 base, const RUNTIME_FUNCTION *function )
721 /* add or lea must be the first instruction, and it must have a rex.W prefix */
722 if ((pc[0] & 0xf8) == 0x48)
724 switch (pc[1])
726 case 0x81: /* add $nnnn,%rsp */
727 if (pc[0] == 0x48 && pc[2] == 0xc4)
729 pc += 7;
730 break;
732 return FALSE;
733 case 0x83: /* add $n,%rsp */
734 if (pc[0] == 0x48 && pc[2] == 0xc4)
736 pc += 4;
737 break;
739 return FALSE;
740 case 0x8d: /* lea n(reg),%rsp */
741 if (pc[0] & 0x06) return FALSE; /* rex.RX must be cleared */
742 if (((pc[2] >> 3) & 7) != 4) return FALSE; /* dest reg mus be %rsp */
743 if ((pc[2] & 7) == 4) return FALSE; /* no SIB byte allowed */
744 if ((pc[2] >> 6) == 1) /* 8-bit offset */
746 pc += 4;
747 break;
749 if ((pc[2] >> 6) == 2) /* 32-bit offset */
751 pc += 7;
752 break;
754 return FALSE;
758 /* now check for various pop instructions */
760 for (;;)
762 if ((*pc & 0xf0) == 0x40) pc++; /* rex prefix */
764 switch (*pc)
766 case 0x58: /* pop %rax/%r8 */
767 case 0x59: /* pop %rcx/%r9 */
768 case 0x5a: /* pop %rdx/%r10 */
769 case 0x5b: /* pop %rbx/%r11 */
770 case 0x5c: /* pop %rsp/%r12 */
771 case 0x5d: /* pop %rbp/%r13 */
772 case 0x5e: /* pop %rsi/%r14 */
773 case 0x5f: /* pop %rdi/%r15 */
774 pc++;
775 continue;
776 case 0xc2: /* ret $nn */
777 case 0xc3: /* ret */
778 return TRUE;
779 case 0xe9: /* jmp nnnn */
780 pc += 5 + *(LONG *)(pc + 1);
781 if (pc - (BYTE *)base >= function->BeginAddress && pc - (BYTE *)base < function->EndAddress)
782 continue;
783 break;
784 case 0xeb: /* jmp n */
785 pc += 2 + (signed char)pc[1];
786 if (pc - (BYTE *)base >= function->BeginAddress && pc - (BYTE *)base < function->EndAddress)
787 continue;
788 break;
789 case 0xf3: /* rep; ret (for amd64 prediction bug) */
790 return pc[1] == 0xc3;
792 return FALSE;
796 /* execute a function epilog, which must have been validated with is_inside_epilog() */
797 static void interpret_epilog( BYTE *pc, CONTEXT *context, KNONVOLATILE_CONTEXT_POINTERS *ctx_ptr )
799 for (;;)
801 BYTE rex = 0;
803 if ((*pc & 0xf0) == 0x40) rex = *pc++ & 0x0f; /* rex prefix */
805 switch (*pc)
807 case 0x58: /* pop %rax/r8 */
808 case 0x59: /* pop %rcx/r9 */
809 case 0x5a: /* pop %rdx/r10 */
810 case 0x5b: /* pop %rbx/r11 */
811 case 0x5c: /* pop %rsp/r12 */
812 case 0x5d: /* pop %rbp/r13 */
813 case 0x5e: /* pop %rsi/r14 */
814 case 0x5f: /* pop %rdi/r15 */
815 set_int_reg( context, ctx_ptr, *pc - 0x58 + (rex & 1) * 8, (ULONG64 *)context->Rsp );
816 context->Rsp += sizeof(ULONG64);
817 pc++;
818 continue;
819 case 0x81: /* add $nnnn,%rsp */
820 context->Rsp += *(LONG *)(pc + 2);
821 pc += 2 + sizeof(LONG);
822 continue;
823 case 0x83: /* add $n,%rsp */
824 context->Rsp += (signed char)pc[2];
825 pc += 3;
826 continue;
827 case 0x8d:
828 if ((pc[1] >> 6) == 1) /* lea n(reg),%rsp */
830 context->Rsp = get_int_reg( context, (pc[1] & 7) + (rex & 1) * 8 ) + (signed char)pc[2];
831 pc += 3;
833 else /* lea nnnn(reg),%rsp */
835 context->Rsp = get_int_reg( context, (pc[1] & 7) + (rex & 1) * 8 ) + *(LONG *)(pc + 2);
836 pc += 2 + sizeof(LONG);
838 continue;
839 case 0xc2: /* ret $nn */
840 context->Rip = *(ULONG64 *)context->Rsp;
841 context->Rsp += sizeof(ULONG64) + *(WORD *)(pc + 1);
842 return;
843 case 0xc3: /* ret */
844 case 0xf3: /* rep; ret */
845 context->Rip = *(ULONG64 *)context->Rsp;
846 context->Rsp += sizeof(ULONG64);
847 return;
848 case 0xe9: /* jmp nnnn */
849 pc += 5 + *(LONG *)(pc + 1);
850 continue;
851 case 0xeb: /* jmp n */
852 pc += 2 + (signed char)pc[1];
853 continue;
855 return;
859 /**********************************************************************
860 * RtlVirtualUnwind (NTDLL.@)
862 PVOID WINAPI RtlVirtualUnwind( ULONG type, ULONG64 base, ULONG64 pc,
863 RUNTIME_FUNCTION *function, CONTEXT *context,
864 PVOID *data, ULONG64 *frame_ret,
865 KNONVOLATILE_CONTEXT_POINTERS *ctx_ptr )
867 union handler_data *handler_data;
868 ULONG64 frame, off;
869 struct UNWIND_INFO *info;
870 unsigned int i, prolog_offset;
871 BOOL mach_frame = FALSE;
873 TRACE( "type %lx rip %I64x rsp %I64x\n", type, pc, context->Rsp );
874 if (TRACE_ON(seh)) dump_unwind_info( base, function );
876 frame = *frame_ret = context->Rsp;
877 for (;;)
879 info = (struct UNWIND_INFO *)((char *)base + function->UnwindData);
880 handler_data = (union handler_data *)&info->opcodes[(info->count + 1) & ~1];
882 if (info->version != 1 && info->version != 2)
884 FIXME( "unknown unwind info version %u at %p\n", info->version, info );
885 return NULL;
888 if (info->frame_reg)
889 frame = get_int_reg( context, info->frame_reg ) - info->frame_offset * 16;
891 /* check if in prolog */
892 if (pc >= base + function->BeginAddress && pc < base + function->BeginAddress + info->prolog)
894 TRACE("inside prolog.\n");
895 prolog_offset = pc - base - function->BeginAddress;
897 else
899 prolog_offset = ~0;
900 /* Since Win10 1809 epilogue does not have a special treatment in case of zero opcode count. */
901 if (info->count && is_inside_epilog( (BYTE *)pc, base, function ))
903 TRACE("inside epilog.\n");
904 interpret_epilog( (BYTE *)pc, context, ctx_ptr );
905 *frame_ret = frame;
906 return NULL;
910 for (i = 0; i < info->count; i += get_opcode_size(info->opcodes[i]))
912 if (prolog_offset < info->opcodes[i].offset) continue; /* skip it */
914 switch (info->opcodes[i].code)
916 case UWOP_PUSH_NONVOL: /* pushq %reg */
917 set_int_reg( context, ctx_ptr, info->opcodes[i].info, (ULONG64 *)context->Rsp );
918 context->Rsp += sizeof(ULONG64);
919 break;
920 case UWOP_ALLOC_LARGE: /* subq $nn,%rsp */
921 if (info->opcodes[i].info) context->Rsp += *(DWORD *)&info->opcodes[i+1];
922 else context->Rsp += *(USHORT *)&info->opcodes[i+1] * 8;
923 break;
924 case UWOP_ALLOC_SMALL: /* subq $n,%rsp */
925 context->Rsp += (info->opcodes[i].info + 1) * 8;
926 break;
927 case UWOP_SET_FPREG: /* leaq nn(%rsp),%framereg */
928 context->Rsp = *frame_ret = frame;
929 break;
930 case UWOP_SAVE_NONVOL: /* movq %reg,n(%rsp) */
931 off = frame + *(USHORT *)&info->opcodes[i+1] * 8;
932 set_int_reg( context, ctx_ptr, info->opcodes[i].info, (ULONG64 *)off );
933 break;
934 case UWOP_SAVE_NONVOL_FAR: /* movq %reg,nn(%rsp) */
935 off = frame + *(DWORD *)&info->opcodes[i+1];
936 set_int_reg( context, ctx_ptr, info->opcodes[i].info, (ULONG64 *)off );
937 break;
938 case UWOP_SAVE_XMM128: /* movaps %xmmreg,n(%rsp) */
939 off = frame + *(USHORT *)&info->opcodes[i+1] * 16;
940 set_float_reg( context, ctx_ptr, info->opcodes[i].info, (M128A *)off );
941 break;
942 case UWOP_SAVE_XMM128_FAR: /* movaps %xmmreg,nn(%rsp) */
943 off = frame + *(DWORD *)&info->opcodes[i+1];
944 set_float_reg( context, ctx_ptr, info->opcodes[i].info, (M128A *)off );
945 break;
946 case UWOP_PUSH_MACHFRAME:
947 if (info->flags & UNW_FLAG_CHAININFO)
949 FIXME("PUSH_MACHFRAME with chained unwind info.\n");
950 break;
952 if (i + get_opcode_size(info->opcodes[i]) < info->count )
954 FIXME("PUSH_MACHFRAME is not the last opcode.\n");
955 break;
958 if (info->opcodes[i].info)
959 context->Rsp += 0x8;
961 context->Rip = *(ULONG64 *)context->Rsp;
962 context->Rsp = *(ULONG64 *)(context->Rsp + 24);
963 mach_frame = TRUE;
964 break;
965 case UWOP_EPILOG:
966 if (info->version == 2)
967 break; /* nothing to do */
968 default:
969 FIXME( "unknown code %u\n", info->opcodes[i].code );
970 break;
974 if (!(info->flags & UNW_FLAG_CHAININFO)) break;
975 function = &handler_data->chain; /* restart with the chained info */
978 if (!mach_frame)
980 /* now pop return address */
981 context->Rip = *(ULONG64 *)context->Rsp;
982 context->Rsp += sizeof(ULONG64);
985 if (!(info->flags & type)) return NULL; /* no matching handler */
986 if (prolog_offset != ~0) return NULL; /* inside prolog */
988 *data = &handler_data->handler + 1;
989 return (char *)base + handler_data->handler;
992 struct unwind_exception_frame
994 EXCEPTION_REGISTRATION_RECORD frame;
995 DISPATCHER_CONTEXT *dispatch;
998 /**********************************************************************
999 * unwind_exception_handler
1001 * Handler for exceptions happening while calling an unwind handler.
1003 static DWORD __cdecl unwind_exception_handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
1004 CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher )
1006 struct unwind_exception_frame *unwind_frame = (struct unwind_exception_frame *)frame;
1007 DISPATCHER_CONTEXT *dispatch = (DISPATCHER_CONTEXT *)dispatcher;
1009 /* copy the original dispatcher into the current one, except for the TargetIp */
1010 dispatch->ControlPc = unwind_frame->dispatch->ControlPc;
1011 dispatch->ImageBase = unwind_frame->dispatch->ImageBase;
1012 dispatch->FunctionEntry = unwind_frame->dispatch->FunctionEntry;
1013 dispatch->EstablisherFrame = unwind_frame->dispatch->EstablisherFrame;
1014 dispatch->ContextRecord = unwind_frame->dispatch->ContextRecord;
1015 dispatch->LanguageHandler = unwind_frame->dispatch->LanguageHandler;
1016 dispatch->HandlerData = unwind_frame->dispatch->HandlerData;
1017 dispatch->HistoryTable = unwind_frame->dispatch->HistoryTable;
1018 dispatch->ScopeIndex = unwind_frame->dispatch->ScopeIndex;
1019 TRACE( "detected collided unwind\n" );
1020 return ExceptionCollidedUnwind;
1023 /**********************************************************************
1024 * call_unwind_handler
1026 * Call a single unwind handler.
1028 static DWORD call_unwind_handler( EXCEPTION_RECORD *rec, DISPATCHER_CONTEXT *dispatch )
1030 struct unwind_exception_frame frame;
1031 DWORD res;
1033 frame.frame.Handler = unwind_exception_handler;
1034 frame.dispatch = dispatch;
1035 __wine_push_frame( &frame.frame );
1037 TRACE( "calling handler %p (rec=%p, frame=%p context=%p, dispatch=%p)\n",
1038 dispatch->LanguageHandler, rec, (void *)dispatch->EstablisherFrame, dispatch->ContextRecord, dispatch );
1039 res = dispatch->LanguageHandler( rec, (void *)dispatch->EstablisherFrame, dispatch->ContextRecord, dispatch );
1040 TRACE( "handler %p returned %lx\n", dispatch->LanguageHandler, res );
1042 __wine_pop_frame( &frame.frame );
1044 switch (res)
1046 case ExceptionContinueSearch:
1047 case ExceptionCollidedUnwind:
1048 break;
1049 default:
1050 raise_status( STATUS_INVALID_DISPOSITION, rec );
1051 break;
1054 return res;
1058 /**********************************************************************
1059 * call_teb_unwind_handler
1061 * Call a single unwind handler from the TEB chain.
1063 static DWORD call_teb_unwind_handler( EXCEPTION_RECORD *rec, DISPATCHER_CONTEXT *dispatch,
1064 EXCEPTION_REGISTRATION_RECORD *teb_frame )
1066 DWORD res;
1068 TRACE( "calling TEB handler %p (rec=%p, frame=%p context=%p, dispatch=%p)\n",
1069 teb_frame->Handler, rec, teb_frame, dispatch->ContextRecord, dispatch );
1070 res = teb_frame->Handler( rec, teb_frame, dispatch->ContextRecord, (EXCEPTION_REGISTRATION_RECORD**)dispatch );
1071 TRACE( "handler at %p returned %lu\n", teb_frame->Handler, res );
1073 switch (res)
1075 case ExceptionContinueSearch:
1076 case ExceptionCollidedUnwind:
1077 break;
1078 default:
1079 raise_status( STATUS_INVALID_DISPOSITION, rec );
1080 break;
1083 return res;
1087 /**********************************************************************
1088 * call_consolidate_callback
1090 * Wrapper function to call a consolidate callback from a fake frame.
1091 * If the callback executes RtlUnwindEx (like for example done in C++ handlers),
1092 * we have to skip all frames which were already processed. To do that we
1093 * trick the unwinding functions into thinking the call came from the specified
1094 * context. All CFI instructions are either DW_CFA_def_cfa_expression or
1095 * DW_CFA_expression, and the expressions have the following format:
1097 * DW_OP_breg6; sleb128 0x10 | Load %rbp + 0x10
1098 * DW_OP_deref | Get *(%rbp + 0x10) == context
1099 * DW_OP_plus_uconst; uleb128 <OFFSET> | Add offset to get struct member
1100 * [DW_OP_deref] | Dereference, only for CFA
1102 extern void * WINAPI call_consolidate_callback( CONTEXT *context,
1103 void *(CALLBACK *callback)(EXCEPTION_RECORD *),
1104 EXCEPTION_RECORD *rec );
1105 __ASM_GLOBAL_FUNC( call_consolidate_callback,
1106 "pushq %rbp\n\t"
1107 __ASM_CFI(".cfi_adjust_cfa_offset 8\n\t")
1108 __ASM_CFI(".cfi_rel_offset %rbp,0\n\t")
1109 "movq %rsp,%rbp\n\t"
1110 __ASM_CFI(".cfi_def_cfa_register %rbp\n\t")
1112 /* Setup SEH machine frame. */
1113 "subq $0x28,%rsp\n\t"
1114 __ASM_CFI(".cfi_adjust_cfa_offset 0x28\n\t")
1115 "movq 0xf8(%rcx),%rax\n\t" /* Context->Rip */
1116 "movq %rax,(%rsp)\n\t"
1117 "movq 0x98(%rcx),%rax\n\t" /* context->Rsp */
1118 "movq %rax,0x18(%rsp)\n\t"
1119 __ASM_SEH(".seh_pushframe\n\t")
1120 __ASM_SEH(".seh_endprologue\n\t")
1122 "subq $0x108,%rsp\n\t" /* 10*16 (float regs) + 8*8 (int regs) + 32 (shadow store) + 8 (align). */
1123 __ASM_SEH(".seh_stackalloc 0x108\n\t")
1124 __ASM_CFI(".cfi_adjust_cfa_offset 0x108\n\t")
1126 /* Setup CFI unwind to context. */
1127 "movq %rcx,0x10(%rbp)\n\t"
1128 __ASM_CFI(".cfi_remember_state\n\t")
1129 __ASM_CFI(".cfi_escape 0x0f,0x07,0x76,0x10,0x06,0x23,0x98,0x01,0x06\n\t") /* CFA */
1130 __ASM_CFI(".cfi_escape 0x10,0x03,0x06,0x76,0x10,0x06,0x23,0x90,0x01\n\t") /* %rbx */
1131 __ASM_CFI(".cfi_escape 0x10,0x04,0x06,0x76,0x10,0x06,0x23,0xa8,0x01\n\t") /* %rsi */
1132 __ASM_CFI(".cfi_escape 0x10,0x05,0x06,0x76,0x10,0x06,0x23,0xb0,0x01\n\t") /* %rdi */
1133 __ASM_CFI(".cfi_escape 0x10,0x06,0x06,0x76,0x10,0x06,0x23,0xa0,0x01\n\t") /* %rbp */
1134 __ASM_CFI(".cfi_escape 0x10,0x0c,0x06,0x76,0x10,0x06,0x23,0xd8,0x01\n\t") /* %r12 */
1135 __ASM_CFI(".cfi_escape 0x10,0x0d,0x06,0x76,0x10,0x06,0x23,0xe0,0x01\n\t") /* %r13 */
1136 __ASM_CFI(".cfi_escape 0x10,0x0e,0x06,0x76,0x10,0x06,0x23,0xe8,0x01\n\t") /* %r14 */
1137 __ASM_CFI(".cfi_escape 0x10,0x0f,0x06,0x76,0x10,0x06,0x23,0xf0,0x01\n\t") /* %r15 */
1138 __ASM_CFI(".cfi_escape 0x10,0x10,0x06,0x76,0x10,0x06,0x23,0xf8,0x01\n\t") /* %rip */
1139 __ASM_CFI(".cfi_escape 0x10,0x17,0x06,0x76,0x10,0x06,0x23,0x80,0x04\n\t") /* %xmm6 */
1140 __ASM_CFI(".cfi_escape 0x10,0x18,0x06,0x76,0x10,0x06,0x23,0x90,0x04\n\t") /* %xmm7 */
1141 __ASM_CFI(".cfi_escape 0x10,0x19,0x06,0x76,0x10,0x06,0x23,0xa0,0x04\n\t") /* %xmm8 */
1142 __ASM_CFI(".cfi_escape 0x10,0x1a,0x06,0x76,0x10,0x06,0x23,0xb0,0x04\n\t") /* %xmm9 */
1143 __ASM_CFI(".cfi_escape 0x10,0x1b,0x06,0x76,0x10,0x06,0x23,0xc0,0x04\n\t") /* %xmm10 */
1144 __ASM_CFI(".cfi_escape 0x10,0x1c,0x06,0x76,0x10,0x06,0x23,0xd0,0x04\n\t") /* %xmm11 */
1145 __ASM_CFI(".cfi_escape 0x10,0x1d,0x06,0x76,0x10,0x06,0x23,0xe0,0x04\n\t") /* %xmm12 */
1146 __ASM_CFI(".cfi_escape 0x10,0x1e,0x06,0x76,0x10,0x06,0x23,0xf0,0x04\n\t") /* %xmm13 */
1147 __ASM_CFI(".cfi_escape 0x10,0x1f,0x06,0x76,0x10,0x06,0x23,0x80,0x05\n\t") /* %xmm14 */
1148 __ASM_CFI(".cfi_escape 0x10,0x20,0x06,0x76,0x10,0x06,0x23,0x90,0x05\n\t") /* %xmm15 */
1150 /* Setup SEH unwind registers restore. */
1151 "movq 0xa0(%rcx),%rax\n\t" /* context->Rbp */
1152 "movq %rax,0x100(%rsp)\n\t"
1153 __ASM_SEH(".seh_savereg %rbp, 0x100\n\t")
1154 "movq 0x90(%rcx),%rax\n\t" /* context->Rbx */
1155 "movq %rax,0x20(%rsp)\n\t"
1156 __ASM_SEH(".seh_savereg %rbx, 0x20\n\t")
1157 "movq 0xa8(%rcx),%rax\n\t" /* context->Rsi */
1158 "movq %rax,0x28(%rsp)\n\t"
1159 __ASM_SEH(".seh_savereg %rsi, 0x28\n\t")
1160 "movq 0xb0(%rcx),%rax\n\t" /* context->Rdi */
1161 "movq %rax,0x30(%rsp)\n\t"
1162 __ASM_SEH(".seh_savereg %rdi, 0x30\n\t")
1164 "movq 0xd8(%rcx),%rax\n\t" /* context->R12 */
1165 "movq %rax,0x38(%rsp)\n\t"
1166 __ASM_SEH(".seh_savereg %r12, 0x38\n\t")
1167 "movq 0xe0(%rcx),%rax\n\t" /* context->R13 */
1168 "movq %rax,0x40(%rsp)\n\t"
1169 __ASM_SEH(".seh_savereg %r13, 0x40\n\t")
1170 "movq 0xe8(%rcx),%rax\n\t" /* context->R14 */
1171 "movq %rax,0x48(%rsp)\n\t"
1172 __ASM_SEH(".seh_savereg %r14, 0x48\n\t")
1173 "movq 0xf0(%rcx),%rax\n\t" /* context->R15 */
1174 "movq %rax,0x50(%rsp)\n\t"
1175 __ASM_SEH(".seh_savereg %r15, 0x50\n\t")
1176 "pushq %rsi\n\t"
1177 "pushq %rdi\n\t"
1178 "leaq 0x200(%rcx),%rsi\n\t"
1179 "leaq 0x70(%rsp),%rdi\n\t"
1180 "movq $0x14,%rcx\n\t"
1181 "cld\n\t"
1182 "rep; movsq\n\t"
1183 "popq %rdi\n\t"
1184 "popq %rsi\n\t"
1185 __ASM_SEH(".seh_savexmm %xmm6, 0x60\n\t")
1186 __ASM_SEH(".seh_savexmm %xmm7, 0x70\n\t")
1187 __ASM_SEH(".seh_savexmm %xmm8, 0x80\n\t")
1188 __ASM_SEH(".seh_savexmm %xmm9, 0x90\n\t")
1189 __ASM_SEH(".seh_savexmm %xmm10, 0xa0\n\t")
1190 __ASM_SEH(".seh_savexmm %xmm11, 0xb0\n\t")
1191 __ASM_SEH(".seh_savexmm %xmm12, 0xc0\n\t")
1192 __ASM_SEH(".seh_savexmm %xmm13, 0xd0\n\t")
1193 __ASM_SEH(".seh_savexmm %xmm14, 0xe0\n\t")
1194 __ASM_SEH(".seh_savexmm %xmm15, 0xf0\n\t")
1196 /* call the callback. */
1197 "movq %r8,%rcx\n\t"
1198 "callq *%rdx\n\t"
1199 __ASM_CFI(".cfi_restore_state\n\t")
1200 "nop\n\t" /* Otherwise RtlVirtualUnwind() will think we are inside epilogue and
1201 * interpret / execute the rest of opcodes here instead of unwind through
1202 * machine frame. */
1203 "leaq 0(%rbp),%rsp\n\t"
1204 __ASM_CFI(".cfi_def_cfa_register %rsp\n\t")
1205 "popq %rbp\n\t"
1206 __ASM_CFI(".cfi_adjust_cfa_offset -8\n\t")
1207 __ASM_CFI(".cfi_same_value %rbp\n\t")
1208 "ret")
1210 /*******************************************************************
1211 * RtlRestoreContext (NTDLL.@)
1213 void CDECL RtlRestoreContext( CONTEXT *context, EXCEPTION_RECORD *rec )
1215 EXCEPTION_REGISTRATION_RECORD *teb_frame = NtCurrentTeb()->Tib.ExceptionList;
1217 if (rec && rec->ExceptionCode == STATUS_LONGJUMP && rec->NumberParameters >= 1)
1219 struct MSVCRT_JUMP_BUFFER *jmp = (struct MSVCRT_JUMP_BUFFER *)rec->ExceptionInformation[0];
1220 context->Rbx = jmp->Rbx;
1221 context->Rsp = jmp->Rsp;
1222 context->Rbp = jmp->Rbp;
1223 context->Rsi = jmp->Rsi;
1224 context->Rdi = jmp->Rdi;
1225 context->R12 = jmp->R12;
1226 context->R13 = jmp->R13;
1227 context->R14 = jmp->R14;
1228 context->R15 = jmp->R15;
1229 context->Rip = jmp->Rip;
1230 context->Xmm6 = jmp->Xmm6;
1231 context->Xmm7 = jmp->Xmm7;
1232 context->Xmm8 = jmp->Xmm8;
1233 context->Xmm9 = jmp->Xmm9;
1234 context->Xmm10 = jmp->Xmm10;
1235 context->Xmm11 = jmp->Xmm11;
1236 context->Xmm12 = jmp->Xmm12;
1237 context->Xmm13 = jmp->Xmm13;
1238 context->Xmm14 = jmp->Xmm14;
1239 context->Xmm15 = jmp->Xmm15;
1240 context->MxCsr = jmp->MxCsr;
1241 context->FltSave.MxCsr = jmp->MxCsr;
1242 context->FltSave.ControlWord = jmp->FpCsr;
1244 else if (rec && rec->ExceptionCode == STATUS_UNWIND_CONSOLIDATE && rec->NumberParameters >= 1)
1246 PVOID (CALLBACK *consolidate)(EXCEPTION_RECORD *) = (void *)rec->ExceptionInformation[0];
1247 TRACE_(seh)( "calling consolidate callback %p (rec=%p)\n", consolidate, rec );
1248 context->Rip = (ULONG64)call_consolidate_callback( context, consolidate, rec );
1251 /* hack: remove no longer accessible TEB frames */
1252 while ((ULONG64)teb_frame < context->Rsp)
1254 TRACE_(seh)( "removing TEB frame: %p\n", teb_frame );
1255 teb_frame = __wine_pop_frame( teb_frame );
1258 TRACE_(seh)( "returning to %p stack %p\n", (void *)context->Rip, (void *)context->Rsp );
1259 NtContinue( context, FALSE );
1263 /*******************************************************************
1264 * RtlUnwindEx (NTDLL.@)
1266 void WINAPI RtlUnwindEx( PVOID end_frame, PVOID target_ip, EXCEPTION_RECORD *rec,
1267 PVOID retval, CONTEXT *context, UNWIND_HISTORY_TABLE *table )
1269 EXCEPTION_REGISTRATION_RECORD *teb_frame = NtCurrentTeb()->Tib.ExceptionList;
1270 EXCEPTION_RECORD record;
1271 DISPATCHER_CONTEXT dispatch;
1272 CONTEXT new_context;
1273 NTSTATUS status;
1274 DWORD i;
1276 RtlCaptureContext( context );
1277 new_context = *context;
1279 /* build an exception record, if we do not have one */
1280 if (!rec)
1282 record.ExceptionCode = STATUS_UNWIND;
1283 record.ExceptionFlags = 0;
1284 record.ExceptionRecord = NULL;
1285 record.ExceptionAddress = (void *)context->Rip;
1286 record.NumberParameters = 0;
1287 rec = &record;
1290 rec->ExceptionFlags |= EH_UNWINDING | (end_frame ? 0 : EH_EXIT_UNWIND);
1292 TRACE( "code=%lx flags=%lx end_frame=%p target_ip=%p rip=%016I64x\n",
1293 rec->ExceptionCode, rec->ExceptionFlags, end_frame, target_ip, context->Rip );
1294 for (i = 0; i < min( EXCEPTION_MAXIMUM_PARAMETERS, rec->NumberParameters ); i++)
1295 TRACE( " info[%ld]=%016I64x\n", i, rec->ExceptionInformation[i] );
1296 TRACE(" rax=%016I64x rbx=%016I64x rcx=%016I64x rdx=%016I64x\n",
1297 context->Rax, context->Rbx, context->Rcx, context->Rdx );
1298 TRACE(" rsi=%016I64x rdi=%016I64x rbp=%016I64x rsp=%016I64x\n",
1299 context->Rsi, context->Rdi, context->Rbp, context->Rsp );
1300 TRACE(" r8=%016I64x r9=%016I64x r10=%016I64x r11=%016I64x\n",
1301 context->R8, context->R9, context->R10, context->R11 );
1302 TRACE(" r12=%016I64x r13=%016I64x r14=%016I64x r15=%016I64x\n",
1303 context->R12, context->R13, context->R14, context->R15 );
1305 dispatch.EstablisherFrame = context->Rsp;
1306 dispatch.TargetIp = (ULONG64)target_ip;
1307 dispatch.ContextRecord = context;
1308 dispatch.HistoryTable = table;
1310 for (;;)
1312 status = virtual_unwind( UNW_FLAG_UHANDLER, &dispatch, &new_context );
1313 if (status != STATUS_SUCCESS) raise_status( status, rec );
1315 unwind_done:
1316 if (!dispatch.EstablisherFrame) break;
1318 if ((dispatch.EstablisherFrame & 7) ||
1319 dispatch.EstablisherFrame < (ULONG64)NtCurrentTeb()->Tib.StackLimit ||
1320 dispatch.EstablisherFrame > (ULONG64)NtCurrentTeb()->Tib.StackBase)
1322 ERR( "invalid frame %p (%p-%p)\n", (void *)dispatch.EstablisherFrame,
1323 NtCurrentTeb()->Tib.StackLimit, NtCurrentTeb()->Tib.StackBase );
1324 rec->ExceptionFlags |= EH_STACK_INVALID;
1325 break;
1328 if (dispatch.LanguageHandler)
1330 if (end_frame && (dispatch.EstablisherFrame > (ULONG64)end_frame))
1332 ERR( "invalid end frame %p/%p\n", (void *)dispatch.EstablisherFrame, end_frame );
1333 raise_status( STATUS_INVALID_UNWIND_TARGET, rec );
1335 if (dispatch.EstablisherFrame == (ULONG64)end_frame) rec->ExceptionFlags |= EH_TARGET_UNWIND;
1336 if (call_unwind_handler( rec, &dispatch ) == ExceptionCollidedUnwind)
1338 ULONG64 frame;
1340 new_context = *dispatch.ContextRecord;
1341 new_context.ContextFlags &= ~0x40;
1342 *context = new_context;
1343 dispatch.ContextRecord = context;
1344 RtlVirtualUnwind( UNW_FLAG_NHANDLER, dispatch.ImageBase,
1345 dispatch.ControlPc, dispatch.FunctionEntry,
1346 &new_context, NULL, &frame, NULL );
1347 rec->ExceptionFlags |= EH_COLLIDED_UNWIND;
1348 goto unwind_done;
1350 rec->ExceptionFlags &= ~EH_COLLIDED_UNWIND;
1352 else /* hack: call builtin handlers registered in the tib list */
1354 DWORD64 backup_frame = dispatch.EstablisherFrame;
1355 while ((ULONG64)teb_frame < new_context.Rsp && (ULONG64)teb_frame < (ULONG64)end_frame)
1357 TRACE( "found builtin frame %p handler %p\n", teb_frame, teb_frame->Handler );
1358 dispatch.EstablisherFrame = (ULONG64)teb_frame;
1359 if (call_teb_unwind_handler( rec, &dispatch, teb_frame ) == ExceptionCollidedUnwind)
1361 ULONG64 frame;
1363 teb_frame = __wine_pop_frame( teb_frame );
1365 new_context = *dispatch.ContextRecord;
1366 new_context.ContextFlags &= ~0x40;
1367 *context = new_context;
1368 dispatch.ContextRecord = context;
1369 RtlVirtualUnwind( UNW_FLAG_NHANDLER, dispatch.ImageBase,
1370 dispatch.ControlPc, dispatch.FunctionEntry,
1371 &new_context, NULL, &frame, NULL );
1372 rec->ExceptionFlags |= EH_COLLIDED_UNWIND;
1373 goto unwind_done;
1375 teb_frame = __wine_pop_frame( teb_frame );
1377 if ((ULONG64)teb_frame == (ULONG64)end_frame && (ULONG64)end_frame < new_context.Rsp) break;
1378 dispatch.EstablisherFrame = backup_frame;
1381 if (dispatch.EstablisherFrame == (ULONG64)end_frame) break;
1382 *context = new_context;
1385 context->Rax = (ULONG64)retval;
1386 context->Rip = (ULONG64)target_ip;
1387 RtlRestoreContext(context, rec);
1391 /*******************************************************************
1392 * RtlUnwind (NTDLL.@)
1394 void WINAPI RtlUnwind( void *frame, void *target_ip, EXCEPTION_RECORD *rec, void *retval )
1396 CONTEXT context;
1397 RtlUnwindEx( frame, target_ip, rec, retval, &context, NULL );
1401 /*******************************************************************
1402 * _local_unwind (NTDLL.@)
1404 void WINAPI _local_unwind( void *frame, void *target_ip )
1406 CONTEXT context;
1407 RtlUnwindEx( frame, target_ip, NULL, NULL, &context, NULL );
1410 /*******************************************************************
1411 * __C_specific_handler (NTDLL.@)
1413 EXCEPTION_DISPOSITION WINAPI __C_specific_handler( EXCEPTION_RECORD *rec,
1414 void *frame,
1415 CONTEXT *context,
1416 struct _DISPATCHER_CONTEXT *dispatch )
1418 SCOPE_TABLE *table = dispatch->HandlerData;
1419 ULONG i;
1421 TRACE_(seh)( "%p %p %p %p\n", rec, frame, context, dispatch );
1422 if (TRACE_ON(seh)) dump_scope_table( dispatch->ImageBase, table );
1424 if (rec->ExceptionFlags & (EH_UNWINDING | EH_EXIT_UNWIND))
1426 for (i = dispatch->ScopeIndex; i < table->Count; i++)
1428 if (dispatch->ControlPc >= dispatch->ImageBase + table->ScopeRecord[i].BeginAddress &&
1429 dispatch->ControlPc < dispatch->ImageBase + table->ScopeRecord[i].EndAddress)
1431 PTERMINATION_HANDLER handler;
1433 if (table->ScopeRecord[i].JumpTarget) continue;
1435 if (rec->ExceptionFlags & EH_TARGET_UNWIND &&
1436 dispatch->TargetIp >= dispatch->ImageBase + table->ScopeRecord[i].BeginAddress &&
1437 dispatch->TargetIp < dispatch->ImageBase + table->ScopeRecord[i].EndAddress)
1439 break;
1442 handler = (PTERMINATION_HANDLER)(dispatch->ImageBase + table->ScopeRecord[i].HandlerAddress);
1443 dispatch->ScopeIndex = i+1;
1445 TRACE_(seh)( "calling __finally %p frame %p\n", handler, frame );
1446 handler( TRUE, frame );
1449 return ExceptionContinueSearch;
1452 for (i = dispatch->ScopeIndex; i < table->Count; i++)
1454 if (dispatch->ControlPc >= dispatch->ImageBase + table->ScopeRecord[i].BeginAddress &&
1455 dispatch->ControlPc < dispatch->ImageBase + table->ScopeRecord[i].EndAddress)
1457 if (!table->ScopeRecord[i].JumpTarget) continue;
1458 if (table->ScopeRecord[i].HandlerAddress != EXCEPTION_EXECUTE_HANDLER)
1460 EXCEPTION_POINTERS ptrs;
1461 PEXCEPTION_FILTER filter;
1463 filter = (PEXCEPTION_FILTER)(dispatch->ImageBase + table->ScopeRecord[i].HandlerAddress);
1464 ptrs.ExceptionRecord = rec;
1465 ptrs.ContextRecord = context;
1466 TRACE_(seh)( "calling filter %p ptrs %p frame %p\n", filter, &ptrs, frame );
1467 switch (filter( &ptrs, frame ))
1469 case EXCEPTION_EXECUTE_HANDLER:
1470 break;
1471 case EXCEPTION_CONTINUE_SEARCH:
1472 continue;
1473 case EXCEPTION_CONTINUE_EXECUTION:
1474 return ExceptionContinueExecution;
1477 TRACE( "unwinding to target %p\n", (char *)dispatch->ImageBase + table->ScopeRecord[i].JumpTarget );
1478 RtlUnwindEx( frame, (char *)dispatch->ImageBase + table->ScopeRecord[i].JumpTarget,
1479 rec, 0, dispatch->ContextRecord, dispatch->HistoryTable );
1482 return ExceptionContinueSearch;
1486 /***********************************************************************
1487 * RtlRaiseException (NTDLL.@)
1489 __ASM_GLOBAL_FUNC( RtlRaiseException,
1490 "sub $0x4f8,%rsp\n\t"
1491 __ASM_SEH(".seh_stackalloc 0x4f8\n\t")
1492 __ASM_SEH(".seh_endprologue\n\t")
1493 __ASM_CFI(".cfi_adjust_cfa_offset 0x4f8\n\t")
1494 "movq %rcx,0x500(%rsp)\n\t"
1495 "leaq 0x20(%rsp),%rcx\n\t"
1496 "call " __ASM_NAME("RtlCaptureContext") "\n\t"
1497 "leaq 0x20(%rsp),%rdx\n\t" /* context pointer */
1498 "leaq 0x500(%rsp),%rax\n\t" /* orig stack pointer */
1499 "movq %rax,0x98(%rdx)\n\t" /* context->Rsp */
1500 "movq (%rax),%rcx\n\t" /* original first parameter */
1501 "movq %rcx,0x80(%rdx)\n\t" /* context->Rcx */
1502 "movq 0x4f8(%rsp),%rax\n\t" /* return address */
1503 "movq %rax,0xf8(%rdx)\n\t" /* context->Rip */
1504 "movq %rax,0x10(%rcx)\n\t" /* rec->ExceptionAddress */
1505 "movl $1,%r8d\n\t"
1506 "movq %gs:(0x30),%rax\n\t" /* Teb */
1507 "movq 0x60(%rax),%rax\n\t" /* Peb */
1508 "cmpb $0,0x02(%rax)\n\t" /* BeingDebugged */
1509 "jne 1f\n\t"
1510 "call " __ASM_NAME("dispatch_exception") "\n"
1511 "1:\tcall " __ASM_NAME("NtRaiseException") "\n\t"
1512 "movq %rax,%rcx\n\t"
1513 "call " __ASM_NAME("RtlRaiseStatus") /* does not return */ );
1516 static inline ULONG hash_pointers( void **ptrs, ULONG count )
1518 /* Based on MurmurHash2, which is in the public domain */
1519 static const ULONG m = 0x5bd1e995;
1520 static const ULONG r = 24;
1521 ULONG hash = count * sizeof(void*);
1522 for (; count > 0; ptrs++, count--)
1524 ULONG_PTR data = (ULONG_PTR)*ptrs;
1525 ULONG k1 = (ULONG)(data & 0xffffffff), k2 = (ULONG)(data >> 32);
1526 k1 *= m;
1527 k1 = (k1 ^ (k1 >> r)) * m;
1528 k2 *= m;
1529 k2 = (k2 ^ (k2 >> r)) * m;
1530 hash = (((hash * m) ^ k1) * m) ^ k2;
1532 hash = (hash ^ (hash >> 13)) * m;
1533 return hash ^ (hash >> 15);
1537 /*************************************************************************
1538 * RtlCaptureStackBackTrace (NTDLL.@)
1540 USHORT WINAPI RtlCaptureStackBackTrace( ULONG skip, ULONG count, PVOID *buffer, ULONG *hash )
1542 UNWIND_HISTORY_TABLE table;
1543 DISPATCHER_CONTEXT dispatch;
1544 CONTEXT context;
1545 NTSTATUS status;
1546 ULONG i;
1547 USHORT num_entries = 0;
1549 TRACE( "(%lu, %lu, %p, %p)\n", skip, count, buffer, hash );
1551 RtlCaptureContext( &context );
1552 dispatch.TargetIp = 0;
1553 dispatch.ContextRecord = &context;
1554 dispatch.HistoryTable = &table;
1555 if (hash) *hash = 0;
1556 for (i = 0; i < skip + count; i++)
1558 status = virtual_unwind( UNW_FLAG_NHANDLER, &dispatch, &context );
1559 if (status != STATUS_SUCCESS) return i;
1561 if (!dispatch.EstablisherFrame) break;
1563 if ((dispatch.EstablisherFrame & 7) ||
1564 dispatch.EstablisherFrame < (ULONG64)NtCurrentTeb()->Tib.StackLimit ||
1565 dispatch.EstablisherFrame > (ULONG64)NtCurrentTeb()->Tib.StackBase)
1567 ERR( "invalid frame %p (%p-%p)\n", (void *)dispatch.EstablisherFrame,
1568 NtCurrentTeb()->Tib.StackLimit, NtCurrentTeb()->Tib.StackBase );
1569 break;
1572 if (context.Rsp == (ULONG64)NtCurrentTeb()->Tib.StackBase) break;
1574 if (i >= skip) buffer[num_entries++] = (void *)context.Rip;
1576 if (hash && num_entries > 0) *hash = hash_pointers( buffer, num_entries );
1577 TRACE( "captured %hu frames\n", num_entries );
1578 return num_entries;
1582 /***********************************************************************
1583 * signal_start_thread
1585 __ASM_GLOBAL_FUNC( signal_start_thread,
1586 "movq %rcx,%rbx\n\t" /* context */
1587 /* clear the thread stack */
1588 "andq $~0xfff,%rcx\n\t" /* round down to page size */
1589 "leaq -0xf0000(%rcx),%rdi\n\t"
1590 "movq %rdi,%rsp\n\t"
1591 "subq %rdi,%rcx\n\t"
1592 "xorl %eax,%eax\n\t"
1593 "shrq $3,%rcx\n\t"
1594 "rep; stosq\n\t"
1595 /* switch to the initial context */
1596 "leaq -32(%rbx),%rsp\n\t"
1597 "movq %rbx,%rcx\n\t"
1598 "movl $1,%edx\n\t"
1599 "call " __ASM_NAME("NtContinue") )
1602 /**********************************************************************
1603 * DbgBreakPoint (NTDLL.@)
1605 __ASM_STDCALL_FUNC( DbgBreakPoint, 0, "int $3; ret"
1606 "\n\tnop; nop; nop; nop; nop; nop; nop; nop"
1607 "\n\tnop; nop; nop; nop; nop; nop" );
1609 /**********************************************************************
1610 * DbgUserBreakPoint (NTDLL.@)
1612 __ASM_STDCALL_FUNC( DbgUserBreakPoint, 0, "int $3; ret"
1613 "\n\tnop; nop; nop; nop; nop; nop; nop; nop"
1614 "\n\tnop; nop; nop; nop; nop; nop" );
1616 #endif /* __x86_64__ */