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
26 #define NONAMELESSUNION
27 #define NONAMELESSSTRUCT
29 #define WIN32_NO_STATUS
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(seh
);
39 typedef struct _SCOPE_TABLE
49 } SCOPE_TABLE
, *PSCOPE_TABLE
;
52 /* layering violation: the setjmp buffer is defined in msvcrt, but used by RtlUnwindEx */
53 struct MSVCRT_JUMP_BUFFER
79 /***********************************************************************
80 * Definitions for Win32 unwind tables
85 RUNTIME_FUNCTION chain
;
103 BYTE frame_offset
: 4;
104 struct opcode opcodes
[1]; /* info->count entries */
105 /* followed by handler_data */
108 #define UWOP_PUSH_NONVOL 0
109 #define UWOP_ALLOC_LARGE 1
110 #define UWOP_ALLOC_SMALL 2
111 #define UWOP_SET_FPREG 3
112 #define UWOP_SAVE_NONVOL 4
113 #define UWOP_SAVE_NONVOL_FAR 5
114 #define UWOP_EPILOG 6
115 #define UWOP_SAVE_XMM128 8
116 #define UWOP_SAVE_XMM128_FAR 9
117 #define UWOP_PUSH_MACHFRAME 10
119 static void dump_unwind_info( ULONG64 base
, RUNTIME_FUNCTION
*function
)
121 static const char * const reg_names
[16] =
122 { "rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi",
123 "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15" };
125 union handler_data
*handler_data
;
126 struct UNWIND_INFO
*info
;
127 unsigned int i
, count
;
129 TRACE( "**** func %x-%x\n", function
->BeginAddress
, function
->EndAddress
);
132 if (function
->UnwindData
& 1)
134 RUNTIME_FUNCTION
*next
= (RUNTIME_FUNCTION
*)((char *)base
+ (function
->UnwindData
& ~1));
135 TRACE( "unwind info for function %p-%p chained to function %p-%p\n",
136 (char *)base
+ function
->BeginAddress
, (char *)base
+ function
->EndAddress
,
137 (char *)base
+ next
->BeginAddress
, (char *)base
+ next
->EndAddress
);
141 info
= (struct UNWIND_INFO
*)((char *)base
+ function
->UnwindData
);
143 TRACE( "unwind info at %p flags %x prolog 0x%x bytes function %p-%p\n",
144 info
, info
->flags
, info
->prolog
,
145 (char *)base
+ function
->BeginAddress
, (char *)base
+ function
->EndAddress
);
148 TRACE( " frame register %s offset 0x%x(%%rsp)\n",
149 reg_names
[info
->frame_reg
], info
->frame_offset
* 16 );
151 for (i
= 0; i
< info
->count
; i
++)
153 TRACE( " 0x%x: ", info
->opcodes
[i
].offset
);
154 switch (info
->opcodes
[i
].code
)
156 case UWOP_PUSH_NONVOL
:
157 TRACE( "pushq %%%s\n", reg_names
[info
->opcodes
[i
].info
] );
159 case UWOP_ALLOC_LARGE
:
160 if (info
->opcodes
[i
].info
)
162 count
= *(DWORD
*)&info
->opcodes
[i
+1];
167 count
= *(USHORT
*)&info
->opcodes
[i
+1] * 8;
170 TRACE( "subq $0x%x,%%rsp\n", count
);
172 case UWOP_ALLOC_SMALL
:
173 count
= (info
->opcodes
[i
].info
+ 1) * 8;
174 TRACE( "subq $0x%x,%%rsp\n", count
);
177 TRACE( "leaq 0x%x(%%rsp),%s\n",
178 info
->frame_offset
* 16, reg_names
[info
->frame_reg
] );
180 case UWOP_SAVE_NONVOL
:
181 count
= *(USHORT
*)&info
->opcodes
[i
+1] * 8;
182 TRACE( "movq %%%s,0x%x(%%rsp)\n", reg_names
[info
->opcodes
[i
].info
], count
);
185 case UWOP_SAVE_NONVOL_FAR
:
186 count
= *(DWORD
*)&info
->opcodes
[i
+1];
187 TRACE( "movq %%%s,0x%x(%%rsp)\n", reg_names
[info
->opcodes
[i
].info
], count
);
190 case UWOP_SAVE_XMM128
:
191 count
= *(USHORT
*)&info
->opcodes
[i
+1] * 16;
192 TRACE( "movaps %%xmm%u,0x%x(%%rsp)\n", info
->opcodes
[i
].info
, count
);
195 case UWOP_SAVE_XMM128_FAR
:
196 count
= *(DWORD
*)&info
->opcodes
[i
+1];
197 TRACE( "movaps %%xmm%u,0x%x(%%rsp)\n", info
->opcodes
[i
].info
, count
);
200 case UWOP_PUSH_MACHFRAME
:
201 TRACE( "PUSH_MACHFRAME %u\n", info
->opcodes
[i
].info
);
204 if (info
->version
== 2)
207 if (info
->opcodes
[i
].info
)
208 offset
= info
->opcodes
[i
].offset
;
210 offset
= (info
->opcodes
[i
+1].info
<< 8) + info
->opcodes
[i
+1].offset
;
211 TRACE("epilog %p-%p\n", (char *)base
+ function
->EndAddress
- offset
,
212 (char *)base
+ function
->EndAddress
- offset
+ info
->opcodes
[i
].offset
);
217 FIXME( "unknown code %u\n", info
->opcodes
[i
].code
);
222 handler_data
= (union handler_data
*)&info
->opcodes
[(info
->count
+ 1) & ~1];
223 if (info
->flags
& UNW_FLAG_CHAININFO
)
225 TRACE( " chained to function %p-%p\n",
226 (char *)base
+ handler_data
->chain
.BeginAddress
,
227 (char *)base
+ handler_data
->chain
.EndAddress
);
228 function
= &handler_data
->chain
;
231 if (info
->flags
& (UNW_FLAG_EHANDLER
| UNW_FLAG_UHANDLER
))
232 TRACE( " handler %p data at %p\n",
233 (char *)base
+ handler_data
->handler
, &handler_data
->handler
+ 1 );
238 static void dump_scope_table( ULONG64 base
, const SCOPE_TABLE
*table
)
242 TRACE( "scope table at %p\n", table
);
243 for (i
= 0; i
< table
->Count
; i
++)
244 TRACE( " %u: %p-%p handler %p target %p\n", i
,
245 (char *)base
+ table
->ScopeRecord
[i
].BeginAddress
,
246 (char *)base
+ table
->ScopeRecord
[i
].EndAddress
,
247 (char *)base
+ table
->ScopeRecord
[i
].HandlerAddress
,
248 (char *)base
+ table
->ScopeRecord
[i
].JumpTarget
);
252 /***********************************************************************
255 static NTSTATUS
virtual_unwind( ULONG type
, DISPATCHER_CONTEXT
*dispatch
, CONTEXT
*context
)
257 LDR_DATA_TABLE_ENTRY
*module
;
260 dispatch
->ImageBase
= 0;
261 dispatch
->ScopeIndex
= 0;
262 dispatch
->ControlPc
= context
->Rip
;
264 /* first look for PE exception information */
266 if ((dispatch
->FunctionEntry
= lookup_function_info( context
->Rip
, &dispatch
->ImageBase
, &module
)))
268 dispatch
->LanguageHandler
= RtlVirtualUnwind( type
, dispatch
->ImageBase
, context
->Rip
,
269 dispatch
->FunctionEntry
, context
,
270 &dispatch
->HandlerData
, &dispatch
->EstablisherFrame
,
272 return STATUS_SUCCESS
;
275 /* then look for host system exception information */
277 if (!module
|| (module
->Flags
& LDR_WINE_INTERNAL
))
279 status
= unix_funcs
->unwind_builtin_dll( type
, dispatch
, context
);
281 if (!status
&& dispatch
->LanguageHandler
&& !module
)
283 FIXME( "calling personality routine in system library not supported yet\n" );
284 dispatch
->LanguageHandler
= NULL
;
286 if (status
!= STATUS_UNSUCCESSFUL
) return status
;
288 else WARN( "exception data not found in %s\n", debugstr_w(module
->BaseDllName
.Buffer
) );
290 /* no exception information, treat as a leaf function */
292 dispatch
->EstablisherFrame
= context
->Rsp
;
293 dispatch
->LanguageHandler
= NULL
;
294 context
->Rip
= *(ULONG64
*)context
->Rsp
;
295 context
->Rsp
= context
->Rsp
+ sizeof(ULONG64
);
296 return STATUS_SUCCESS
;
300 /**************************************************************************
303 * Supposed to touch all the stack pages, but we shouldn't need that.
305 __ASM_GLOBAL_FUNC( __chkstk
, "ret" );
308 /***********************************************************************
309 * RtlCaptureContext (NTDLL.@)
311 __ASM_GLOBAL_FUNC( RtlCaptureContext
,
313 __ASM_CFI(".cfi_adjust_cfa_offset 8\n\t")
314 "movl $0x10000f,0x30(%rcx)\n\t" /* context->ContextFlags */
315 "stmxcsr 0x34(%rcx)\n\t" /* context->MxCsr */
316 "movw %cs,0x38(%rcx)\n\t" /* context->SegCs */
317 "movw %ds,0x3a(%rcx)\n\t" /* context->SegDs */
318 "movw %es,0x3c(%rcx)\n\t" /* context->SegEs */
319 "movw %fs,0x3e(%rcx)\n\t" /* context->SegFs */
320 "movw %gs,0x40(%rcx)\n\t" /* context->SegGs */
321 "movw %ss,0x42(%rcx)\n\t" /* context->SegSs */
322 "popq 0x44(%rcx)\n\t" /* context->Eflags */
323 __ASM_CFI(".cfi_adjust_cfa_offset -8\n\t")
324 "movq %rax,0x78(%rcx)\n\t" /* context->Rax */
325 "movq %rcx,0x80(%rcx)\n\t" /* context->Rcx */
326 "movq %rdx,0x88(%rcx)\n\t" /* context->Rdx */
327 "movq %rbx,0x90(%rcx)\n\t" /* context->Rbx */
328 "leaq 8(%rsp),%rax\n\t"
329 "movq %rax,0x98(%rcx)\n\t" /* context->Rsp */
330 "movq %rbp,0xa0(%rcx)\n\t" /* context->Rbp */
331 "movq %rsi,0xa8(%rcx)\n\t" /* context->Rsi */
332 "movq %rdi,0xb0(%rcx)\n\t" /* context->Rdi */
333 "movq %r8,0xb8(%rcx)\n\t" /* context->R8 */
334 "movq %r9,0xc0(%rcx)\n\t" /* context->R9 */
335 "movq %r10,0xc8(%rcx)\n\t" /* context->R10 */
336 "movq %r11,0xd0(%rcx)\n\t" /* context->R11 */
337 "movq %r12,0xd8(%rcx)\n\t" /* context->R12 */
338 "movq %r13,0xe0(%rcx)\n\t" /* context->R13 */
339 "movq %r14,0xe8(%rcx)\n\t" /* context->R14 */
340 "movq %r15,0xf0(%rcx)\n\t" /* context->R15 */
341 "movq (%rsp),%rax\n\t"
342 "movq %rax,0xf8(%rcx)\n\t" /* context->Rip */
343 "fxsave 0x100(%rcx)\n\t" /* context->FltSave */
346 /******************************************************************************
347 * RtlWow64GetThreadContext (NTDLL.@)
349 NTSTATUS WINAPI
RtlWow64GetThreadContext( HANDLE handle
, WOW64_CONTEXT
*context
)
351 return NtQueryInformationThread( handle
, ThreadWow64Context
, context
, sizeof(*context
), NULL
);
355 /******************************************************************************
356 * RtlWow64SetThreadContext (NTDLL.@)
358 NTSTATUS WINAPI
RtlWow64SetThreadContext( HANDLE handle
, const WOW64_CONTEXT
*context
)
360 return NtSetInformationThread( handle
, ThreadWow64Context
, context
, sizeof(*context
) );
364 static DWORD __cdecl
nested_exception_handler( EXCEPTION_RECORD
*rec
, EXCEPTION_REGISTRATION_RECORD
*frame
,
365 CONTEXT
*context
, EXCEPTION_REGISTRATION_RECORD
**dispatcher
)
367 if (!(rec
->ExceptionFlags
& (EH_UNWINDING
| EH_EXIT_UNWIND
)))
368 rec
->ExceptionFlags
|= EH_NESTED_CALL
;
370 return ExceptionContinueSearch
;
373 /**********************************************************************
376 * Call a single exception handler.
377 * FIXME: Handle nested exceptions.
379 static DWORD
call_handler( EXCEPTION_RECORD
*rec
, CONTEXT
*context
, DISPATCHER_CONTEXT
*dispatch
)
381 EXCEPTION_REGISTRATION_RECORD frame
;
384 frame
.Handler
= nested_exception_handler
;
385 __wine_push_frame( &frame
);
387 TRACE( "calling handler %p (rec=%p, frame=%p context=%p, dispatch=%p)\n",
388 dispatch
->LanguageHandler
, rec
, (void *)dispatch
->EstablisherFrame
, dispatch
->ContextRecord
, dispatch
);
389 res
= dispatch
->LanguageHandler( rec
, (void *)dispatch
->EstablisherFrame
, context
, dispatch
);
390 TRACE( "handler at %p returned %u\n", dispatch
->LanguageHandler
, res
);
392 rec
->ExceptionFlags
&= EH_NONCONTINUABLE
;
393 __wine_pop_frame( &frame
);
398 /**********************************************************************
401 * Call a single exception handler from the TEB chain.
402 * FIXME: Handle nested exceptions.
404 static DWORD
call_teb_handler( EXCEPTION_RECORD
*rec
, CONTEXT
*context
, DISPATCHER_CONTEXT
*dispatch
,
405 EXCEPTION_REGISTRATION_RECORD
*teb_frame
)
409 TRACE( "calling TEB handler %p (rec=%p, frame=%p context=%p, dispatch=%p)\n",
410 teb_frame
->Handler
, rec
, teb_frame
, dispatch
->ContextRecord
, dispatch
);
411 res
= teb_frame
->Handler( rec
, teb_frame
, context
, (EXCEPTION_REGISTRATION_RECORD
**)dispatch
);
412 TRACE( "handler at %p returned %u\n", teb_frame
->Handler
, res
);
417 /**********************************************************************
418 * call_stack_handlers
420 * Call the stack handlers chain.
422 static NTSTATUS
call_stack_handlers( EXCEPTION_RECORD
*rec
, CONTEXT
*orig_context
)
424 EXCEPTION_REGISTRATION_RECORD
*teb_frame
= NtCurrentTeb()->Tib
.ExceptionList
;
425 UNWIND_HISTORY_TABLE table
;
426 DISPATCHER_CONTEXT dispatch
;
430 context
= *orig_context
;
431 context
.ContextFlags
&= ~0x40; /* Clear xstate flag. */
433 dispatch
.TargetIp
= 0;
434 dispatch
.ContextRecord
= &context
;
435 dispatch
.HistoryTable
= &table
;
438 status
= virtual_unwind( UNW_FLAG_EHANDLER
, &dispatch
, &context
);
439 if (status
!= STATUS_SUCCESS
) return status
;
442 if (!dispatch
.EstablisherFrame
) break;
444 if ((dispatch
.EstablisherFrame
& 7) ||
445 dispatch
.EstablisherFrame
< (ULONG64
)NtCurrentTeb()->Tib
.StackLimit
||
446 dispatch
.EstablisherFrame
> (ULONG64
)NtCurrentTeb()->Tib
.StackBase
)
448 ERR( "invalid frame %p (%p-%p)\n", (void *)dispatch
.EstablisherFrame
,
449 NtCurrentTeb()->Tib
.StackLimit
, NtCurrentTeb()->Tib
.StackBase
);
450 rec
->ExceptionFlags
|= EH_STACK_INVALID
;
454 if (dispatch
.LanguageHandler
)
456 switch (call_handler( rec
, orig_context
, &dispatch
))
458 case ExceptionContinueExecution
:
459 if (rec
->ExceptionFlags
& EH_NONCONTINUABLE
) return STATUS_NONCONTINUABLE_EXCEPTION
;
460 return STATUS_SUCCESS
;
461 case ExceptionContinueSearch
:
463 case ExceptionNestedException
:
464 FIXME( "nested exception\n" );
466 case ExceptionCollidedUnwind
: {
469 context
= *dispatch
.ContextRecord
;
470 dispatch
.ContextRecord
= &context
;
471 RtlVirtualUnwind( UNW_FLAG_NHANDLER
, dispatch
.ImageBase
,
472 dispatch
.ControlPc
, dispatch
.FunctionEntry
,
473 &context
, NULL
, &frame
, NULL
);
477 return STATUS_INVALID_DISPOSITION
;
480 /* hack: call wine handlers registered in the tib list */
481 else while ((ULONG64
)teb_frame
< context
.Rsp
)
483 TRACE( "found wine frame %p rsp %p handler %p\n",
484 teb_frame
, (void *)context
.Rsp
, teb_frame
->Handler
);
485 dispatch
.EstablisherFrame
= (ULONG64
)teb_frame
;
486 switch (call_teb_handler( rec
, orig_context
, &dispatch
, teb_frame
))
488 case ExceptionContinueExecution
:
489 if (rec
->ExceptionFlags
& EH_NONCONTINUABLE
) return STATUS_NONCONTINUABLE_EXCEPTION
;
490 return STATUS_SUCCESS
;
491 case ExceptionContinueSearch
:
493 case ExceptionNestedException
:
494 FIXME( "nested exception\n" );
496 case ExceptionCollidedUnwind
: {
499 context
= *dispatch
.ContextRecord
;
500 dispatch
.ContextRecord
= &context
;
501 RtlVirtualUnwind( UNW_FLAG_NHANDLER
, dispatch
.ImageBase
,
502 dispatch
.ControlPc
, dispatch
.FunctionEntry
,
503 &context
, NULL
, &frame
, NULL
);
504 teb_frame
= teb_frame
->Prev
;
508 return STATUS_INVALID_DISPOSITION
;
510 teb_frame
= teb_frame
->Prev
;
513 if (context
.Rsp
== (ULONG64
)NtCurrentTeb()->Tib
.StackBase
) break;
515 return STATUS_UNHANDLED_EXCEPTION
;
519 NTSTATUS WINAPI
dispatch_exception( EXCEPTION_RECORD
*rec
, CONTEXT
*context
)
524 TRACE( "code=%x flags=%x addr=%p ip=%p tid=%04x\n",
525 rec
->ExceptionCode
, rec
->ExceptionFlags
, rec
->ExceptionAddress
,
526 (void *)context
->Rip
, GetCurrentThreadId() );
527 for (c
= 0; c
< min( EXCEPTION_MAXIMUM_PARAMETERS
, rec
->NumberParameters
); c
++)
528 TRACE( " info[%d]=%016lx\n", c
, rec
->ExceptionInformation
[c
] );
530 if (rec
->ExceptionCode
== EXCEPTION_WINE_STUB
)
532 if (rec
->ExceptionInformation
[1] >> 16)
533 MESSAGE( "wine: Call from %p to unimplemented function %s.%s, aborting\n",
534 rec
->ExceptionAddress
,
535 (char*)rec
->ExceptionInformation
[0], (char*)rec
->ExceptionInformation
[1] );
537 MESSAGE( "wine: Call from %p to unimplemented function %s.%ld, aborting\n",
538 rec
->ExceptionAddress
,
539 (char*)rec
->ExceptionInformation
[0], rec
->ExceptionInformation
[1] );
541 else if (rec
->ExceptionCode
== EXCEPTION_WINE_NAME_THREAD
&& rec
->ExceptionInformation
[0] == 0x1000)
543 WARN( "Thread %04x renamed to %s\n", (DWORD
)rec
->ExceptionInformation
[2], debugstr_a((char *)rec
->ExceptionInformation
[1]) );
545 else if (rec
->ExceptionCode
== DBG_PRINTEXCEPTION_C
)
547 WARN( "%s\n", debugstr_an((char *)rec
->ExceptionInformation
[1], rec
->ExceptionInformation
[0] - 1) );
549 else if (rec
->ExceptionCode
== DBG_PRINTEXCEPTION_WIDE_C
)
551 WARN( "%s\n", debugstr_wn((WCHAR
*)rec
->ExceptionInformation
[1], rec
->ExceptionInformation
[0] - 1) );
555 if (rec
->ExceptionCode
== STATUS_ASSERTION_FAILURE
)
556 ERR( "%s exception (code=%x) raised\n", debugstr_exception_code(rec
->ExceptionCode
), rec
->ExceptionCode
);
558 WARN( "%s exception (code=%x) raised\n", debugstr_exception_code(rec
->ExceptionCode
), rec
->ExceptionCode
);
560 TRACE(" rax=%016lx rbx=%016lx rcx=%016lx rdx=%016lx\n",
561 context
->Rax
, context
->Rbx
, context
->Rcx
, context
->Rdx
);
562 TRACE(" rsi=%016lx rdi=%016lx rbp=%016lx rsp=%016lx\n",
563 context
->Rsi
, context
->Rdi
, context
->Rbp
, context
->Rsp
);
564 TRACE(" r8=%016lx r9=%016lx r10=%016lx r11=%016lx\n",
565 context
->R8
, context
->R9
, context
->R10
, context
->R11
);
566 TRACE(" r12=%016lx r13=%016lx r14=%016lx r15=%016lx\n",
567 context
->R12
, context
->R13
, context
->R14
, context
->R15
);
570 /* Legends of Runeterra depends on having SegDs == SegSs in an exception
572 context
->SegDs
= context
->SegSs
;
574 if (call_vectored_handlers( rec
, context
) == EXCEPTION_CONTINUE_EXECUTION
)
575 NtContinue( context
, FALSE
);
577 if ((status
= call_stack_handlers( rec
, context
)) == STATUS_SUCCESS
)
578 NtContinue( context
, FALSE
);
580 if (status
!= STATUS_UNHANDLED_EXCEPTION
) RtlRaiseStatus( status
);
581 return NtRaiseException( rec
, context
, FALSE
);
585 /*******************************************************************
586 * KiUserExceptionDispatcher (NTDLL.@)
588 __ASM_GLOBAL_FUNC( KiUserExceptionDispatcher
,
589 "mov 0x98(%rsp),%rcx\n\t" /* context->Rsp */
590 "mov 0xf8(%rsp),%rdx\n\t" /* context->Rip */
591 "mov %rdx,-0x8(%rcx)\n\t"
592 "mov %rbp,-0x10(%rcx)\n\t"
593 "mov %rdi,-0x18(%rcx)\n\t"
594 "mov %rsi,-0x20(%rcx)\n\t"
595 "lea -0x20(%rcx),%rbp\n\t"
596 "mov %rsp,%rdx\n\t" /* context */
597 "lea 0x4f0(%rsp),%rcx\n\t" /* rec */
598 __ASM_SEH(".seh_pushreg %rbp\n\t")
599 __ASM_SEH(".seh_pushreg %rdi\n\t")
600 __ASM_SEH(".seh_pushreg %rsi\n\t")
601 __ASM_SEH(".seh_setframe %rbp,0\n\t")
602 __ASM_SEH(".seh_endprologue\n\t")
604 __ASM_CFI(".cfi_signal_frame\n\t")
605 __ASM_CFI(".cfi_adjust_cfa_offset 0x20\n\t")
606 __ASM_CFI(".cfi_def_cfa %rbp,0x20\n\t")
607 __ASM_CFI(".cfi_rel_offset %rip,0x18\n\t")
608 __ASM_CFI(".cfi_rel_offset %rbp,0x10\n\t")
609 __ASM_CFI(".cfi_rel_offset %rdi,0x8\n\t")
610 __ASM_CFI(".cfi_rel_offset %rsi,0\n\t")
611 "call " __ASM_NAME("dispatch_exception") "\n\t"
615 /*******************************************************************
616 * KiUserApcDispatcher (NTDLL.@)
618 void WINAPI
dispatch_apc( CONTEXT
*context
, ULONG_PTR ctx
, ULONG_PTR arg1
, ULONG_PTR arg2
,
621 func( ctx
, arg1
, arg2
);
622 NtContinue( context
, TRUE
);
625 __ASM_GLOBAL_FUNC( KiUserApcDispatcher
,
627 "mov 0x98(%rcx),%r10\n\t" /* context->Rsp */
628 "mov 0xf8(%rcx),%r11\n\t" /* context->Rip */
629 "mov %r11,-0x8(%r10)\n\t"
630 "mov %rbp,-0x10(%r10)\n\t"
631 "lea -0x10(%r10),%rbp\n\t"
632 __ASM_SEH(".seh_pushreg %rbp\n\t")
633 __ASM_SEH(".seh_setframe %rbp,0\n\t")
634 __ASM_SEH(".seh_endprologue\n\t")
635 __ASM_CFI(".cfi_signal_frame\n\t")
636 __ASM_CFI(".cfi_adjust_cfa_offset 0x10\n\t")
637 __ASM_CFI(".cfi_def_cfa %rbp,0x10\n\t")
638 __ASM_CFI(".cfi_rel_offset %rip,0x8\n\t")
639 __ASM_CFI(".cfi_rel_offset %rbp,0\n\t")
640 "call " __ASM_NAME("dispatch_apc") "\n\t"
644 static ULONG64
get_int_reg( CONTEXT
*context
, int reg
)
646 return *(&context
->Rax
+ reg
);
649 static void set_int_reg( CONTEXT
*context
, KNONVOLATILE_CONTEXT_POINTERS
*ctx_ptr
, int reg
, ULONG64
*val
)
651 *(&context
->Rax
+ reg
) = *val
;
652 if (ctx_ptr
) ctx_ptr
->u2
.IntegerContext
[reg
] = val
;
655 static void set_float_reg( CONTEXT
*context
, KNONVOLATILE_CONTEXT_POINTERS
*ctx_ptr
, int reg
, M128A
*val
)
657 /* Use a memcpy() to avoid issues if val is misaligned. */
658 memcpy(&context
->u
.s
.Xmm0
+ reg
, val
, sizeof(*val
));
659 if (ctx_ptr
) ctx_ptr
->u
.FloatingContext
[reg
] = val
;
662 static int get_opcode_size( struct opcode op
)
666 case UWOP_ALLOC_LARGE
:
667 return 2 + (op
.info
!= 0);
668 case UWOP_SAVE_NONVOL
:
669 case UWOP_SAVE_XMM128
:
672 case UWOP_SAVE_NONVOL_FAR
:
673 case UWOP_SAVE_XMM128_FAR
:
680 static BOOL
is_inside_epilog( BYTE
*pc
, ULONG64 base
, const RUNTIME_FUNCTION
*function
)
682 /* add or lea must be the first instruction, and it must have a rex.W prefix */
683 if ((pc
[0] & 0xf8) == 0x48)
687 case 0x81: /* add $nnnn,%rsp */
688 if (pc
[0] == 0x48 && pc
[2] == 0xc4)
694 case 0x83: /* add $n,%rsp */
695 if (pc
[0] == 0x48 && pc
[2] == 0xc4)
701 case 0x8d: /* lea n(reg),%rsp */
702 if (pc
[0] & 0x06) return FALSE
; /* rex.RX must be cleared */
703 if (((pc
[2] >> 3) & 7) != 4) return FALSE
; /* dest reg mus be %rsp */
704 if ((pc
[2] & 7) == 4) return FALSE
; /* no SIB byte allowed */
705 if ((pc
[2] >> 6) == 1) /* 8-bit offset */
710 if ((pc
[2] >> 6) == 2) /* 32-bit offset */
719 /* now check for various pop instructions */
723 if ((*pc
& 0xf0) == 0x40) pc
++; /* rex prefix */
727 case 0x58: /* pop %rax/%r8 */
728 case 0x59: /* pop %rcx/%r9 */
729 case 0x5a: /* pop %rdx/%r10 */
730 case 0x5b: /* pop %rbx/%r11 */
731 case 0x5c: /* pop %rsp/%r12 */
732 case 0x5d: /* pop %rbp/%r13 */
733 case 0x5e: /* pop %rsi/%r14 */
734 case 0x5f: /* pop %rdi/%r15 */
737 case 0xc2: /* ret $nn */
740 case 0xe9: /* jmp nnnn */
741 pc
+= 5 + *(LONG
*)(pc
+ 1);
742 if (pc
- (BYTE
*)base
>= function
->BeginAddress
&& pc
- (BYTE
*)base
< function
->EndAddress
)
745 case 0xeb: /* jmp n */
746 pc
+= 2 + (signed char)pc
[1];
747 if (pc
- (BYTE
*)base
>= function
->BeginAddress
&& pc
- (BYTE
*)base
< function
->EndAddress
)
750 case 0xf3: /* rep; ret (for amd64 prediction bug) */
751 return pc
[1] == 0xc3;
757 /* execute a function epilog, which must have been validated with is_inside_epilog() */
758 static void interpret_epilog( BYTE
*pc
, CONTEXT
*context
, KNONVOLATILE_CONTEXT_POINTERS
*ctx_ptr
)
764 if ((*pc
& 0xf0) == 0x40) rex
= *pc
++ & 0x0f; /* rex prefix */
768 case 0x58: /* pop %rax/r8 */
769 case 0x59: /* pop %rcx/r9 */
770 case 0x5a: /* pop %rdx/r10 */
771 case 0x5b: /* pop %rbx/r11 */
772 case 0x5c: /* pop %rsp/r12 */
773 case 0x5d: /* pop %rbp/r13 */
774 case 0x5e: /* pop %rsi/r14 */
775 case 0x5f: /* pop %rdi/r15 */
776 set_int_reg( context
, ctx_ptr
, *pc
- 0x58 + (rex
& 1) * 8, (ULONG64
*)context
->Rsp
);
777 context
->Rsp
+= sizeof(ULONG64
);
780 case 0x81: /* add $nnnn,%rsp */
781 context
->Rsp
+= *(LONG
*)(pc
+ 2);
782 pc
+= 2 + sizeof(LONG
);
784 case 0x83: /* add $n,%rsp */
785 context
->Rsp
+= (signed char)pc
[2];
789 if ((pc
[1] >> 6) == 1) /* lea n(reg),%rsp */
791 context
->Rsp
= get_int_reg( context
, (pc
[1] & 7) + (rex
& 1) * 8 ) + (signed char)pc
[2];
794 else /* lea nnnn(reg),%rsp */
796 context
->Rsp
= get_int_reg( context
, (pc
[1] & 7) + (rex
& 1) * 8 ) + *(LONG
*)(pc
+ 2);
797 pc
+= 2 + sizeof(LONG
);
800 case 0xc2: /* ret $nn */
801 context
->Rip
= *(ULONG64
*)context
->Rsp
;
802 context
->Rsp
+= sizeof(ULONG64
) + *(WORD
*)(pc
+ 1);
805 case 0xf3: /* rep; ret */
806 context
->Rip
= *(ULONG64
*)context
->Rsp
;
807 context
->Rsp
+= sizeof(ULONG64
);
809 case 0xe9: /* jmp nnnn */
810 pc
+= 5 + *(LONG
*)(pc
+ 1);
812 case 0xeb: /* jmp n */
813 pc
+= 2 + (signed char)pc
[1];
820 /**********************************************************************
821 * RtlVirtualUnwind (NTDLL.@)
823 PVOID WINAPI
RtlVirtualUnwind( ULONG type
, ULONG64 base
, ULONG64 pc
,
824 RUNTIME_FUNCTION
*function
, CONTEXT
*context
,
825 PVOID
*data
, ULONG64
*frame_ret
,
826 KNONVOLATILE_CONTEXT_POINTERS
*ctx_ptr
)
828 union handler_data
*handler_data
;
830 struct UNWIND_INFO
*info
;
831 unsigned int i
, prolog_offset
;
832 BOOL mach_frame
= FALSE
;
834 TRACE( "type %x rip %p rsp %p\n", type
, (void *)pc
, (void *)context
->Rsp
);
835 if (TRACE_ON(seh
)) dump_unwind_info( base
, function
);
837 frame
= *frame_ret
= context
->Rsp
;
840 info
= (struct UNWIND_INFO
*)((char *)base
+ function
->UnwindData
);
841 handler_data
= (union handler_data
*)&info
->opcodes
[(info
->count
+ 1) & ~1];
843 if (info
->version
!= 1 && info
->version
!= 2)
845 FIXME( "unknown unwind info version %u at %p\n", info
->version
, info
);
850 frame
= get_int_reg( context
, info
->frame_reg
) - info
->frame_offset
* 16;
852 /* check if in prolog */
853 if (pc
>= base
+ function
->BeginAddress
&& pc
< base
+ function
->BeginAddress
+ info
->prolog
)
855 TRACE("inside prolog.\n");
856 prolog_offset
= pc
- base
- function
->BeginAddress
;
861 /* Since Win10 1809 epilogue does not have a special treatment in case of zero opcode count. */
862 if (info
->count
&& is_inside_epilog( (BYTE
*)pc
, base
, function
))
864 TRACE("inside epilog.\n");
865 interpret_epilog( (BYTE
*)pc
, context
, ctx_ptr
);
871 for (i
= 0; i
< info
->count
; i
+= get_opcode_size(info
->opcodes
[i
]))
873 if (prolog_offset
< info
->opcodes
[i
].offset
) continue; /* skip it */
875 switch (info
->opcodes
[i
].code
)
877 case UWOP_PUSH_NONVOL
: /* pushq %reg */
878 set_int_reg( context
, ctx_ptr
, info
->opcodes
[i
].info
, (ULONG64
*)context
->Rsp
);
879 context
->Rsp
+= sizeof(ULONG64
);
881 case UWOP_ALLOC_LARGE
: /* subq $nn,%rsp */
882 if (info
->opcodes
[i
].info
) context
->Rsp
+= *(DWORD
*)&info
->opcodes
[i
+1];
883 else context
->Rsp
+= *(USHORT
*)&info
->opcodes
[i
+1] * 8;
885 case UWOP_ALLOC_SMALL
: /* subq $n,%rsp */
886 context
->Rsp
+= (info
->opcodes
[i
].info
+ 1) * 8;
888 case UWOP_SET_FPREG
: /* leaq nn(%rsp),%framereg */
889 context
->Rsp
= *frame_ret
= frame
;
891 case UWOP_SAVE_NONVOL
: /* movq %reg,n(%rsp) */
892 off
= frame
+ *(USHORT
*)&info
->opcodes
[i
+1] * 8;
893 set_int_reg( context
, ctx_ptr
, info
->opcodes
[i
].info
, (ULONG64
*)off
);
895 case UWOP_SAVE_NONVOL_FAR
: /* movq %reg,nn(%rsp) */
896 off
= frame
+ *(DWORD
*)&info
->opcodes
[i
+1];
897 set_int_reg( context
, ctx_ptr
, info
->opcodes
[i
].info
, (ULONG64
*)off
);
899 case UWOP_SAVE_XMM128
: /* movaps %xmmreg,n(%rsp) */
900 off
= frame
+ *(USHORT
*)&info
->opcodes
[i
+1] * 16;
901 set_float_reg( context
, ctx_ptr
, info
->opcodes
[i
].info
, (M128A
*)off
);
903 case UWOP_SAVE_XMM128_FAR
: /* movaps %xmmreg,nn(%rsp) */
904 off
= frame
+ *(DWORD
*)&info
->opcodes
[i
+1];
905 set_float_reg( context
, ctx_ptr
, info
->opcodes
[i
].info
, (M128A
*)off
);
907 case UWOP_PUSH_MACHFRAME
:
908 if (info
->flags
& UNW_FLAG_CHAININFO
)
910 FIXME("PUSH_MACHFRAME with chained unwind info.\n");
913 if (i
+ get_opcode_size(info
->opcodes
[i
]) < info
->count
)
915 FIXME("PUSH_MACHFRAME is not the last opcode.\n");
919 if (info
->opcodes
[i
].info
)
922 context
->Rip
= *(ULONG64
*)context
->Rsp
;
923 context
->Rsp
= *(ULONG64
*)(context
->Rsp
+ 24);
927 if (info
->version
== 2)
928 break; /* nothing to do */
930 FIXME( "unknown code %u\n", info
->opcodes
[i
].code
);
935 if (!(info
->flags
& UNW_FLAG_CHAININFO
)) break;
936 function
= &handler_data
->chain
; /* restart with the chained info */
941 /* now pop return address */
942 context
->Rip
= *(ULONG64
*)context
->Rsp
;
943 context
->Rsp
+= sizeof(ULONG64
);
946 if (!(info
->flags
& type
)) return NULL
; /* no matching handler */
947 if (prolog_offset
!= ~0) return NULL
; /* inside prolog */
949 *data
= &handler_data
->handler
+ 1;
950 return (char *)base
+ handler_data
->handler
;
953 struct unwind_exception_frame
955 EXCEPTION_REGISTRATION_RECORD frame
;
956 DISPATCHER_CONTEXT
*dispatch
;
959 /**********************************************************************
960 * unwind_exception_handler
962 * Handler for exceptions happening while calling an unwind handler.
964 static DWORD __cdecl
unwind_exception_handler( EXCEPTION_RECORD
*rec
, EXCEPTION_REGISTRATION_RECORD
*frame
,
965 CONTEXT
*context
, EXCEPTION_REGISTRATION_RECORD
**dispatcher
)
967 struct unwind_exception_frame
*unwind_frame
= (struct unwind_exception_frame
*)frame
;
968 DISPATCHER_CONTEXT
*dispatch
= (DISPATCHER_CONTEXT
*)dispatcher
;
970 /* copy the original dispatcher into the current one, except for the TargetIp */
971 dispatch
->ControlPc
= unwind_frame
->dispatch
->ControlPc
;
972 dispatch
->ImageBase
= unwind_frame
->dispatch
->ImageBase
;
973 dispatch
->FunctionEntry
= unwind_frame
->dispatch
->FunctionEntry
;
974 dispatch
->EstablisherFrame
= unwind_frame
->dispatch
->EstablisherFrame
;
975 dispatch
->ContextRecord
= unwind_frame
->dispatch
->ContextRecord
;
976 dispatch
->LanguageHandler
= unwind_frame
->dispatch
->LanguageHandler
;
977 dispatch
->HandlerData
= unwind_frame
->dispatch
->HandlerData
;
978 dispatch
->HistoryTable
= unwind_frame
->dispatch
->HistoryTable
;
979 dispatch
->ScopeIndex
= unwind_frame
->dispatch
->ScopeIndex
;
980 TRACE( "detected collided unwind\n" );
981 return ExceptionCollidedUnwind
;
984 /**********************************************************************
985 * call_unwind_handler
987 * Call a single unwind handler.
989 static DWORD
call_unwind_handler( EXCEPTION_RECORD
*rec
, DISPATCHER_CONTEXT
*dispatch
)
991 struct unwind_exception_frame frame
;
994 frame
.frame
.Handler
= unwind_exception_handler
;
995 frame
.dispatch
= dispatch
;
996 __wine_push_frame( &frame
.frame
);
998 TRACE( "calling handler %p (rec=%p, frame=%p context=%p, dispatch=%p)\n",
999 dispatch
->LanguageHandler
, rec
, (void *)dispatch
->EstablisherFrame
, dispatch
->ContextRecord
, dispatch
);
1000 res
= dispatch
->LanguageHandler( rec
, (void *)dispatch
->EstablisherFrame
, dispatch
->ContextRecord
, dispatch
);
1001 TRACE( "handler %p returned %x\n", dispatch
->LanguageHandler
, res
);
1003 __wine_pop_frame( &frame
.frame
);
1007 case ExceptionContinueSearch
:
1008 case ExceptionCollidedUnwind
:
1011 raise_status( STATUS_INVALID_DISPOSITION
, rec
);
1019 /**********************************************************************
1020 * call_teb_unwind_handler
1022 * Call a single unwind handler from the TEB chain.
1024 static DWORD
call_teb_unwind_handler( EXCEPTION_RECORD
*rec
, DISPATCHER_CONTEXT
*dispatch
,
1025 EXCEPTION_REGISTRATION_RECORD
*teb_frame
)
1029 TRACE( "calling TEB handler %p (rec=%p, frame=%p context=%p, dispatch=%p)\n",
1030 teb_frame
->Handler
, rec
, teb_frame
, dispatch
->ContextRecord
, dispatch
);
1031 res
= teb_frame
->Handler( rec
, teb_frame
, dispatch
->ContextRecord
, (EXCEPTION_REGISTRATION_RECORD
**)dispatch
);
1032 TRACE( "handler at %p returned %u\n", teb_frame
->Handler
, res
);
1036 case ExceptionContinueSearch
:
1037 case ExceptionCollidedUnwind
:
1040 raise_status( STATUS_INVALID_DISPOSITION
, rec
);
1048 /**********************************************************************
1049 * call_consolidate_callback
1051 * Wrapper function to call a consolidate callback from a fake frame.
1052 * If the callback executes RtlUnwindEx (like for example done in C++ handlers),
1053 * we have to skip all frames which were already processed. To do that we
1054 * trick the unwinding functions into thinking the call came from the specified
1055 * context. All CFI instructions are either DW_CFA_def_cfa_expression or
1056 * DW_CFA_expression, and the expressions have the following format:
1058 * DW_OP_breg6; sleb128 0x10 | Load %rbp + 0x10
1059 * DW_OP_deref | Get *(%rbp + 0x10) == context
1060 * DW_OP_plus_uconst; uleb128 <OFFSET> | Add offset to get struct member
1061 * [DW_OP_deref] | Dereference, only for CFA
1063 extern void * WINAPI
call_consolidate_callback( CONTEXT
*context
,
1064 void *(CALLBACK
*callback
)(EXCEPTION_RECORD
*),
1065 EXCEPTION_RECORD
*rec
);
1066 __ASM_GLOBAL_FUNC( call_consolidate_callback
,
1068 __ASM_CFI(".cfi_adjust_cfa_offset 8\n\t")
1069 __ASM_CFI(".cfi_rel_offset %rbp,0\n\t")
1070 "movq %rsp,%rbp\n\t"
1071 __ASM_CFI(".cfi_def_cfa_register %rbp\n\t")
1073 /* Setup SEH machine frame. */
1074 "subq $0x28,%rsp\n\t"
1075 __ASM_CFI(".cfi_adjust_cfa_offset 0x28\n\t")
1076 "movq 0xf8(%rcx),%rax\n\t" /* Context->Rip */
1077 "movq %rax,(%rsp)\n\t"
1078 "movq 0x98(%rcx),%rax\n\t" /* context->Rsp */
1079 "movq %rax,0x18(%rsp)\n\t"
1080 __ASM_SEH(".seh_pushframe\n\t")
1081 __ASM_SEH(".seh_endprologue\n\t")
1083 "subq $0x108,%rsp\n\t" /* 10*16 (float regs) + 8*8 (int regs) + 32 (shadow store) + 8 (align). */
1084 __ASM_SEH(".seh_stackalloc 0x108\n\t")
1085 __ASM_CFI(".cfi_adjust_cfa_offset 0x108\n\t")
1087 /* Setup CFI unwind to context. */
1088 "movq %rcx,0x10(%rbp)\n\t"
1089 __ASM_CFI(".cfi_remember_state\n\t")
1090 __ASM_CFI(".cfi_escape 0x0f,0x07,0x76,0x10,0x06,0x23,0x98,0x01,0x06\n\t") /* CFA */
1091 __ASM_CFI(".cfi_escape 0x10,0x03,0x06,0x76,0x10,0x06,0x23,0x90,0x01\n\t") /* %rbx */
1092 __ASM_CFI(".cfi_escape 0x10,0x04,0x06,0x76,0x10,0x06,0x23,0xa8,0x01\n\t") /* %rsi */
1093 __ASM_CFI(".cfi_escape 0x10,0x05,0x06,0x76,0x10,0x06,0x23,0xb0,0x01\n\t") /* %rdi */
1094 __ASM_CFI(".cfi_escape 0x10,0x06,0x06,0x76,0x10,0x06,0x23,0xa0,0x01\n\t") /* %rbp */
1095 __ASM_CFI(".cfi_escape 0x10,0x0c,0x06,0x76,0x10,0x06,0x23,0xd8,0x01\n\t") /* %r12 */
1096 __ASM_CFI(".cfi_escape 0x10,0x0d,0x06,0x76,0x10,0x06,0x23,0xe0,0x01\n\t") /* %r13 */
1097 __ASM_CFI(".cfi_escape 0x10,0x0e,0x06,0x76,0x10,0x06,0x23,0xe8,0x01\n\t") /* %r14 */
1098 __ASM_CFI(".cfi_escape 0x10,0x0f,0x06,0x76,0x10,0x06,0x23,0xf0,0x01\n\t") /* %r15 */
1099 __ASM_CFI(".cfi_escape 0x10,0x10,0x06,0x76,0x10,0x06,0x23,0xf8,0x01\n\t") /* %rip */
1100 __ASM_CFI(".cfi_escape 0x10,0x17,0x06,0x76,0x10,0x06,0x23,0x80,0x04\n\t") /* %xmm6 */
1101 __ASM_CFI(".cfi_escape 0x10,0x18,0x06,0x76,0x10,0x06,0x23,0x90,0x04\n\t") /* %xmm7 */
1102 __ASM_CFI(".cfi_escape 0x10,0x19,0x06,0x76,0x10,0x06,0x23,0xa0,0x04\n\t") /* %xmm8 */
1103 __ASM_CFI(".cfi_escape 0x10,0x1a,0x06,0x76,0x10,0x06,0x23,0xb0,0x04\n\t") /* %xmm9 */
1104 __ASM_CFI(".cfi_escape 0x10,0x1b,0x06,0x76,0x10,0x06,0x23,0xc0,0x04\n\t") /* %xmm10 */
1105 __ASM_CFI(".cfi_escape 0x10,0x1c,0x06,0x76,0x10,0x06,0x23,0xd0,0x04\n\t") /* %xmm11 */
1106 __ASM_CFI(".cfi_escape 0x10,0x1d,0x06,0x76,0x10,0x06,0x23,0xe0,0x04\n\t") /* %xmm12 */
1107 __ASM_CFI(".cfi_escape 0x10,0x1e,0x06,0x76,0x10,0x06,0x23,0xf0,0x04\n\t") /* %xmm13 */
1108 __ASM_CFI(".cfi_escape 0x10,0x1f,0x06,0x76,0x10,0x06,0x23,0x80,0x05\n\t") /* %xmm14 */
1109 __ASM_CFI(".cfi_escape 0x10,0x20,0x06,0x76,0x10,0x06,0x23,0x90,0x05\n\t") /* %xmm15 */
1111 /* Setup SEH unwind registers restore. */
1112 "movq 0xa0(%rcx),%rax\n\t" /* context->Rbp */
1113 "movq %rax,0x100(%rsp)\n\t"
1114 __ASM_SEH(".seh_savereg %rbp, 0x100\n\t")
1115 "movq 0x90(%rcx),%rax\n\t" /* context->Rbx */
1116 "movq %rax,0x20(%rsp)\n\t"
1117 __ASM_SEH(".seh_savereg %rbx, 0x20\n\t")
1118 "movq 0xa8(%rcx),%rax\n\t" /* context->Rsi */
1119 "movq %rax,0x28(%rsp)\n\t"
1120 __ASM_SEH(".seh_savereg %rsi, 0x28\n\t")
1121 "movq 0xb0(%rcx),%rax\n\t" /* context->Rdi */
1122 "movq %rax,0x30(%rsp)\n\t"
1123 __ASM_SEH(".seh_savereg %rdi, 0x30\n\t")
1125 "movq 0xd8(%rcx),%rax\n\t" /* context->R12 */
1126 "movq %rax,0x38(%rsp)\n\t"
1127 __ASM_SEH(".seh_savereg %r12, 0x38\n\t")
1128 "movq 0xe0(%rcx),%rax\n\t" /* context->R13 */
1129 "movq %rax,0x40(%rsp)\n\t"
1130 __ASM_SEH(".seh_savereg %r13, 0x40\n\t")
1131 "movq 0xe8(%rcx),%rax\n\t" /* context->R14 */
1132 "movq %rax,0x48(%rsp)\n\t"
1133 __ASM_SEH(".seh_savereg %r14, 0x48\n\t")
1134 "movq 0xf0(%rcx),%rax\n\t" /* context->R15 */
1135 "movq %rax,0x50(%rsp)\n\t"
1136 __ASM_SEH(".seh_savereg %r15, 0x50\n\t")
1139 "leaq 0x200(%rcx),%rsi\n\t"
1140 "leaq 0x70(%rsp),%rdi\n\t"
1141 "movq $0x14,%rcx\n\t"
1146 __ASM_SEH(".seh_savexmm %xmm6, 0x60\n\t")
1147 __ASM_SEH(".seh_savexmm %xmm7, 0x70\n\t")
1148 __ASM_SEH(".seh_savexmm %xmm8, 0x80\n\t")
1149 __ASM_SEH(".seh_savexmm %xmm9, 0x90\n\t")
1150 __ASM_SEH(".seh_savexmm %xmm10, 0xa0\n\t")
1151 __ASM_SEH(".seh_savexmm %xmm11, 0xb0\n\t")
1152 __ASM_SEH(".seh_savexmm %xmm12, 0xc0\n\t")
1153 __ASM_SEH(".seh_savexmm %xmm13, 0xd0\n\t")
1154 __ASM_SEH(".seh_savexmm %xmm14, 0xe0\n\t")
1155 __ASM_SEH(".seh_savexmm %xmm15, 0xf0\n\t")
1157 /* call the callback. */
1160 __ASM_CFI(".cfi_restore_state\n\t")
1161 "nop\n\t" /* Otherwise RtlVirtualUnwind() will think we are inside epilogue and
1162 * interpret / execute the rest of opcodes here instead of unwind through
1164 "leaq 0(%rbp),%rsp\n\t"
1165 __ASM_CFI(".cfi_def_cfa_register %rsp\n\t")
1167 __ASM_CFI(".cfi_adjust_cfa_offset -8\n\t")
1168 __ASM_CFI(".cfi_same_value %rbp\n\t")
1171 /*******************************************************************
1172 * RtlRestoreContext (NTDLL.@)
1174 void CDECL
RtlRestoreContext( CONTEXT
*context
, EXCEPTION_RECORD
*rec
)
1176 EXCEPTION_REGISTRATION_RECORD
*teb_frame
= NtCurrentTeb()->Tib
.ExceptionList
;
1178 if (rec
&& rec
->ExceptionCode
== STATUS_LONGJUMP
&& rec
->NumberParameters
>= 1)
1180 struct MSVCRT_JUMP_BUFFER
*jmp
= (struct MSVCRT_JUMP_BUFFER
*)rec
->ExceptionInformation
[0];
1181 context
->Rbx
= jmp
->Rbx
;
1182 context
->Rsp
= jmp
->Rsp
;
1183 context
->Rbp
= jmp
->Rbp
;
1184 context
->Rsi
= jmp
->Rsi
;
1185 context
->Rdi
= jmp
->Rdi
;
1186 context
->R12
= jmp
->R12
;
1187 context
->R13
= jmp
->R13
;
1188 context
->R14
= jmp
->R14
;
1189 context
->R15
= jmp
->R15
;
1190 context
->Rip
= jmp
->Rip
;
1191 context
->u
.s
.Xmm6
= jmp
->Xmm6
;
1192 context
->u
.s
.Xmm7
= jmp
->Xmm7
;
1193 context
->u
.s
.Xmm8
= jmp
->Xmm8
;
1194 context
->u
.s
.Xmm9
= jmp
->Xmm9
;
1195 context
->u
.s
.Xmm10
= jmp
->Xmm10
;
1196 context
->u
.s
.Xmm11
= jmp
->Xmm11
;
1197 context
->u
.s
.Xmm12
= jmp
->Xmm12
;
1198 context
->u
.s
.Xmm13
= jmp
->Xmm13
;
1199 context
->u
.s
.Xmm14
= jmp
->Xmm14
;
1200 context
->u
.s
.Xmm15
= jmp
->Xmm15
;
1202 else if (rec
&& rec
->ExceptionCode
== STATUS_UNWIND_CONSOLIDATE
&& rec
->NumberParameters
>= 1)
1204 PVOID (CALLBACK
*consolidate
)(EXCEPTION_RECORD
*) = (void *)rec
->ExceptionInformation
[0];
1205 TRACE( "calling consolidate callback %p (rec=%p)\n", consolidate
, rec
);
1206 context
->Rip
= (ULONG64
)call_consolidate_callback( context
, consolidate
, rec
);
1209 /* hack: remove no longer accessible TEB frames */
1210 while ((ULONG64
)teb_frame
< context
->Rsp
)
1212 TRACE( "removing TEB frame: %p\n", teb_frame
);
1213 teb_frame
= __wine_pop_frame( teb_frame
);
1216 TRACE( "returning to %p stack %p\n", (void *)context
->Rip
, (void *)context
->Rsp
);
1217 NtContinue( context
, FALSE
);
1221 /*******************************************************************
1222 * RtlUnwindEx (NTDLL.@)
1224 void WINAPI
RtlUnwindEx( PVOID end_frame
, PVOID target_ip
, EXCEPTION_RECORD
*rec
,
1225 PVOID retval
, CONTEXT
*context
, UNWIND_HISTORY_TABLE
*table
)
1227 EXCEPTION_REGISTRATION_RECORD
*teb_frame
= NtCurrentTeb()->Tib
.ExceptionList
;
1228 EXCEPTION_RECORD record
;
1229 DISPATCHER_CONTEXT dispatch
;
1230 CONTEXT new_context
;
1234 RtlCaptureContext( context
);
1235 new_context
= *context
;
1237 /* build an exception record, if we do not have one */
1240 record
.ExceptionCode
= STATUS_UNWIND
;
1241 record
.ExceptionFlags
= 0;
1242 record
.ExceptionRecord
= NULL
;
1243 record
.ExceptionAddress
= (void *)context
->Rip
;
1244 record
.NumberParameters
= 0;
1248 rec
->ExceptionFlags
|= EH_UNWINDING
| (end_frame
? 0 : EH_EXIT_UNWIND
);
1250 TRACE( "code=%x flags=%x end_frame=%p target_ip=%p rip=%016lx\n",
1251 rec
->ExceptionCode
, rec
->ExceptionFlags
, end_frame
, target_ip
, context
->Rip
);
1252 for (i
= 0; i
< min( EXCEPTION_MAXIMUM_PARAMETERS
, rec
->NumberParameters
); i
++)
1253 TRACE( " info[%d]=%016lx\n", i
, rec
->ExceptionInformation
[i
] );
1254 TRACE(" rax=%016lx rbx=%016lx rcx=%016lx rdx=%016lx\n",
1255 context
->Rax
, context
->Rbx
, context
->Rcx
, context
->Rdx
);
1256 TRACE(" rsi=%016lx rdi=%016lx rbp=%016lx rsp=%016lx\n",
1257 context
->Rsi
, context
->Rdi
, context
->Rbp
, context
->Rsp
);
1258 TRACE(" r8=%016lx r9=%016lx r10=%016lx r11=%016lx\n",
1259 context
->R8
, context
->R9
, context
->R10
, context
->R11
);
1260 TRACE(" r12=%016lx r13=%016lx r14=%016lx r15=%016lx\n",
1261 context
->R12
, context
->R13
, context
->R14
, context
->R15
);
1263 dispatch
.EstablisherFrame
= context
->Rsp
;
1264 dispatch
.TargetIp
= (ULONG64
)target_ip
;
1265 dispatch
.ContextRecord
= context
;
1266 dispatch
.HistoryTable
= table
;
1270 status
= virtual_unwind( UNW_FLAG_UHANDLER
, &dispatch
, &new_context
);
1271 if (status
!= STATUS_SUCCESS
) raise_status( status
, rec
);
1274 if (!dispatch
.EstablisherFrame
) break;
1276 if ((dispatch
.EstablisherFrame
& 7) ||
1277 dispatch
.EstablisherFrame
< (ULONG64
)NtCurrentTeb()->Tib
.StackLimit
||
1278 dispatch
.EstablisherFrame
> (ULONG64
)NtCurrentTeb()->Tib
.StackBase
)
1280 ERR( "invalid frame %p (%p-%p)\n", (void *)dispatch
.EstablisherFrame
,
1281 NtCurrentTeb()->Tib
.StackLimit
, NtCurrentTeb()->Tib
.StackBase
);
1282 rec
->ExceptionFlags
|= EH_STACK_INVALID
;
1286 if (dispatch
.LanguageHandler
)
1288 if (end_frame
&& (dispatch
.EstablisherFrame
> (ULONG64
)end_frame
))
1290 ERR( "invalid end frame %p/%p\n", (void *)dispatch
.EstablisherFrame
, end_frame
);
1291 raise_status( STATUS_INVALID_UNWIND_TARGET
, rec
);
1293 if (dispatch
.EstablisherFrame
== (ULONG64
)end_frame
) rec
->ExceptionFlags
|= EH_TARGET_UNWIND
;
1294 if (call_unwind_handler( rec
, &dispatch
) == ExceptionCollidedUnwind
)
1298 new_context
= *dispatch
.ContextRecord
;
1299 new_context
.ContextFlags
&= ~0x40;
1300 *context
= new_context
;
1301 dispatch
.ContextRecord
= context
;
1302 RtlVirtualUnwind( UNW_FLAG_NHANDLER
, dispatch
.ImageBase
,
1303 dispatch
.ControlPc
, dispatch
.FunctionEntry
,
1304 &new_context
, NULL
, &frame
, NULL
);
1305 rec
->ExceptionFlags
|= EH_COLLIDED_UNWIND
;
1308 rec
->ExceptionFlags
&= ~EH_COLLIDED_UNWIND
;
1310 else /* hack: call builtin handlers registered in the tib list */
1312 DWORD64 backup_frame
= dispatch
.EstablisherFrame
;
1313 while ((ULONG64
)teb_frame
< new_context
.Rsp
&& (ULONG64
)teb_frame
< (ULONG64
)end_frame
)
1315 TRACE( "found builtin frame %p handler %p\n", teb_frame
, teb_frame
->Handler
);
1316 dispatch
.EstablisherFrame
= (ULONG64
)teb_frame
;
1317 if (call_teb_unwind_handler( rec
, &dispatch
, teb_frame
) == ExceptionCollidedUnwind
)
1321 teb_frame
= __wine_pop_frame( teb_frame
);
1323 new_context
= *dispatch
.ContextRecord
;
1324 new_context
.ContextFlags
&= ~0x40;
1325 *context
= new_context
;
1326 dispatch
.ContextRecord
= context
;
1327 RtlVirtualUnwind( UNW_FLAG_NHANDLER
, dispatch
.ImageBase
,
1328 dispatch
.ControlPc
, dispatch
.FunctionEntry
,
1329 &new_context
, NULL
, &frame
, NULL
);
1330 rec
->ExceptionFlags
|= EH_COLLIDED_UNWIND
;
1333 teb_frame
= __wine_pop_frame( teb_frame
);
1335 if ((ULONG64
)teb_frame
== (ULONG64
)end_frame
&& (ULONG64
)end_frame
< new_context
.Rsp
) break;
1336 dispatch
.EstablisherFrame
= backup_frame
;
1339 if (dispatch
.EstablisherFrame
== (ULONG64
)end_frame
) break;
1340 *context
= new_context
;
1343 context
->Rax
= (ULONG64
)retval
;
1344 context
->Rip
= (ULONG64
)target_ip
;
1345 RtlRestoreContext(context
, rec
);
1349 /*******************************************************************
1350 * RtlUnwind (NTDLL.@)
1352 void WINAPI
RtlUnwind( void *frame
, void *target_ip
, EXCEPTION_RECORD
*rec
, void *retval
)
1355 RtlUnwindEx( frame
, target_ip
, rec
, retval
, &context
, NULL
);
1359 /*******************************************************************
1360 * _local_unwind (NTDLL.@)
1362 void WINAPI
_local_unwind( void *frame
, void *target_ip
)
1365 RtlUnwindEx( frame
, target_ip
, NULL
, NULL
, &context
, NULL
);
1368 /*******************************************************************
1369 * __C_specific_handler (NTDLL.@)
1371 EXCEPTION_DISPOSITION WINAPI
__C_specific_handler( EXCEPTION_RECORD
*rec
,
1374 struct _DISPATCHER_CONTEXT
*dispatch
)
1376 SCOPE_TABLE
*table
= dispatch
->HandlerData
;
1379 TRACE( "%p %p %p %p\n", rec
, frame
, context
, dispatch
);
1380 if (TRACE_ON(seh
)) dump_scope_table( dispatch
->ImageBase
, table
);
1382 if (rec
->ExceptionFlags
& (EH_UNWINDING
| EH_EXIT_UNWIND
))
1384 for (i
= dispatch
->ScopeIndex
; i
< table
->Count
; i
++)
1386 if (dispatch
->ControlPc
>= dispatch
->ImageBase
+ table
->ScopeRecord
[i
].BeginAddress
&&
1387 dispatch
->ControlPc
< dispatch
->ImageBase
+ table
->ScopeRecord
[i
].EndAddress
)
1389 PTERMINATION_HANDLER handler
;
1391 if (table
->ScopeRecord
[i
].JumpTarget
) continue;
1393 if (rec
->ExceptionFlags
& EH_TARGET_UNWIND
&&
1394 dispatch
->TargetIp
>= dispatch
->ImageBase
+ table
->ScopeRecord
[i
].BeginAddress
&&
1395 dispatch
->TargetIp
< dispatch
->ImageBase
+ table
->ScopeRecord
[i
].EndAddress
)
1400 handler
= (PTERMINATION_HANDLER
)(dispatch
->ImageBase
+ table
->ScopeRecord
[i
].HandlerAddress
);
1401 dispatch
->ScopeIndex
= i
+1;
1403 TRACE( "calling __finally %p frame %p\n", handler
, frame
);
1404 handler( TRUE
, frame
);
1407 return ExceptionContinueSearch
;
1410 for (i
= dispatch
->ScopeIndex
; i
< table
->Count
; i
++)
1412 if (dispatch
->ControlPc
>= dispatch
->ImageBase
+ table
->ScopeRecord
[i
].BeginAddress
&&
1413 dispatch
->ControlPc
< dispatch
->ImageBase
+ table
->ScopeRecord
[i
].EndAddress
)
1415 if (!table
->ScopeRecord
[i
].JumpTarget
) continue;
1416 if (table
->ScopeRecord
[i
].HandlerAddress
!= EXCEPTION_EXECUTE_HANDLER
)
1418 EXCEPTION_POINTERS ptrs
;
1419 PEXCEPTION_FILTER filter
;
1421 filter
= (PEXCEPTION_FILTER
)(dispatch
->ImageBase
+ table
->ScopeRecord
[i
].HandlerAddress
);
1422 ptrs
.ExceptionRecord
= rec
;
1423 ptrs
.ContextRecord
= context
;
1424 TRACE( "calling filter %p ptrs %p frame %p\n", filter
, &ptrs
, frame
);
1425 switch (filter( &ptrs
, frame
))
1427 case EXCEPTION_EXECUTE_HANDLER
:
1429 case EXCEPTION_CONTINUE_SEARCH
:
1431 case EXCEPTION_CONTINUE_EXECUTION
:
1432 return ExceptionContinueExecution
;
1435 TRACE( "unwinding to target %p\n", (char *)dispatch
->ImageBase
+ table
->ScopeRecord
[i
].JumpTarget
);
1436 RtlUnwindEx( frame
, (char *)dispatch
->ImageBase
+ table
->ScopeRecord
[i
].JumpTarget
,
1437 rec
, 0, dispatch
->ContextRecord
, dispatch
->HistoryTable
);
1440 return ExceptionContinueSearch
;
1444 /***********************************************************************
1445 * RtlRaiseException (NTDLL.@)
1447 __ASM_GLOBAL_FUNC( RtlRaiseException
,
1448 "sub $0x4f8,%rsp\n\t"
1449 __ASM_SEH(".seh_stackalloc 0x4f8\n\t")
1450 __ASM_SEH(".seh_endprologue\n\t")
1451 __ASM_CFI(".cfi_adjust_cfa_offset 0x4f8\n\t")
1452 "movq %rcx,0x500(%rsp)\n\t"
1453 "leaq 0x20(%rsp),%rcx\n\t"
1454 "call " __ASM_NAME("RtlCaptureContext") "\n\t"
1455 "leaq 0x20(%rsp),%rdx\n\t" /* context pointer */
1456 "leaq 0x500(%rsp),%rax\n\t" /* orig stack pointer */
1457 "movq %rax,0x98(%rdx)\n\t" /* context->Rsp */
1458 "movq (%rax),%rcx\n\t" /* original first parameter */
1459 "movq %rcx,0x80(%rdx)\n\t" /* context->Rcx */
1460 "movq 0x4f8(%rsp),%rax\n\t" /* return address */
1461 "movq %rax,0xf8(%rdx)\n\t" /* context->Rip */
1462 "movq %rax,0x10(%rcx)\n\t" /* rec->ExceptionAddress */
1464 "movq %gs:(0x30),%rax\n\t" /* Teb */
1465 "movq 0x60(%rax),%rax\n\t" /* Peb */
1466 "cmpb $0,0x02(%rax)\n\t" /* BeingDebugged */
1468 "call " __ASM_NAME("dispatch_exception") "\n"
1469 "1:\tcall " __ASM_NAME("NtRaiseException") "\n\t"
1470 "movq %rax,%rcx\n\t"
1471 "call " __ASM_NAME("RtlRaiseStatus") /* does not return */ );
1474 static inline ULONG
hash_pointers( void **ptrs
, ULONG count
)
1476 /* Based on MurmurHash2, which is in the public domain */
1477 static const ULONG m
= 0x5bd1e995;
1478 static const ULONG r
= 24;
1479 ULONG hash
= count
* sizeof(void*);
1480 for (; count
> 0; ptrs
++, count
--)
1482 ULONG_PTR data
= (ULONG_PTR
)*ptrs
;
1483 ULONG k1
= (ULONG
)(data
& 0xffffffff), k2
= (ULONG
)(data
>> 32);
1485 k1
= (k1
^ (k1
>> r
)) * m
;
1487 k2
= (k2
^ (k2
>> r
)) * m
;
1488 hash
= (((hash
* m
) ^ k1
) * m
) ^ k2
;
1490 hash
= (hash
^ (hash
>> 13)) * m
;
1491 return hash
^ (hash
>> 15);
1495 /*************************************************************************
1496 * RtlCaptureStackBackTrace (NTDLL.@)
1498 USHORT WINAPI
RtlCaptureStackBackTrace( ULONG skip
, ULONG count
, PVOID
*buffer
, ULONG
*hash
)
1500 UNWIND_HISTORY_TABLE table
;
1501 DISPATCHER_CONTEXT dispatch
;
1505 USHORT num_entries
= 0;
1507 TRACE( "(%u, %u, %p, %p)\n", skip
, count
, buffer
, hash
);
1509 RtlCaptureContext( &context
);
1510 dispatch
.TargetIp
= 0;
1511 dispatch
.ContextRecord
= &context
;
1512 dispatch
.HistoryTable
= &table
;
1513 if (hash
) *hash
= 0;
1514 for (i
= 0; i
< skip
+ count
; i
++)
1516 status
= virtual_unwind( UNW_FLAG_NHANDLER
, &dispatch
, &context
);
1517 if (status
!= STATUS_SUCCESS
) return i
;
1519 if (!dispatch
.EstablisherFrame
) break;
1521 if ((dispatch
.EstablisherFrame
& 7) ||
1522 dispatch
.EstablisherFrame
< (ULONG64
)NtCurrentTeb()->Tib
.StackLimit
||
1523 dispatch
.EstablisherFrame
> (ULONG64
)NtCurrentTeb()->Tib
.StackBase
)
1525 ERR( "invalid frame %p (%p-%p)\n", (void *)dispatch
.EstablisherFrame
,
1526 NtCurrentTeb()->Tib
.StackLimit
, NtCurrentTeb()->Tib
.StackBase
);
1530 if (context
.Rsp
== (ULONG64
)NtCurrentTeb()->Tib
.StackBase
) break;
1532 if (i
>= skip
) buffer
[num_entries
++] = (void *)context
.Rip
;
1534 if (hash
&& num_entries
> 0) *hash
= hash_pointers( buffer
, num_entries
);
1535 TRACE( "captured %hu frames\n", num_entries
);
1540 /***********************************************************************
1541 * signal_start_thread
1543 __ASM_GLOBAL_FUNC( signal_start_thread
,
1544 "movq %rcx,%rbx\n\t" /* context */
1545 /* clear the thread stack */
1546 "andq $~0xfff,%rcx\n\t" /* round down to page size */
1547 "movq %gs:0x30,%rax\n\t"
1548 "movq 0x10(%rax),%rdi\n\t" /* NtCurrentTeb()->Tib.StackLimit */
1549 "addq $0x2000,%rdi\n\t"
1550 "movq %rdi,%rsp\n\t"
1551 "subq %rdi,%rcx\n\t"
1552 "xorl %eax,%eax\n\t"
1555 /* switch to the initial context */
1556 "leaq -32(%rbx),%rsp\n\t"
1557 "movq %rbx,%rcx\n\t"
1559 "call " __ASM_NAME("NtContinue") )
1562 /**********************************************************************
1563 * DbgBreakPoint (NTDLL.@)
1565 __ASM_STDCALL_FUNC( DbgBreakPoint
, 0, "int $3; ret"
1566 "\n\tnop; nop; nop; nop; nop; nop; nop; nop"
1567 "\n\tnop; nop; nop; nop; nop; nop" );
1569 /**********************************************************************
1570 * DbgUserBreakPoint (NTDLL.@)
1572 __ASM_STDCALL_FUNC( DbgUserBreakPoint
, 0, "int $3; ret"
1573 "\n\tnop; nop; nop; nop; nop; nop; nop; nop"
1574 "\n\tnop; nop; nop; nop; nop; nop" );
1576 #endif /* __x86_64__ */