user32: Hook drawing menu buttons.
[wine.git] / dlls / ntdll / signal_x86_64.c
blobb1ab4933b93ccd318f1870990316fe6595d47915
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 #define NONAMELESSUNION
27 #define NONAMELESSSTRUCT
28 #include "ntstatus.h"
29 #define WIN32_NO_STATUS
30 #include "windef.h"
31 #include "winternl.h"
32 #include "wine/exception.h"
33 #include "wine/list.h"
34 #include "ntdll_misc.h"
35 #include "wine/debug.h"
37 WINE_DEFAULT_DEBUG_CHANNEL(unwind);
38 WINE_DECLARE_DEBUG_CHANNEL(seh);
39 WINE_DECLARE_DEBUG_CHANNEL(threadname);
41 typedef struct _SCOPE_TABLE
43 ULONG Count;
44 struct
46 ULONG BeginAddress;
47 ULONG EndAddress;
48 ULONG HandlerAddress;
49 ULONG JumpTarget;
50 } ScopeRecord[1];
51 } SCOPE_TABLE, *PSCOPE_TABLE;
54 /* layering violation: the setjmp buffer is defined in msvcrt, but used by RtlUnwindEx */
55 struct MSVCRT_JUMP_BUFFER
57 ULONG64 Frame;
58 ULONG64 Rbx;
59 ULONG64 Rsp;
60 ULONG64 Rbp;
61 ULONG64 Rsi;
62 ULONG64 Rdi;
63 ULONG64 R12;
64 ULONG64 R13;
65 ULONG64 R14;
66 ULONG64 R15;
67 ULONG64 Rip;
68 ULONG MxCsr;
69 USHORT FpCsr;
70 USHORT Spare;
71 M128A Xmm6;
72 M128A Xmm7;
73 M128A Xmm8;
74 M128A Xmm9;
75 M128A Xmm10;
76 M128A Xmm11;
77 M128A Xmm12;
78 M128A Xmm13;
79 M128A Xmm14;
80 M128A Xmm15;
83 /***********************************************************************
84 * Definitions for Win32 unwind tables
87 union handler_data
89 RUNTIME_FUNCTION chain;
90 ULONG handler;
93 struct opcode
95 BYTE offset;
96 BYTE code : 4;
97 BYTE info : 4;
100 struct UNWIND_INFO
102 BYTE version : 3;
103 BYTE flags : 5;
104 BYTE prolog;
105 BYTE count;
106 BYTE frame_reg : 4;
107 BYTE frame_offset : 4;
108 struct opcode opcodes[1]; /* info->count entries */
109 /* followed by handler_data */
112 #define UWOP_PUSH_NONVOL 0
113 #define UWOP_ALLOC_LARGE 1
114 #define UWOP_ALLOC_SMALL 2
115 #define UWOP_SET_FPREG 3
116 #define UWOP_SAVE_NONVOL 4
117 #define UWOP_SAVE_NONVOL_FAR 5
118 #define UWOP_EPILOG 6
119 #define UWOP_SAVE_XMM128 8
120 #define UWOP_SAVE_XMM128_FAR 9
121 #define UWOP_PUSH_MACHFRAME 10
123 static void dump_unwind_info( ULONG64 base, RUNTIME_FUNCTION *function )
125 static const char * const reg_names[16] =
126 { "rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi",
127 "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15" };
129 union handler_data *handler_data;
130 struct UNWIND_INFO *info;
131 unsigned int i, count;
133 TRACE( "**** func %x-%x\n", function->BeginAddress, function->EndAddress );
134 for (;;)
136 if (function->UnwindData & 1)
138 RUNTIME_FUNCTION *next = (RUNTIME_FUNCTION *)((char *)base + (function->UnwindData & ~1));
139 TRACE( "unwind info for function %p-%p chained to function %p-%p\n",
140 (char *)base + function->BeginAddress, (char *)base + function->EndAddress,
141 (char *)base + next->BeginAddress, (char *)base + next->EndAddress );
142 function = next;
143 continue;
145 info = (struct UNWIND_INFO *)((char *)base + function->UnwindData);
147 TRACE( "unwind info at %p flags %x prolog 0x%x bytes function %p-%p\n",
148 info, info->flags, info->prolog,
149 (char *)base + function->BeginAddress, (char *)base + function->EndAddress );
151 if (info->frame_reg)
152 TRACE( " frame register %s offset 0x%x(%%rsp)\n",
153 reg_names[info->frame_reg], info->frame_offset * 16 );
155 for (i = 0; i < info->count; i++)
157 TRACE( " 0x%x: ", info->opcodes[i].offset );
158 switch (info->opcodes[i].code)
160 case UWOP_PUSH_NONVOL:
161 TRACE( "pushq %%%s\n", reg_names[info->opcodes[i].info] );
162 break;
163 case UWOP_ALLOC_LARGE:
164 if (info->opcodes[i].info)
166 count = *(DWORD *)&info->opcodes[i+1];
167 i += 2;
169 else
171 count = *(USHORT *)&info->opcodes[i+1] * 8;
172 i++;
174 TRACE( "subq $0x%x,%%rsp\n", count );
175 break;
176 case UWOP_ALLOC_SMALL:
177 count = (info->opcodes[i].info + 1) * 8;
178 TRACE( "subq $0x%x,%%rsp\n", count );
179 break;
180 case UWOP_SET_FPREG:
181 TRACE( "leaq 0x%x(%%rsp),%s\n",
182 info->frame_offset * 16, reg_names[info->frame_reg] );
183 break;
184 case UWOP_SAVE_NONVOL:
185 count = *(USHORT *)&info->opcodes[i+1] * 8;
186 TRACE( "movq %%%s,0x%x(%%rsp)\n", reg_names[info->opcodes[i].info], count );
187 i++;
188 break;
189 case UWOP_SAVE_NONVOL_FAR:
190 count = *(DWORD *)&info->opcodes[i+1];
191 TRACE( "movq %%%s,0x%x(%%rsp)\n", reg_names[info->opcodes[i].info], count );
192 i += 2;
193 break;
194 case UWOP_SAVE_XMM128:
195 count = *(USHORT *)&info->opcodes[i+1] * 16;
196 TRACE( "movaps %%xmm%u,0x%x(%%rsp)\n", info->opcodes[i].info, count );
197 i++;
198 break;
199 case UWOP_SAVE_XMM128_FAR:
200 count = *(DWORD *)&info->opcodes[i+1];
201 TRACE( "movaps %%xmm%u,0x%x(%%rsp)\n", info->opcodes[i].info, count );
202 i += 2;
203 break;
204 case UWOP_PUSH_MACHFRAME:
205 TRACE( "PUSH_MACHFRAME %u\n", info->opcodes[i].info );
206 break;
207 case UWOP_EPILOG:
208 if (info->version == 2)
210 unsigned int offset;
211 if (info->opcodes[i].info)
212 offset = info->opcodes[i].offset;
213 else
214 offset = (info->opcodes[i+1].info << 8) + info->opcodes[i+1].offset;
215 TRACE("epilog %p-%p\n", (char *)base + function->EndAddress - offset,
216 (char *)base + function->EndAddress - offset + info->opcodes[i].offset );
217 i += 1;
218 break;
220 default:
221 FIXME( "unknown code %u\n", info->opcodes[i].code );
222 break;
226 handler_data = (union handler_data *)&info->opcodes[(info->count + 1) & ~1];
227 if (info->flags & UNW_FLAG_CHAININFO)
229 TRACE( " chained to function %p-%p\n",
230 (char *)base + handler_data->chain.BeginAddress,
231 (char *)base + handler_data->chain.EndAddress );
232 function = &handler_data->chain;
233 continue;
235 if (info->flags & (UNW_FLAG_EHANDLER | UNW_FLAG_UHANDLER))
236 TRACE( " handler %p data at %p\n",
237 (char *)base + handler_data->handler, &handler_data->handler + 1 );
238 break;
242 static void dump_scope_table( ULONG64 base, const SCOPE_TABLE *table )
244 unsigned int i;
246 TRACE( "scope table at %p\n", table );
247 for (i = 0; i < table->Count; i++)
248 TRACE( " %u: %p-%p handler %p target %p\n", i,
249 (char *)base + table->ScopeRecord[i].BeginAddress,
250 (char *)base + table->ScopeRecord[i].EndAddress,
251 (char *)base + table->ScopeRecord[i].HandlerAddress,
252 (char *)base + table->ScopeRecord[i].JumpTarget );
256 /***********************************************************************
257 * virtual_unwind
259 static NTSTATUS virtual_unwind( ULONG type, DISPATCHER_CONTEXT *dispatch, CONTEXT *context )
261 LDR_DATA_TABLE_ENTRY *module;
262 NTSTATUS status;
264 dispatch->ImageBase = 0;
265 dispatch->ScopeIndex = 0;
266 dispatch->ControlPc = context->Rip;
268 /* first look for PE exception information */
270 if ((dispatch->FunctionEntry = lookup_function_info( context->Rip, &dispatch->ImageBase, &module )))
272 dispatch->LanguageHandler = RtlVirtualUnwind( type, dispatch->ImageBase, context->Rip,
273 dispatch->FunctionEntry, context,
274 &dispatch->HandlerData, &dispatch->EstablisherFrame,
275 NULL );
276 return STATUS_SUCCESS;
279 /* then look for host system exception information */
281 if (!module || (module->Flags & LDR_WINE_INTERNAL))
283 status = unix_funcs->unwind_builtin_dll( type, dispatch, context );
285 if (!status && dispatch->LanguageHandler && !module)
287 FIXME( "calling personality routine in system library not supported yet\n" );
288 dispatch->LanguageHandler = NULL;
290 if (status != STATUS_UNSUCCESSFUL) return status;
292 else WARN( "exception data not found in %s\n", debugstr_w(module->BaseDllName.Buffer) );
294 /* no exception information, treat as a leaf function */
296 dispatch->EstablisherFrame = context->Rsp;
297 dispatch->LanguageHandler = NULL;
298 context->Rip = *(ULONG64 *)context->Rsp;
299 context->Rsp = context->Rsp + sizeof(ULONG64);
300 return STATUS_SUCCESS;
304 /**************************************************************************
305 * __chkstk (NTDLL.@)
307 * Supposed to touch all the stack pages, but we shouldn't need that.
309 __ASM_GLOBAL_FUNC( __chkstk, "ret" );
312 /***********************************************************************
313 * RtlCaptureContext (NTDLL.@)
315 __ASM_GLOBAL_FUNC( RtlCaptureContext,
316 "pushfq\n\t"
317 __ASM_CFI(".cfi_adjust_cfa_offset 8\n\t")
318 "movl $0x10000f,0x30(%rcx)\n\t" /* context->ContextFlags */
319 "stmxcsr 0x34(%rcx)\n\t" /* context->MxCsr */
320 "movw %cs,0x38(%rcx)\n\t" /* context->SegCs */
321 "movw %ds,0x3a(%rcx)\n\t" /* context->SegDs */
322 "movw %es,0x3c(%rcx)\n\t" /* context->SegEs */
323 "movw %fs,0x3e(%rcx)\n\t" /* context->SegFs */
324 "movw %gs,0x40(%rcx)\n\t" /* context->SegGs */
325 "movw %ss,0x42(%rcx)\n\t" /* context->SegSs */
326 "popq 0x44(%rcx)\n\t" /* context->Eflags */
327 __ASM_CFI(".cfi_adjust_cfa_offset -8\n\t")
328 "movq %rax,0x78(%rcx)\n\t" /* context->Rax */
329 "movq %rcx,0x80(%rcx)\n\t" /* context->Rcx */
330 "movq %rdx,0x88(%rcx)\n\t" /* context->Rdx */
331 "movq %rbx,0x90(%rcx)\n\t" /* context->Rbx */
332 "leaq 8(%rsp),%rax\n\t"
333 "movq %rax,0x98(%rcx)\n\t" /* context->Rsp */
334 "movq %rbp,0xa0(%rcx)\n\t" /* context->Rbp */
335 "movq %rsi,0xa8(%rcx)\n\t" /* context->Rsi */
336 "movq %rdi,0xb0(%rcx)\n\t" /* context->Rdi */
337 "movq %r8,0xb8(%rcx)\n\t" /* context->R8 */
338 "movq %r9,0xc0(%rcx)\n\t" /* context->R9 */
339 "movq %r10,0xc8(%rcx)\n\t" /* context->R10 */
340 "movq %r11,0xd0(%rcx)\n\t" /* context->R11 */
341 "movq %r12,0xd8(%rcx)\n\t" /* context->R12 */
342 "movq %r13,0xe0(%rcx)\n\t" /* context->R13 */
343 "movq %r14,0xe8(%rcx)\n\t" /* context->R14 */
344 "movq %r15,0xf0(%rcx)\n\t" /* context->R15 */
345 "movq (%rsp),%rax\n\t"
346 "movq %rax,0xf8(%rcx)\n\t" /* context->Rip */
347 "fxsave 0x100(%rcx)\n\t" /* context->FltSave */
348 "ret" );
350 static DWORD __cdecl nested_exception_handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
351 CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher )
353 if (!(rec->ExceptionFlags & (EH_UNWINDING | EH_EXIT_UNWIND)))
354 rec->ExceptionFlags |= EH_NESTED_CALL;
356 return ExceptionContinueSearch;
359 /**********************************************************************
360 * call_handler
362 * Call a single exception handler.
363 * FIXME: Handle nested exceptions.
365 static DWORD call_handler( EXCEPTION_RECORD *rec, CONTEXT *context, DISPATCHER_CONTEXT *dispatch )
367 EXCEPTION_REGISTRATION_RECORD frame;
368 DWORD res;
370 frame.Handler = nested_exception_handler;
371 __wine_push_frame( &frame );
373 TRACE_(seh)( "calling handler %p (rec=%p, frame=%p context=%p, dispatch=%p)\n",
374 dispatch->LanguageHandler, rec, (void *)dispatch->EstablisherFrame, dispatch->ContextRecord, dispatch );
375 res = dispatch->LanguageHandler( rec, (void *)dispatch->EstablisherFrame, context, dispatch );
376 TRACE_(seh)( "handler at %p returned %u\n", dispatch->LanguageHandler, res );
378 rec->ExceptionFlags &= EH_NONCONTINUABLE;
379 __wine_pop_frame( &frame );
380 return res;
384 /**********************************************************************
385 * call_teb_handler
387 * Call a single exception handler from the TEB chain.
388 * FIXME: Handle nested exceptions.
390 static DWORD call_teb_handler( EXCEPTION_RECORD *rec, CONTEXT *context, DISPATCHER_CONTEXT *dispatch,
391 EXCEPTION_REGISTRATION_RECORD *teb_frame )
393 DWORD res;
395 TRACE_(seh)( "calling TEB handler %p (rec=%p, frame=%p context=%p, dispatch=%p)\n",
396 teb_frame->Handler, rec, teb_frame, dispatch->ContextRecord, dispatch );
397 res = teb_frame->Handler( rec, teb_frame, context, (EXCEPTION_REGISTRATION_RECORD**)dispatch );
398 TRACE_(seh)( "handler at %p returned %u\n", teb_frame->Handler, res );
399 return res;
403 /**********************************************************************
404 * call_stack_handlers
406 * Call the stack handlers chain.
408 static NTSTATUS call_stack_handlers( EXCEPTION_RECORD *rec, CONTEXT *orig_context )
410 EXCEPTION_REGISTRATION_RECORD *teb_frame = NtCurrentTeb()->Tib.ExceptionList;
411 UNWIND_HISTORY_TABLE table;
412 DISPATCHER_CONTEXT dispatch;
413 CONTEXT context;
414 NTSTATUS status;
416 context = *orig_context;
417 context.ContextFlags &= ~0x40; /* Clear xstate flag. */
419 dispatch.TargetIp = 0;
420 dispatch.ContextRecord = &context;
421 dispatch.HistoryTable = &table;
422 for (;;)
424 status = virtual_unwind( UNW_FLAG_EHANDLER, &dispatch, &context );
425 if (status != STATUS_SUCCESS) return status;
427 unwind_done:
428 if (!dispatch.EstablisherFrame) break;
430 if ((dispatch.EstablisherFrame & 7) ||
431 dispatch.EstablisherFrame < (ULONG64)NtCurrentTeb()->Tib.StackLimit ||
432 dispatch.EstablisherFrame > (ULONG64)NtCurrentTeb()->Tib.StackBase)
434 ERR_(seh)( "invalid frame %p (%p-%p)\n", (void *)dispatch.EstablisherFrame,
435 NtCurrentTeb()->Tib.StackLimit, NtCurrentTeb()->Tib.StackBase );
436 rec->ExceptionFlags |= EH_STACK_INVALID;
437 break;
440 if (dispatch.LanguageHandler)
442 switch (call_handler( rec, orig_context, &dispatch ))
444 case ExceptionContinueExecution:
445 if (rec->ExceptionFlags & EH_NONCONTINUABLE) return STATUS_NONCONTINUABLE_EXCEPTION;
446 return STATUS_SUCCESS;
447 case ExceptionContinueSearch:
448 break;
449 case ExceptionNestedException:
450 FIXME_(seh)( "nested exception\n" );
451 break;
452 case ExceptionCollidedUnwind: {
453 ULONG64 frame;
455 context = *dispatch.ContextRecord;
456 dispatch.ContextRecord = &context;
457 RtlVirtualUnwind( UNW_FLAG_NHANDLER, dispatch.ImageBase,
458 dispatch.ControlPc, dispatch.FunctionEntry,
459 &context, NULL, &frame, NULL );
460 goto unwind_done;
462 default:
463 return STATUS_INVALID_DISPOSITION;
466 /* hack: call wine handlers registered in the tib list */
467 else while ((ULONG64)teb_frame < context.Rsp)
469 TRACE_(seh)( "found wine frame %p rsp %p handler %p\n",
470 teb_frame, (void *)context.Rsp, teb_frame->Handler );
471 dispatch.EstablisherFrame = (ULONG64)teb_frame;
472 switch (call_teb_handler( rec, orig_context, &dispatch, teb_frame ))
474 case ExceptionContinueExecution:
475 if (rec->ExceptionFlags & EH_NONCONTINUABLE) return STATUS_NONCONTINUABLE_EXCEPTION;
476 return STATUS_SUCCESS;
477 case ExceptionContinueSearch:
478 break;
479 case ExceptionNestedException:
480 FIXME_(seh)( "nested exception\n" );
481 break;
482 case ExceptionCollidedUnwind: {
483 ULONG64 frame;
485 context = *dispatch.ContextRecord;
486 dispatch.ContextRecord = &context;
487 RtlVirtualUnwind( UNW_FLAG_NHANDLER, dispatch.ImageBase,
488 dispatch.ControlPc, dispatch.FunctionEntry,
489 &context, NULL, &frame, NULL );
490 teb_frame = teb_frame->Prev;
491 goto unwind_done;
493 default:
494 return STATUS_INVALID_DISPOSITION;
496 teb_frame = teb_frame->Prev;
499 if (context.Rsp == (ULONG64)NtCurrentTeb()->Tib.StackBase) break;
501 return STATUS_UNHANDLED_EXCEPTION;
505 NTSTATUS WINAPI dispatch_exception( EXCEPTION_RECORD *rec, CONTEXT *context )
507 NTSTATUS status;
508 DWORD c;
510 TRACE_(seh)( "code=%x flags=%x addr=%p ip=%p tid=%04x\n",
511 rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress,
512 (void *)context->Rip, GetCurrentThreadId() );
513 for (c = 0; c < min( EXCEPTION_MAXIMUM_PARAMETERS, rec->NumberParameters ); c++)
514 TRACE_(seh)( " info[%d]=%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)
530 WARN_(threadname)( "Thread renamed to %s\n", debugstr_a((char *)rec->ExceptionInformation[1]) );
531 else
532 WARN_(threadname)( "Thread ID %04x renamed to %s\n", (DWORD)rec->ExceptionInformation[2],
533 debugstr_a((char *)rec->ExceptionInformation[1]) );
535 else if (rec->ExceptionCode == DBG_PRINTEXCEPTION_C)
537 WARN_(seh)( "%s\n", debugstr_an((char *)rec->ExceptionInformation[1], rec->ExceptionInformation[0] - 1) );
539 else if (rec->ExceptionCode == DBG_PRINTEXCEPTION_WIDE_C)
541 WARN_(seh)( "%s\n", debugstr_wn((WCHAR *)rec->ExceptionInformation[1], rec->ExceptionInformation[0] - 1) );
543 else
545 if (rec->ExceptionCode == STATUS_ASSERTION_FAILURE)
546 ERR_(seh)( "%s exception (code=%x) raised\n", debugstr_exception_code(rec->ExceptionCode), rec->ExceptionCode );
547 else
548 WARN_(seh)( "%s exception (code=%x) raised\n", debugstr_exception_code(rec->ExceptionCode), rec->ExceptionCode );
550 TRACE_(seh)( " rax=%016I64x rbx=%016I64x rcx=%016I64x rdx=%016I64x\n",
551 context->Rax, context->Rbx, context->Rcx, context->Rdx );
552 TRACE_(seh)( " rsi=%016I64x rdi=%016I64x rbp=%016I64x rsp=%016I64x\n",
553 context->Rsi, context->Rdi, context->Rbp, context->Rsp );
554 TRACE_(seh)( " r8=%016I64x r9=%016I64x r10=%016I64x r11=%016I64x\n",
555 context->R8, context->R9, context->R10, context->R11 );
556 TRACE_(seh)( " r12=%016I64x r13=%016I64x r14=%016I64x r15=%016I64x\n",
557 context->R12, context->R13, context->R14, context->R15 );
560 /* Legends of Runeterra depends on having SegDs == SegSs in an exception
561 * handler. */
562 context->SegDs = context->SegSs;
564 if (call_vectored_handlers( rec, context ) == EXCEPTION_CONTINUE_EXECUTION)
565 NtContinue( context, FALSE );
567 if ((status = call_stack_handlers( rec, context )) == STATUS_SUCCESS)
568 NtContinue( context, FALSE );
570 if (status != STATUS_UNHANDLED_EXCEPTION) RtlRaiseStatus( status );
571 return NtRaiseException( rec, context, FALSE );
575 NTSTATUS WINAPI dispatch_wow_exception( EXCEPTION_RECORD *rec_ptr, CONTEXT *context_ptr )
577 char buffer[sizeof(CONTEXT) + sizeof(CONTEXT_EX) + sizeof(XSTATE) + 128];
578 CONTEXT *context;
579 CONTEXT_EX *context_ex;
580 EXCEPTION_RECORD rec = *rec_ptr;
582 RtlInitializeExtendedContext( buffer, context_ptr->ContextFlags, &context_ex );
583 context = RtlLocateLegacyContext( context_ex, NULL );
584 RtlCopyContext( context, context_ptr->ContextFlags, context_ptr );
585 pWow64PrepareForException( &rec, context );
586 return dispatch_exception( &rec, context );
590 /*******************************************************************
591 * KiUserExceptionDispatcher (NTDLL.@)
593 __ASM_GLOBAL_FUNC( KiUserExceptionDispatcher,
594 "mov 0x98(%rsp),%rcx\n\t" /* context->Rsp */
595 "movw %cs,%ax\n\t"
596 "cmpw %ax,0x38(%rsp)\n\t" /* context->SegCs */
597 "je 1f\n\t"
598 "mov %rsp,%rdx\n\t" /* context */
599 "lea 0x4f0(%rsp),%rcx\n\t" /* rec */
600 "movq %r14,%rsp\n\t" /* switch to 64-bit stack */
601 "call " __ASM_NAME("dispatch_wow_exception") "\n\t"
602 "int3\n"
603 "1:\tmov 0xf8(%rsp),%rdx\n\t" /* context->Rip */
604 "mov %rdx,-0x8(%rcx)\n\t"
605 "mov %rbp,-0x10(%rcx)\n\t"
606 "mov %rdi,-0x18(%rcx)\n\t"
607 "mov %rsi,-0x20(%rcx)\n\t"
608 "lea -0x20(%rcx),%rbp\n\t"
609 "mov %rsp,%rdx\n\t" /* context */
610 "lea 0x4f0(%rsp),%rcx\n\t" /* rec */
611 __ASM_SEH(".seh_pushreg %rbp\n\t")
612 __ASM_SEH(".seh_pushreg %rdi\n\t")
613 __ASM_SEH(".seh_pushreg %rsi\n\t")
614 __ASM_SEH(".seh_setframe %rbp,0\n\t")
615 __ASM_SEH(".seh_endprologue\n\t")
617 __ASM_CFI(".cfi_signal_frame\n\t")
618 __ASM_CFI(".cfi_adjust_cfa_offset 0x20\n\t")
619 __ASM_CFI(".cfi_def_cfa %rbp,0x20\n\t")
620 __ASM_CFI(".cfi_rel_offset %rip,0x18\n\t")
621 __ASM_CFI(".cfi_rel_offset %rbp,0x10\n\t")
622 __ASM_CFI(".cfi_rel_offset %rdi,0x8\n\t")
623 __ASM_CFI(".cfi_rel_offset %rsi,0\n\t")
624 "call " __ASM_NAME("dispatch_exception") "\n\t"
625 "int3")
628 /*******************************************************************
629 * KiUserApcDispatcher (NTDLL.@)
631 void WINAPI dispatch_apc( CONTEXT *context, ULONG_PTR arg1, ULONG_PTR arg2, ULONG_PTR arg3,
632 void (CALLBACK *func)(ULONG_PTR,ULONG_PTR,ULONG_PTR,CONTEXT*) )
634 func( arg1, arg2, arg3, context );
635 NtContinue( context, TRUE );
638 __ASM_GLOBAL_FUNC( KiUserApcDispatcher,
639 "addq $0x8,%rsp\n\t"
640 "mov 0x98(%rcx),%r10\n\t" /* context->Rsp */
641 "mov 0xf8(%rcx),%r11\n\t" /* context->Rip */
642 "mov %r11,-0x8(%r10)\n\t"
643 "mov %rbp,-0x10(%r10)\n\t"
644 "lea -0x10(%r10),%rbp\n\t"
645 __ASM_SEH(".seh_pushreg %rbp\n\t")
646 __ASM_SEH(".seh_setframe %rbp,0\n\t")
647 __ASM_SEH(".seh_endprologue\n\t")
648 __ASM_CFI(".cfi_signal_frame\n\t")
649 __ASM_CFI(".cfi_adjust_cfa_offset 0x10\n\t")
650 __ASM_CFI(".cfi_def_cfa %rbp,0x10\n\t")
651 __ASM_CFI(".cfi_rel_offset %rip,0x8\n\t")
652 __ASM_CFI(".cfi_rel_offset %rbp,0\n\t")
653 "call " __ASM_NAME("dispatch_apc") "\n\t"
654 "int3")
657 /*******************************************************************
658 * KiUserCallbackDispatcher (NTDLL.@)
660 * FIXME: not binary compatible
662 void WINAPI KiUserCallbackDispatcher( ULONG id, void *args, ULONG len )
664 NTSTATUS status;
666 __TRY
668 NTSTATUS (WINAPI *func)(void *, ULONG) = ((void **)NtCurrentTeb()->Peb->KernelCallbackTable)[id];
669 status = NtCallbackReturn( NULL, 0, func( args, len ));
671 __EXCEPT_ALL
673 ERR_(seh)( "ignoring exception\n" );
674 status = NtCallbackReturn( 0, 0, 0 );
676 __ENDTRY
678 RtlRaiseStatus( status );
682 static ULONG64 get_int_reg( CONTEXT *context, int reg )
684 return *(&context->Rax + reg);
687 static void set_int_reg( CONTEXT *context, KNONVOLATILE_CONTEXT_POINTERS *ctx_ptr, int reg, ULONG64 *val )
689 *(&context->Rax + reg) = *val;
690 if (ctx_ptr) ctx_ptr->u2.IntegerContext[reg] = val;
693 static void set_float_reg( CONTEXT *context, KNONVOLATILE_CONTEXT_POINTERS *ctx_ptr, int reg, M128A *val )
695 /* Use a memcpy() to avoid issues if val is misaligned. */
696 memcpy(&context->u.s.Xmm0 + reg, val, sizeof(*val));
697 if (ctx_ptr) ctx_ptr->u.FloatingContext[reg] = val;
700 static int get_opcode_size( struct opcode op )
702 switch (op.code)
704 case UWOP_ALLOC_LARGE:
705 return 2 + (op.info != 0);
706 case UWOP_SAVE_NONVOL:
707 case UWOP_SAVE_XMM128:
708 case UWOP_EPILOG:
709 return 2;
710 case UWOP_SAVE_NONVOL_FAR:
711 case UWOP_SAVE_XMM128_FAR:
712 return 3;
713 default:
714 return 1;
718 static BOOL is_inside_epilog( BYTE *pc, ULONG64 base, const RUNTIME_FUNCTION *function )
720 /* add or lea must be the first instruction, and it must have a rex.W prefix */
721 if ((pc[0] & 0xf8) == 0x48)
723 switch (pc[1])
725 case 0x81: /* add $nnnn,%rsp */
726 if (pc[0] == 0x48 && pc[2] == 0xc4)
728 pc += 7;
729 break;
731 return FALSE;
732 case 0x83: /* add $n,%rsp */
733 if (pc[0] == 0x48 && pc[2] == 0xc4)
735 pc += 4;
736 break;
738 return FALSE;
739 case 0x8d: /* lea n(reg),%rsp */
740 if (pc[0] & 0x06) return FALSE; /* rex.RX must be cleared */
741 if (((pc[2] >> 3) & 7) != 4) return FALSE; /* dest reg mus be %rsp */
742 if ((pc[2] & 7) == 4) return FALSE; /* no SIB byte allowed */
743 if ((pc[2] >> 6) == 1) /* 8-bit offset */
745 pc += 4;
746 break;
748 if ((pc[2] >> 6) == 2) /* 32-bit offset */
750 pc += 7;
751 break;
753 return FALSE;
757 /* now check for various pop instructions */
759 for (;;)
761 if ((*pc & 0xf0) == 0x40) pc++; /* rex prefix */
763 switch (*pc)
765 case 0x58: /* pop %rax/%r8 */
766 case 0x59: /* pop %rcx/%r9 */
767 case 0x5a: /* pop %rdx/%r10 */
768 case 0x5b: /* pop %rbx/%r11 */
769 case 0x5c: /* pop %rsp/%r12 */
770 case 0x5d: /* pop %rbp/%r13 */
771 case 0x5e: /* pop %rsi/%r14 */
772 case 0x5f: /* pop %rdi/%r15 */
773 pc++;
774 continue;
775 case 0xc2: /* ret $nn */
776 case 0xc3: /* ret */
777 return TRUE;
778 case 0xe9: /* jmp nnnn */
779 pc += 5 + *(LONG *)(pc + 1);
780 if (pc - (BYTE *)base >= function->BeginAddress && pc - (BYTE *)base < function->EndAddress)
781 continue;
782 break;
783 case 0xeb: /* jmp n */
784 pc += 2 + (signed char)pc[1];
785 if (pc - (BYTE *)base >= function->BeginAddress && pc - (BYTE *)base < function->EndAddress)
786 continue;
787 break;
788 case 0xf3: /* rep; ret (for amd64 prediction bug) */
789 return pc[1] == 0xc3;
791 return FALSE;
795 /* execute a function epilog, which must have been validated with is_inside_epilog() */
796 static void interpret_epilog( BYTE *pc, CONTEXT *context, KNONVOLATILE_CONTEXT_POINTERS *ctx_ptr )
798 for (;;)
800 BYTE rex = 0;
802 if ((*pc & 0xf0) == 0x40) rex = *pc++ & 0x0f; /* rex prefix */
804 switch (*pc)
806 case 0x58: /* pop %rax/r8 */
807 case 0x59: /* pop %rcx/r9 */
808 case 0x5a: /* pop %rdx/r10 */
809 case 0x5b: /* pop %rbx/r11 */
810 case 0x5c: /* pop %rsp/r12 */
811 case 0x5d: /* pop %rbp/r13 */
812 case 0x5e: /* pop %rsi/r14 */
813 case 0x5f: /* pop %rdi/r15 */
814 set_int_reg( context, ctx_ptr, *pc - 0x58 + (rex & 1) * 8, (ULONG64 *)context->Rsp );
815 context->Rsp += sizeof(ULONG64);
816 pc++;
817 continue;
818 case 0x81: /* add $nnnn,%rsp */
819 context->Rsp += *(LONG *)(pc + 2);
820 pc += 2 + sizeof(LONG);
821 continue;
822 case 0x83: /* add $n,%rsp */
823 context->Rsp += (signed char)pc[2];
824 pc += 3;
825 continue;
826 case 0x8d:
827 if ((pc[1] >> 6) == 1) /* lea n(reg),%rsp */
829 context->Rsp = get_int_reg( context, (pc[1] & 7) + (rex & 1) * 8 ) + (signed char)pc[2];
830 pc += 3;
832 else /* lea nnnn(reg),%rsp */
834 context->Rsp = get_int_reg( context, (pc[1] & 7) + (rex & 1) * 8 ) + *(LONG *)(pc + 2);
835 pc += 2 + sizeof(LONG);
837 continue;
838 case 0xc2: /* ret $nn */
839 context->Rip = *(ULONG64 *)context->Rsp;
840 context->Rsp += sizeof(ULONG64) + *(WORD *)(pc + 1);
841 return;
842 case 0xc3: /* ret */
843 case 0xf3: /* rep; ret */
844 context->Rip = *(ULONG64 *)context->Rsp;
845 context->Rsp += sizeof(ULONG64);
846 return;
847 case 0xe9: /* jmp nnnn */
848 pc += 5 + *(LONG *)(pc + 1);
849 continue;
850 case 0xeb: /* jmp n */
851 pc += 2 + (signed char)pc[1];
852 continue;
854 return;
858 /**********************************************************************
859 * RtlVirtualUnwind (NTDLL.@)
861 PVOID WINAPI RtlVirtualUnwind( ULONG type, ULONG64 base, ULONG64 pc,
862 RUNTIME_FUNCTION *function, CONTEXT *context,
863 PVOID *data, ULONG64 *frame_ret,
864 KNONVOLATILE_CONTEXT_POINTERS *ctx_ptr )
866 union handler_data *handler_data;
867 ULONG64 frame, off;
868 struct UNWIND_INFO *info;
869 unsigned int i, prolog_offset;
870 BOOL mach_frame = FALSE;
872 TRACE( "type %x rip %p rsp %p\n", type, (void *)pc, (void *)context->Rsp );
873 if (TRACE_ON(seh)) dump_unwind_info( base, function );
875 frame = *frame_ret = context->Rsp;
876 for (;;)
878 info = (struct UNWIND_INFO *)((char *)base + function->UnwindData);
879 handler_data = (union handler_data *)&info->opcodes[(info->count + 1) & ~1];
881 if (info->version != 1 && info->version != 2)
883 FIXME( "unknown unwind info version %u at %p\n", info->version, info );
884 return NULL;
887 if (info->frame_reg)
888 frame = get_int_reg( context, info->frame_reg ) - info->frame_offset * 16;
890 /* check if in prolog */
891 if (pc >= base + function->BeginAddress && pc < base + function->BeginAddress + info->prolog)
893 TRACE("inside prolog.\n");
894 prolog_offset = pc - base - function->BeginAddress;
896 else
898 prolog_offset = ~0;
899 /* Since Win10 1809 epilogue does not have a special treatment in case of zero opcode count. */
900 if (info->count && is_inside_epilog( (BYTE *)pc, base, function ))
902 TRACE("inside epilog.\n");
903 interpret_epilog( (BYTE *)pc, context, ctx_ptr );
904 *frame_ret = frame;
905 return NULL;
909 for (i = 0; i < info->count; i += get_opcode_size(info->opcodes[i]))
911 if (prolog_offset < info->opcodes[i].offset) continue; /* skip it */
913 switch (info->opcodes[i].code)
915 case UWOP_PUSH_NONVOL: /* pushq %reg */
916 set_int_reg( context, ctx_ptr, info->opcodes[i].info, (ULONG64 *)context->Rsp );
917 context->Rsp += sizeof(ULONG64);
918 break;
919 case UWOP_ALLOC_LARGE: /* subq $nn,%rsp */
920 if (info->opcodes[i].info) context->Rsp += *(DWORD *)&info->opcodes[i+1];
921 else context->Rsp += *(USHORT *)&info->opcodes[i+1] * 8;
922 break;
923 case UWOP_ALLOC_SMALL: /* subq $n,%rsp */
924 context->Rsp += (info->opcodes[i].info + 1) * 8;
925 break;
926 case UWOP_SET_FPREG: /* leaq nn(%rsp),%framereg */
927 context->Rsp = *frame_ret = frame;
928 break;
929 case UWOP_SAVE_NONVOL: /* movq %reg,n(%rsp) */
930 off = frame + *(USHORT *)&info->opcodes[i+1] * 8;
931 set_int_reg( context, ctx_ptr, info->opcodes[i].info, (ULONG64 *)off );
932 break;
933 case UWOP_SAVE_NONVOL_FAR: /* movq %reg,nn(%rsp) */
934 off = frame + *(DWORD *)&info->opcodes[i+1];
935 set_int_reg( context, ctx_ptr, info->opcodes[i].info, (ULONG64 *)off );
936 break;
937 case UWOP_SAVE_XMM128: /* movaps %xmmreg,n(%rsp) */
938 off = frame + *(USHORT *)&info->opcodes[i+1] * 16;
939 set_float_reg( context, ctx_ptr, info->opcodes[i].info, (M128A *)off );
940 break;
941 case UWOP_SAVE_XMM128_FAR: /* movaps %xmmreg,nn(%rsp) */
942 off = frame + *(DWORD *)&info->opcodes[i+1];
943 set_float_reg( context, ctx_ptr, info->opcodes[i].info, (M128A *)off );
944 break;
945 case UWOP_PUSH_MACHFRAME:
946 if (info->flags & UNW_FLAG_CHAININFO)
948 FIXME("PUSH_MACHFRAME with chained unwind info.\n");
949 break;
951 if (i + get_opcode_size(info->opcodes[i]) < info->count )
953 FIXME("PUSH_MACHFRAME is not the last opcode.\n");
954 break;
957 if (info->opcodes[i].info)
958 context->Rsp += 0x8;
960 context->Rip = *(ULONG64 *)context->Rsp;
961 context->Rsp = *(ULONG64 *)(context->Rsp + 24);
962 mach_frame = TRUE;
963 break;
964 case UWOP_EPILOG:
965 if (info->version == 2)
966 break; /* nothing to do */
967 default:
968 FIXME( "unknown code %u\n", info->opcodes[i].code );
969 break;
973 if (!(info->flags & UNW_FLAG_CHAININFO)) break;
974 function = &handler_data->chain; /* restart with the chained info */
977 if (!mach_frame)
979 /* now pop return address */
980 context->Rip = *(ULONG64 *)context->Rsp;
981 context->Rsp += sizeof(ULONG64);
984 if (!(info->flags & type)) return NULL; /* no matching handler */
985 if (prolog_offset != ~0) return NULL; /* inside prolog */
987 *data = &handler_data->handler + 1;
988 return (char *)base + handler_data->handler;
991 struct unwind_exception_frame
993 EXCEPTION_REGISTRATION_RECORD frame;
994 DISPATCHER_CONTEXT *dispatch;
997 /**********************************************************************
998 * unwind_exception_handler
1000 * Handler for exceptions happening while calling an unwind handler.
1002 static DWORD __cdecl unwind_exception_handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
1003 CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher )
1005 struct unwind_exception_frame *unwind_frame = (struct unwind_exception_frame *)frame;
1006 DISPATCHER_CONTEXT *dispatch = (DISPATCHER_CONTEXT *)dispatcher;
1008 /* copy the original dispatcher into the current one, except for the TargetIp */
1009 dispatch->ControlPc = unwind_frame->dispatch->ControlPc;
1010 dispatch->ImageBase = unwind_frame->dispatch->ImageBase;
1011 dispatch->FunctionEntry = unwind_frame->dispatch->FunctionEntry;
1012 dispatch->EstablisherFrame = unwind_frame->dispatch->EstablisherFrame;
1013 dispatch->ContextRecord = unwind_frame->dispatch->ContextRecord;
1014 dispatch->LanguageHandler = unwind_frame->dispatch->LanguageHandler;
1015 dispatch->HandlerData = unwind_frame->dispatch->HandlerData;
1016 dispatch->HistoryTable = unwind_frame->dispatch->HistoryTable;
1017 dispatch->ScopeIndex = unwind_frame->dispatch->ScopeIndex;
1018 TRACE( "detected collided unwind\n" );
1019 return ExceptionCollidedUnwind;
1022 /**********************************************************************
1023 * call_unwind_handler
1025 * Call a single unwind handler.
1027 static DWORD call_unwind_handler( EXCEPTION_RECORD *rec, DISPATCHER_CONTEXT *dispatch )
1029 struct unwind_exception_frame frame;
1030 DWORD res;
1032 frame.frame.Handler = unwind_exception_handler;
1033 frame.dispatch = dispatch;
1034 __wine_push_frame( &frame.frame );
1036 TRACE( "calling handler %p (rec=%p, frame=%p context=%p, dispatch=%p)\n",
1037 dispatch->LanguageHandler, rec, (void *)dispatch->EstablisherFrame, dispatch->ContextRecord, dispatch );
1038 res = dispatch->LanguageHandler( rec, (void *)dispatch->EstablisherFrame, dispatch->ContextRecord, dispatch );
1039 TRACE( "handler %p returned %x\n", dispatch->LanguageHandler, res );
1041 __wine_pop_frame( &frame.frame );
1043 switch (res)
1045 case ExceptionContinueSearch:
1046 case ExceptionCollidedUnwind:
1047 break;
1048 default:
1049 raise_status( STATUS_INVALID_DISPOSITION, rec );
1050 break;
1053 return res;
1057 /**********************************************************************
1058 * call_teb_unwind_handler
1060 * Call a single unwind handler from the TEB chain.
1062 static DWORD call_teb_unwind_handler( EXCEPTION_RECORD *rec, DISPATCHER_CONTEXT *dispatch,
1063 EXCEPTION_REGISTRATION_RECORD *teb_frame )
1065 DWORD res;
1067 TRACE( "calling TEB handler %p (rec=%p, frame=%p context=%p, dispatch=%p)\n",
1068 teb_frame->Handler, rec, teb_frame, dispatch->ContextRecord, dispatch );
1069 res = teb_frame->Handler( rec, teb_frame, dispatch->ContextRecord, (EXCEPTION_REGISTRATION_RECORD**)dispatch );
1070 TRACE( "handler at %p returned %u\n", teb_frame->Handler, res );
1072 switch (res)
1074 case ExceptionContinueSearch:
1075 case ExceptionCollidedUnwind:
1076 break;
1077 default:
1078 raise_status( STATUS_INVALID_DISPOSITION, rec );
1079 break;
1082 return res;
1086 /**********************************************************************
1087 * call_consolidate_callback
1089 * Wrapper function to call a consolidate callback from a fake frame.
1090 * If the callback executes RtlUnwindEx (like for example done in C++ handlers),
1091 * we have to skip all frames which were already processed. To do that we
1092 * trick the unwinding functions into thinking the call came from the specified
1093 * context. All CFI instructions are either DW_CFA_def_cfa_expression or
1094 * DW_CFA_expression, and the expressions have the following format:
1096 * DW_OP_breg6; sleb128 0x10 | Load %rbp + 0x10
1097 * DW_OP_deref | Get *(%rbp + 0x10) == context
1098 * DW_OP_plus_uconst; uleb128 <OFFSET> | Add offset to get struct member
1099 * [DW_OP_deref] | Dereference, only for CFA
1101 extern void * WINAPI call_consolidate_callback( CONTEXT *context,
1102 void *(CALLBACK *callback)(EXCEPTION_RECORD *),
1103 EXCEPTION_RECORD *rec );
1104 __ASM_GLOBAL_FUNC( call_consolidate_callback,
1105 "pushq %rbp\n\t"
1106 __ASM_CFI(".cfi_adjust_cfa_offset 8\n\t")
1107 __ASM_CFI(".cfi_rel_offset %rbp,0\n\t")
1108 "movq %rsp,%rbp\n\t"
1109 __ASM_CFI(".cfi_def_cfa_register %rbp\n\t")
1111 /* Setup SEH machine frame. */
1112 "subq $0x28,%rsp\n\t"
1113 __ASM_CFI(".cfi_adjust_cfa_offset 0x28\n\t")
1114 "movq 0xf8(%rcx),%rax\n\t" /* Context->Rip */
1115 "movq %rax,(%rsp)\n\t"
1116 "movq 0x98(%rcx),%rax\n\t" /* context->Rsp */
1117 "movq %rax,0x18(%rsp)\n\t"
1118 __ASM_SEH(".seh_pushframe\n\t")
1119 __ASM_SEH(".seh_endprologue\n\t")
1121 "subq $0x108,%rsp\n\t" /* 10*16 (float regs) + 8*8 (int regs) + 32 (shadow store) + 8 (align). */
1122 __ASM_SEH(".seh_stackalloc 0x108\n\t")
1123 __ASM_CFI(".cfi_adjust_cfa_offset 0x108\n\t")
1125 /* Setup CFI unwind to context. */
1126 "movq %rcx,0x10(%rbp)\n\t"
1127 __ASM_CFI(".cfi_remember_state\n\t")
1128 __ASM_CFI(".cfi_escape 0x0f,0x07,0x76,0x10,0x06,0x23,0x98,0x01,0x06\n\t") /* CFA */
1129 __ASM_CFI(".cfi_escape 0x10,0x03,0x06,0x76,0x10,0x06,0x23,0x90,0x01\n\t") /* %rbx */
1130 __ASM_CFI(".cfi_escape 0x10,0x04,0x06,0x76,0x10,0x06,0x23,0xa8,0x01\n\t") /* %rsi */
1131 __ASM_CFI(".cfi_escape 0x10,0x05,0x06,0x76,0x10,0x06,0x23,0xb0,0x01\n\t") /* %rdi */
1132 __ASM_CFI(".cfi_escape 0x10,0x06,0x06,0x76,0x10,0x06,0x23,0xa0,0x01\n\t") /* %rbp */
1133 __ASM_CFI(".cfi_escape 0x10,0x0c,0x06,0x76,0x10,0x06,0x23,0xd8,0x01\n\t") /* %r12 */
1134 __ASM_CFI(".cfi_escape 0x10,0x0d,0x06,0x76,0x10,0x06,0x23,0xe0,0x01\n\t") /* %r13 */
1135 __ASM_CFI(".cfi_escape 0x10,0x0e,0x06,0x76,0x10,0x06,0x23,0xe8,0x01\n\t") /* %r14 */
1136 __ASM_CFI(".cfi_escape 0x10,0x0f,0x06,0x76,0x10,0x06,0x23,0xf0,0x01\n\t") /* %r15 */
1137 __ASM_CFI(".cfi_escape 0x10,0x10,0x06,0x76,0x10,0x06,0x23,0xf8,0x01\n\t") /* %rip */
1138 __ASM_CFI(".cfi_escape 0x10,0x17,0x06,0x76,0x10,0x06,0x23,0x80,0x04\n\t") /* %xmm6 */
1139 __ASM_CFI(".cfi_escape 0x10,0x18,0x06,0x76,0x10,0x06,0x23,0x90,0x04\n\t") /* %xmm7 */
1140 __ASM_CFI(".cfi_escape 0x10,0x19,0x06,0x76,0x10,0x06,0x23,0xa0,0x04\n\t") /* %xmm8 */
1141 __ASM_CFI(".cfi_escape 0x10,0x1a,0x06,0x76,0x10,0x06,0x23,0xb0,0x04\n\t") /* %xmm9 */
1142 __ASM_CFI(".cfi_escape 0x10,0x1b,0x06,0x76,0x10,0x06,0x23,0xc0,0x04\n\t") /* %xmm10 */
1143 __ASM_CFI(".cfi_escape 0x10,0x1c,0x06,0x76,0x10,0x06,0x23,0xd0,0x04\n\t") /* %xmm11 */
1144 __ASM_CFI(".cfi_escape 0x10,0x1d,0x06,0x76,0x10,0x06,0x23,0xe0,0x04\n\t") /* %xmm12 */
1145 __ASM_CFI(".cfi_escape 0x10,0x1e,0x06,0x76,0x10,0x06,0x23,0xf0,0x04\n\t") /* %xmm13 */
1146 __ASM_CFI(".cfi_escape 0x10,0x1f,0x06,0x76,0x10,0x06,0x23,0x80,0x05\n\t") /* %xmm14 */
1147 __ASM_CFI(".cfi_escape 0x10,0x20,0x06,0x76,0x10,0x06,0x23,0x90,0x05\n\t") /* %xmm15 */
1149 /* Setup SEH unwind registers restore. */
1150 "movq 0xa0(%rcx),%rax\n\t" /* context->Rbp */
1151 "movq %rax,0x100(%rsp)\n\t"
1152 __ASM_SEH(".seh_savereg %rbp, 0x100\n\t")
1153 "movq 0x90(%rcx),%rax\n\t" /* context->Rbx */
1154 "movq %rax,0x20(%rsp)\n\t"
1155 __ASM_SEH(".seh_savereg %rbx, 0x20\n\t")
1156 "movq 0xa8(%rcx),%rax\n\t" /* context->Rsi */
1157 "movq %rax,0x28(%rsp)\n\t"
1158 __ASM_SEH(".seh_savereg %rsi, 0x28\n\t")
1159 "movq 0xb0(%rcx),%rax\n\t" /* context->Rdi */
1160 "movq %rax,0x30(%rsp)\n\t"
1161 __ASM_SEH(".seh_savereg %rdi, 0x30\n\t")
1163 "movq 0xd8(%rcx),%rax\n\t" /* context->R12 */
1164 "movq %rax,0x38(%rsp)\n\t"
1165 __ASM_SEH(".seh_savereg %r12, 0x38\n\t")
1166 "movq 0xe0(%rcx),%rax\n\t" /* context->R13 */
1167 "movq %rax,0x40(%rsp)\n\t"
1168 __ASM_SEH(".seh_savereg %r13, 0x40\n\t")
1169 "movq 0xe8(%rcx),%rax\n\t" /* context->R14 */
1170 "movq %rax,0x48(%rsp)\n\t"
1171 __ASM_SEH(".seh_savereg %r14, 0x48\n\t")
1172 "movq 0xf0(%rcx),%rax\n\t" /* context->R15 */
1173 "movq %rax,0x50(%rsp)\n\t"
1174 __ASM_SEH(".seh_savereg %r15, 0x50\n\t")
1175 "pushq %rsi\n\t"
1176 "pushq %rdi\n\t"
1177 "leaq 0x200(%rcx),%rsi\n\t"
1178 "leaq 0x70(%rsp),%rdi\n\t"
1179 "movq $0x14,%rcx\n\t"
1180 "cld\n\t"
1181 "rep; movsq\n\t"
1182 "popq %rdi\n\t"
1183 "popq %rsi\n\t"
1184 __ASM_SEH(".seh_savexmm %xmm6, 0x60\n\t")
1185 __ASM_SEH(".seh_savexmm %xmm7, 0x70\n\t")
1186 __ASM_SEH(".seh_savexmm %xmm8, 0x80\n\t")
1187 __ASM_SEH(".seh_savexmm %xmm9, 0x90\n\t")
1188 __ASM_SEH(".seh_savexmm %xmm10, 0xa0\n\t")
1189 __ASM_SEH(".seh_savexmm %xmm11, 0xb0\n\t")
1190 __ASM_SEH(".seh_savexmm %xmm12, 0xc0\n\t")
1191 __ASM_SEH(".seh_savexmm %xmm13, 0xd0\n\t")
1192 __ASM_SEH(".seh_savexmm %xmm14, 0xe0\n\t")
1193 __ASM_SEH(".seh_savexmm %xmm15, 0xf0\n\t")
1195 /* call the callback. */
1196 "movq %r8,%rcx\n\t"
1197 "callq *%rdx\n\t"
1198 __ASM_CFI(".cfi_restore_state\n\t")
1199 "nop\n\t" /* Otherwise RtlVirtualUnwind() will think we are inside epilogue and
1200 * interpret / execute the rest of opcodes here instead of unwind through
1201 * machine frame. */
1202 "leaq 0(%rbp),%rsp\n\t"
1203 __ASM_CFI(".cfi_def_cfa_register %rsp\n\t")
1204 "popq %rbp\n\t"
1205 __ASM_CFI(".cfi_adjust_cfa_offset -8\n\t")
1206 __ASM_CFI(".cfi_same_value %rbp\n\t")
1207 "ret")
1209 /*******************************************************************
1210 * RtlRestoreContext (NTDLL.@)
1212 void CDECL RtlRestoreContext( CONTEXT *context, EXCEPTION_RECORD *rec )
1214 EXCEPTION_REGISTRATION_RECORD *teb_frame = NtCurrentTeb()->Tib.ExceptionList;
1216 if (rec && rec->ExceptionCode == STATUS_LONGJUMP && rec->NumberParameters >= 1)
1218 struct MSVCRT_JUMP_BUFFER *jmp = (struct MSVCRT_JUMP_BUFFER *)rec->ExceptionInformation[0];
1219 context->Rbx = jmp->Rbx;
1220 context->Rsp = jmp->Rsp;
1221 context->Rbp = jmp->Rbp;
1222 context->Rsi = jmp->Rsi;
1223 context->Rdi = jmp->Rdi;
1224 context->R12 = jmp->R12;
1225 context->R13 = jmp->R13;
1226 context->R14 = jmp->R14;
1227 context->R15 = jmp->R15;
1228 context->Rip = jmp->Rip;
1229 context->u.s.Xmm6 = jmp->Xmm6;
1230 context->u.s.Xmm7 = jmp->Xmm7;
1231 context->u.s.Xmm8 = jmp->Xmm8;
1232 context->u.s.Xmm9 = jmp->Xmm9;
1233 context->u.s.Xmm10 = jmp->Xmm10;
1234 context->u.s.Xmm11 = jmp->Xmm11;
1235 context->u.s.Xmm12 = jmp->Xmm12;
1236 context->u.s.Xmm13 = jmp->Xmm13;
1237 context->u.s.Xmm14 = jmp->Xmm14;
1238 context->u.s.Xmm15 = jmp->Xmm15;
1239 context->MxCsr = jmp->MxCsr;
1240 context->u.FltSave.MxCsr = jmp->MxCsr;
1241 context->u.FltSave.ControlWord = jmp->FpCsr;
1243 else if (rec && rec->ExceptionCode == STATUS_UNWIND_CONSOLIDATE && rec->NumberParameters >= 1)
1245 PVOID (CALLBACK *consolidate)(EXCEPTION_RECORD *) = (void *)rec->ExceptionInformation[0];
1246 TRACE_(seh)( "calling consolidate callback %p (rec=%p)\n", consolidate, rec );
1247 context->Rip = (ULONG64)call_consolidate_callback( context, consolidate, rec );
1250 /* hack: remove no longer accessible TEB frames */
1251 while ((ULONG64)teb_frame < context->Rsp)
1253 TRACE_(seh)( "removing TEB frame: %p\n", teb_frame );
1254 teb_frame = __wine_pop_frame( teb_frame );
1257 TRACE_(seh)( "returning to %p stack %p\n", (void *)context->Rip, (void *)context->Rsp );
1258 NtContinue( context, FALSE );
1262 /*******************************************************************
1263 * RtlUnwindEx (NTDLL.@)
1265 void WINAPI RtlUnwindEx( PVOID end_frame, PVOID target_ip, EXCEPTION_RECORD *rec,
1266 PVOID retval, CONTEXT *context, UNWIND_HISTORY_TABLE *table )
1268 EXCEPTION_REGISTRATION_RECORD *teb_frame = NtCurrentTeb()->Tib.ExceptionList;
1269 EXCEPTION_RECORD record;
1270 DISPATCHER_CONTEXT dispatch;
1271 CONTEXT new_context;
1272 NTSTATUS status;
1273 DWORD i;
1275 RtlCaptureContext( context );
1276 new_context = *context;
1278 /* build an exception record, if we do not have one */
1279 if (!rec)
1281 record.ExceptionCode = STATUS_UNWIND;
1282 record.ExceptionFlags = 0;
1283 record.ExceptionRecord = NULL;
1284 record.ExceptionAddress = (void *)context->Rip;
1285 record.NumberParameters = 0;
1286 rec = &record;
1289 rec->ExceptionFlags |= EH_UNWINDING | (end_frame ? 0 : EH_EXIT_UNWIND);
1291 TRACE( "code=%x flags=%x end_frame=%p target_ip=%p rip=%016I64x\n",
1292 rec->ExceptionCode, rec->ExceptionFlags, end_frame, target_ip, context->Rip );
1293 for (i = 0; i < min( EXCEPTION_MAXIMUM_PARAMETERS, rec->NumberParameters ); i++)
1294 TRACE( " info[%d]=%016I64x\n", i, rec->ExceptionInformation[i] );
1295 TRACE(" rax=%016I64x rbx=%016I64x rcx=%016I64x rdx=%016I64x\n",
1296 context->Rax, context->Rbx, context->Rcx, context->Rdx );
1297 TRACE(" rsi=%016I64x rdi=%016I64x rbp=%016I64x rsp=%016I64x\n",
1298 context->Rsi, context->Rdi, context->Rbp, context->Rsp );
1299 TRACE(" r8=%016I64x r9=%016I64x r10=%016I64x r11=%016I64x\n",
1300 context->R8, context->R9, context->R10, context->R11 );
1301 TRACE(" r12=%016I64x r13=%016I64x r14=%016I64x r15=%016I64x\n",
1302 context->R12, context->R13, context->R14, context->R15 );
1304 dispatch.EstablisherFrame = context->Rsp;
1305 dispatch.TargetIp = (ULONG64)target_ip;
1306 dispatch.ContextRecord = context;
1307 dispatch.HistoryTable = table;
1309 for (;;)
1311 status = virtual_unwind( UNW_FLAG_UHANDLER, &dispatch, &new_context );
1312 if (status != STATUS_SUCCESS) raise_status( status, rec );
1314 unwind_done:
1315 if (!dispatch.EstablisherFrame) break;
1317 if ((dispatch.EstablisherFrame & 7) ||
1318 dispatch.EstablisherFrame < (ULONG64)NtCurrentTeb()->Tib.StackLimit ||
1319 dispatch.EstablisherFrame > (ULONG64)NtCurrentTeb()->Tib.StackBase)
1321 ERR( "invalid frame %p (%p-%p)\n", (void *)dispatch.EstablisherFrame,
1322 NtCurrentTeb()->Tib.StackLimit, NtCurrentTeb()->Tib.StackBase );
1323 rec->ExceptionFlags |= EH_STACK_INVALID;
1324 break;
1327 if (dispatch.LanguageHandler)
1329 if (end_frame && (dispatch.EstablisherFrame > (ULONG64)end_frame))
1331 ERR( "invalid end frame %p/%p\n", (void *)dispatch.EstablisherFrame, end_frame );
1332 raise_status( STATUS_INVALID_UNWIND_TARGET, rec );
1334 if (dispatch.EstablisherFrame == (ULONG64)end_frame) rec->ExceptionFlags |= EH_TARGET_UNWIND;
1335 if (call_unwind_handler( rec, &dispatch ) == ExceptionCollidedUnwind)
1337 ULONG64 frame;
1339 new_context = *dispatch.ContextRecord;
1340 new_context.ContextFlags &= ~0x40;
1341 *context = new_context;
1342 dispatch.ContextRecord = context;
1343 RtlVirtualUnwind( UNW_FLAG_NHANDLER, dispatch.ImageBase,
1344 dispatch.ControlPc, dispatch.FunctionEntry,
1345 &new_context, NULL, &frame, NULL );
1346 rec->ExceptionFlags |= EH_COLLIDED_UNWIND;
1347 goto unwind_done;
1349 rec->ExceptionFlags &= ~EH_COLLIDED_UNWIND;
1351 else /* hack: call builtin handlers registered in the tib list */
1353 DWORD64 backup_frame = dispatch.EstablisherFrame;
1354 while ((ULONG64)teb_frame < new_context.Rsp && (ULONG64)teb_frame < (ULONG64)end_frame)
1356 TRACE( "found builtin frame %p handler %p\n", teb_frame, teb_frame->Handler );
1357 dispatch.EstablisherFrame = (ULONG64)teb_frame;
1358 if (call_teb_unwind_handler( rec, &dispatch, teb_frame ) == ExceptionCollidedUnwind)
1360 ULONG64 frame;
1362 teb_frame = __wine_pop_frame( teb_frame );
1364 new_context = *dispatch.ContextRecord;
1365 new_context.ContextFlags &= ~0x40;
1366 *context = new_context;
1367 dispatch.ContextRecord = context;
1368 RtlVirtualUnwind( UNW_FLAG_NHANDLER, dispatch.ImageBase,
1369 dispatch.ControlPc, dispatch.FunctionEntry,
1370 &new_context, NULL, &frame, NULL );
1371 rec->ExceptionFlags |= EH_COLLIDED_UNWIND;
1372 goto unwind_done;
1374 teb_frame = __wine_pop_frame( teb_frame );
1376 if ((ULONG64)teb_frame == (ULONG64)end_frame && (ULONG64)end_frame < new_context.Rsp) break;
1377 dispatch.EstablisherFrame = backup_frame;
1380 if (dispatch.EstablisherFrame == (ULONG64)end_frame) break;
1381 *context = new_context;
1384 context->Rax = (ULONG64)retval;
1385 context->Rip = (ULONG64)target_ip;
1386 RtlRestoreContext(context, rec);
1390 /*******************************************************************
1391 * RtlUnwind (NTDLL.@)
1393 void WINAPI RtlUnwind( void *frame, void *target_ip, EXCEPTION_RECORD *rec, void *retval )
1395 CONTEXT context;
1396 RtlUnwindEx( frame, target_ip, rec, retval, &context, NULL );
1400 /*******************************************************************
1401 * _local_unwind (NTDLL.@)
1403 void WINAPI _local_unwind( void *frame, void *target_ip )
1405 CONTEXT context;
1406 RtlUnwindEx( frame, target_ip, NULL, NULL, &context, NULL );
1409 /*******************************************************************
1410 * __C_specific_handler (NTDLL.@)
1412 EXCEPTION_DISPOSITION WINAPI __C_specific_handler( EXCEPTION_RECORD *rec,
1413 void *frame,
1414 CONTEXT *context,
1415 struct _DISPATCHER_CONTEXT *dispatch )
1417 SCOPE_TABLE *table = dispatch->HandlerData;
1418 ULONG i;
1420 TRACE_(seh)( "%p %p %p %p\n", rec, frame, context, dispatch );
1421 if (TRACE_ON(seh)) dump_scope_table( dispatch->ImageBase, table );
1423 if (rec->ExceptionFlags & (EH_UNWINDING | EH_EXIT_UNWIND))
1425 for (i = dispatch->ScopeIndex; i < table->Count; i++)
1427 if (dispatch->ControlPc >= dispatch->ImageBase + table->ScopeRecord[i].BeginAddress &&
1428 dispatch->ControlPc < dispatch->ImageBase + table->ScopeRecord[i].EndAddress)
1430 PTERMINATION_HANDLER handler;
1432 if (table->ScopeRecord[i].JumpTarget) continue;
1434 if (rec->ExceptionFlags & EH_TARGET_UNWIND &&
1435 dispatch->TargetIp >= dispatch->ImageBase + table->ScopeRecord[i].BeginAddress &&
1436 dispatch->TargetIp < dispatch->ImageBase + table->ScopeRecord[i].EndAddress)
1438 break;
1441 handler = (PTERMINATION_HANDLER)(dispatch->ImageBase + table->ScopeRecord[i].HandlerAddress);
1442 dispatch->ScopeIndex = i+1;
1444 TRACE_(seh)( "calling __finally %p frame %p\n", handler, frame );
1445 handler( TRUE, frame );
1448 return ExceptionContinueSearch;
1451 for (i = dispatch->ScopeIndex; i < table->Count; i++)
1453 if (dispatch->ControlPc >= dispatch->ImageBase + table->ScopeRecord[i].BeginAddress &&
1454 dispatch->ControlPc < dispatch->ImageBase + table->ScopeRecord[i].EndAddress)
1456 if (!table->ScopeRecord[i].JumpTarget) continue;
1457 if (table->ScopeRecord[i].HandlerAddress != EXCEPTION_EXECUTE_HANDLER)
1459 EXCEPTION_POINTERS ptrs;
1460 PEXCEPTION_FILTER filter;
1462 filter = (PEXCEPTION_FILTER)(dispatch->ImageBase + table->ScopeRecord[i].HandlerAddress);
1463 ptrs.ExceptionRecord = rec;
1464 ptrs.ContextRecord = context;
1465 TRACE_(seh)( "calling filter %p ptrs %p frame %p\n", filter, &ptrs, frame );
1466 switch (filter( &ptrs, frame ))
1468 case EXCEPTION_EXECUTE_HANDLER:
1469 break;
1470 case EXCEPTION_CONTINUE_SEARCH:
1471 continue;
1472 case EXCEPTION_CONTINUE_EXECUTION:
1473 return ExceptionContinueExecution;
1476 TRACE( "unwinding to target %p\n", (char *)dispatch->ImageBase + table->ScopeRecord[i].JumpTarget );
1477 RtlUnwindEx( frame, (char *)dispatch->ImageBase + table->ScopeRecord[i].JumpTarget,
1478 rec, 0, dispatch->ContextRecord, dispatch->HistoryTable );
1481 return ExceptionContinueSearch;
1485 /***********************************************************************
1486 * RtlRaiseException (NTDLL.@)
1488 __ASM_GLOBAL_FUNC( RtlRaiseException,
1489 "sub $0x4f8,%rsp\n\t"
1490 __ASM_SEH(".seh_stackalloc 0x4f8\n\t")
1491 __ASM_SEH(".seh_endprologue\n\t")
1492 __ASM_CFI(".cfi_adjust_cfa_offset 0x4f8\n\t")
1493 "movq %rcx,0x500(%rsp)\n\t"
1494 "leaq 0x20(%rsp),%rcx\n\t"
1495 "call " __ASM_NAME("RtlCaptureContext") "\n\t"
1496 "leaq 0x20(%rsp),%rdx\n\t" /* context pointer */
1497 "leaq 0x500(%rsp),%rax\n\t" /* orig stack pointer */
1498 "movq %rax,0x98(%rdx)\n\t" /* context->Rsp */
1499 "movq (%rax),%rcx\n\t" /* original first parameter */
1500 "movq %rcx,0x80(%rdx)\n\t" /* context->Rcx */
1501 "movq 0x4f8(%rsp),%rax\n\t" /* return address */
1502 "movq %rax,0xf8(%rdx)\n\t" /* context->Rip */
1503 "movq %rax,0x10(%rcx)\n\t" /* rec->ExceptionAddress */
1504 "movl $1,%r8d\n\t"
1505 "movq %gs:(0x30),%rax\n\t" /* Teb */
1506 "movq 0x60(%rax),%rax\n\t" /* Peb */
1507 "cmpb $0,0x02(%rax)\n\t" /* BeingDebugged */
1508 "jne 1f\n\t"
1509 "call " __ASM_NAME("dispatch_exception") "\n"
1510 "1:\tcall " __ASM_NAME("NtRaiseException") "\n\t"
1511 "movq %rax,%rcx\n\t"
1512 "call " __ASM_NAME("RtlRaiseStatus") /* does not return */ );
1515 static inline ULONG hash_pointers( void **ptrs, ULONG count )
1517 /* Based on MurmurHash2, which is in the public domain */
1518 static const ULONG m = 0x5bd1e995;
1519 static const ULONG r = 24;
1520 ULONG hash = count * sizeof(void*);
1521 for (; count > 0; ptrs++, count--)
1523 ULONG_PTR data = (ULONG_PTR)*ptrs;
1524 ULONG k1 = (ULONG)(data & 0xffffffff), k2 = (ULONG)(data >> 32);
1525 k1 *= m;
1526 k1 = (k1 ^ (k1 >> r)) * m;
1527 k2 *= m;
1528 k2 = (k2 ^ (k2 >> r)) * m;
1529 hash = (((hash * m) ^ k1) * m) ^ k2;
1531 hash = (hash ^ (hash >> 13)) * m;
1532 return hash ^ (hash >> 15);
1536 /*************************************************************************
1537 * RtlCaptureStackBackTrace (NTDLL.@)
1539 USHORT WINAPI RtlCaptureStackBackTrace( ULONG skip, ULONG count, PVOID *buffer, ULONG *hash )
1541 UNWIND_HISTORY_TABLE table;
1542 DISPATCHER_CONTEXT dispatch;
1543 CONTEXT context;
1544 NTSTATUS status;
1545 ULONG i;
1546 USHORT num_entries = 0;
1548 TRACE( "(%u, %u, %p, %p)\n", skip, count, buffer, hash );
1550 RtlCaptureContext( &context );
1551 dispatch.TargetIp = 0;
1552 dispatch.ContextRecord = &context;
1553 dispatch.HistoryTable = &table;
1554 if (hash) *hash = 0;
1555 for (i = 0; i < skip + count; i++)
1557 status = virtual_unwind( UNW_FLAG_NHANDLER, &dispatch, &context );
1558 if (status != STATUS_SUCCESS) return i;
1560 if (!dispatch.EstablisherFrame) break;
1562 if ((dispatch.EstablisherFrame & 7) ||
1563 dispatch.EstablisherFrame < (ULONG64)NtCurrentTeb()->Tib.StackLimit ||
1564 dispatch.EstablisherFrame > (ULONG64)NtCurrentTeb()->Tib.StackBase)
1566 ERR( "invalid frame %p (%p-%p)\n", (void *)dispatch.EstablisherFrame,
1567 NtCurrentTeb()->Tib.StackLimit, NtCurrentTeb()->Tib.StackBase );
1568 break;
1571 if (context.Rsp == (ULONG64)NtCurrentTeb()->Tib.StackBase) break;
1573 if (i >= skip) buffer[num_entries++] = (void *)context.Rip;
1575 if (hash && num_entries > 0) *hash = hash_pointers( buffer, num_entries );
1576 TRACE( "captured %hu frames\n", num_entries );
1577 return num_entries;
1581 /***********************************************************************
1582 * signal_start_thread
1584 __ASM_GLOBAL_FUNC( signal_start_thread,
1585 "movq %rcx,%rbx\n\t" /* context */
1586 /* clear the thread stack */
1587 "andq $~0xfff,%rcx\n\t" /* round down to page size */
1588 "leaq -0xf0000(%rcx),%rdi\n\t"
1589 "movq %rdi,%rsp\n\t"
1590 "subq %rdi,%rcx\n\t"
1591 "xorl %eax,%eax\n\t"
1592 "shrq $3,%rcx\n\t"
1593 "rep; stosq\n\t"
1594 /* switch to the initial context */
1595 "leaq -32(%rbx),%rsp\n\t"
1596 "movq %rbx,%rcx\n\t"
1597 "movl $1,%edx\n\t"
1598 "call " __ASM_NAME("NtContinue") )
1601 /**********************************************************************
1602 * DbgBreakPoint (NTDLL.@)
1604 __ASM_STDCALL_FUNC( DbgBreakPoint, 0, "int $3; ret"
1605 "\n\tnop; nop; nop; nop; nop; nop; nop; nop"
1606 "\n\tnop; nop; nop; nop; nop; nop" );
1608 /**********************************************************************
1609 * DbgUserBreakPoint (NTDLL.@)
1611 __ASM_STDCALL_FUNC( DbgUserBreakPoint, 0, "int $3; ret"
1612 "\n\tnop; nop; nop; nop; nop; nop; nop; nop"
1613 "\n\tnop; nop; nop; nop; nop; nop" );
1615 #endif /* __x86_64__ */