2 * Unit test suite for ntdll exceptions
4 * Copyright 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
25 #define _WIN32_WINNT 0x500 /* For NTSTATUS */
28 #define NONAMELESSUNION
29 #define NONAMELESSSTRUCT
31 #define WIN32_NO_STATUS
38 #include "wine/test.h"
40 static void *code_mem;
42 static struct _TEB * (WINAPI *pNtCurrentTeb)(void);
43 static NTSTATUS (WINAPI *pNtGetContextThread)(HANDLE,CONTEXT*);
44 static NTSTATUS (WINAPI *pNtSetContextThread)(HANDLE,CONTEXT*);
45 static NTSTATUS (WINAPI *pRtlRaiseException)(EXCEPTION_RECORD *rec);
46 static PVOID (WINAPI *pRtlAddVectoredExceptionHandler)(ULONG first, PVECTORED_EXCEPTION_HANDLER func);
47 static ULONG (WINAPI *pRtlRemoveVectoredExceptionHandler)(PVOID handler);
48 static NTSTATUS (WINAPI *pNtReadVirtualMemory)(HANDLE, const void*, void*, SIZE_T, SIZE_T*);
49 static NTSTATUS (WINAPI *pNtTerminateProcess)(HANDLE handle, LONG exit_code);
53 static char** my_argv;
54 static int test_stage;
56 /* Test various instruction combinations that cause a protection fault on the i386,
57 * and check what the resulting exception looks like.
60 static const struct exception
62 BYTE code[18]; /* asm code */
63 BYTE offset; /* offset of faulting instruction */
64 BYTE length; /* length of faulting instruction */
65 NTSTATUS status; /* expected status code */
66 DWORD nb_params; /* expected number of parameters */
67 DWORD params[4]; /* expected parameters */
70 /* test some privileged instructions */
71 { { 0xfb, 0xc3 }, /* 0: sti; ret */
72 0, 1, STATUS_PRIVILEGED_INSTRUCTION, 0 },
73 { { 0x6c, 0xc3 }, /* 1: insb (%dx); ret */
74 0, 1, STATUS_PRIVILEGED_INSTRUCTION, 0 },
75 { { 0x6d, 0xc3 }, /* 2: insl (%dx); ret */
76 0, 1, STATUS_PRIVILEGED_INSTRUCTION, 0 },
77 { { 0x6e, 0xc3 }, /* 3: outsb (%dx); ret */
78 0, 1, STATUS_PRIVILEGED_INSTRUCTION, 0 },
79 { { 0x6f, 0xc3 }, /* 4: outsl (%dx); ret */
80 0, 1, STATUS_PRIVILEGED_INSTRUCTION, 0 },
81 { { 0xe4, 0x11, 0xc3 }, /* 5: inb $0x11,%al; ret */
82 0, 2, STATUS_PRIVILEGED_INSTRUCTION, 0 },
83 { { 0xe5, 0x11, 0xc3 }, /* 6: inl $0x11,%eax; ret */
84 0, 2, STATUS_PRIVILEGED_INSTRUCTION, 0 },
85 { { 0xe6, 0x11, 0xc3 }, /* 7: outb %al,$0x11; ret */
86 0, 2, STATUS_PRIVILEGED_INSTRUCTION, 0 },
87 { { 0xe7, 0x11, 0xc3 }, /* 8: outl %eax,$0x11; ret */
88 0, 2, STATUS_PRIVILEGED_INSTRUCTION, 0 },
89 { { 0xed, 0xc3 }, /* 9: inl (%dx),%eax; ret */
90 0, 1, STATUS_PRIVILEGED_INSTRUCTION, 0 },
91 { { 0xee, 0xc3 }, /* 10: outb %al,(%dx); ret */
92 0, 1, STATUS_PRIVILEGED_INSTRUCTION, 0 },
93 { { 0xef, 0xc3 }, /* 11: outl %eax,(%dx); ret */
94 0, 1, STATUS_PRIVILEGED_INSTRUCTION, 0 },
95 { { 0xf4, 0xc3 }, /* 12: hlt; ret */
96 0, 1, STATUS_PRIVILEGED_INSTRUCTION, 0 },
97 { { 0xfa, 0xc3 }, /* 13: cli; ret */
98 0, 1, STATUS_PRIVILEGED_INSTRUCTION, 0 },
100 /* test long jump to invalid selector */
101 { { 0xea, 0, 0, 0, 0, 0, 0, 0xc3 }, /* 14: ljmp $0,$0; ret */
102 0, 7, STATUS_ACCESS_VIOLATION, 2, { 0, 0xffffffff } },
104 /* test iret to invalid selector */
105 { { 0x6a, 0x00, 0x6a, 0x00, 0x6a, 0x00, 0xcf, 0x83, 0xc4, 0x0c, 0xc3 },
106 /* 15: pushl $0; pushl $0; pushl $0; iret; addl $12,%esp; ret */
107 6, 1, STATUS_ACCESS_VIOLATION, 2, { 0, 0xffffffff } },
109 /* test loading an invalid selector */
110 { { 0xb8, 0xef, 0xbe, 0x00, 0x00, 0x8e, 0xe8, 0xc3 }, /* 16: mov $beef,%ax; mov %ax,%gs; ret */
111 5, 2, STATUS_ACCESS_VIOLATION, 2, { 0, 0xbee8 } }, /* 0xbee8 or 0xffffffff */
113 /* test accessing a zero selector */
114 { { 0x06, 0x31, 0xc0, 0x8e, 0xc0, 0x26, 0xa1, 0, 0, 0, 0, 0x07, 0xc3 },
115 /* 17: push %es; xor %eax,%eax; mov %ax,%es; mov %es:(0),%ax; pop %es; ret */
116 5, 6, STATUS_ACCESS_VIOLATION, 2, { 0, 0xffffffff } },
118 /* test moving %cs -> %ss */
119 { { 0x0e, 0x17, 0x58, 0xc3 }, /* 18: pushl %cs; popl %ss; popl %eax; ret */
120 1, 1, STATUS_ACCESS_VIOLATION, 2, { 0, 0xffffffff } },
122 /* 19: test overlong instruction (limit is 16 bytes) */
123 { { 0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0xfa,0xc3 },
124 0, 16, STATUS_ILLEGAL_INSTRUCTION, 0 },
125 { { 0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0xfa,0xc3 },
126 0, 15, STATUS_PRIVILEGED_INSTRUCTION, 0 },
128 /* test invalid interrupt */
129 { { 0xcd, 0xff, 0xc3 }, /* 21: int $0xff; ret */
130 0, 2, STATUS_ACCESS_VIOLATION, 2, { 0, 0xffffffff } },
132 /* test moves to/from Crx */
133 { { 0x0f, 0x20, 0xc0, 0xc3 }, /* 22: movl %cr0,%eax; ret */
134 0, 3, STATUS_PRIVILEGED_INSTRUCTION, 0 },
135 { { 0x0f, 0x20, 0xe0, 0xc3 }, /* 23: movl %cr4,%eax; ret */
136 0, 3, STATUS_PRIVILEGED_INSTRUCTION, 0 },
137 { { 0x0f, 0x22, 0xc0, 0xc3 }, /* 24: movl %eax,%cr0; ret */
138 0, 3, STATUS_PRIVILEGED_INSTRUCTION, 0 },
139 { { 0x0f, 0x22, 0xe0, 0xc3 }, /* 25: movl %eax,%cr4; ret */
140 0, 3, STATUS_PRIVILEGED_INSTRUCTION, 0 },
142 /* test moves to/from Drx */
143 { { 0x0f, 0x21, 0xc0, 0xc3 }, /* 26: movl %dr0,%eax; ret */
144 0, 3, STATUS_PRIVILEGED_INSTRUCTION, 0 },
145 { { 0x0f, 0x21, 0xc8, 0xc3 }, /* 27: movl %dr1,%eax; ret */
146 0, 3, STATUS_PRIVILEGED_INSTRUCTION, 0 },
147 { { 0x0f, 0x21, 0xf8, 0xc3 }, /* 28: movl %dr7,%eax; ret */
148 0, 3, STATUS_PRIVILEGED_INSTRUCTION, 0 },
149 { { 0x0f, 0x23, 0xc0, 0xc3 }, /* 29: movl %eax,%dr0; ret */
150 0, 3, STATUS_PRIVILEGED_INSTRUCTION, 0 },
151 { { 0x0f, 0x23, 0xc8, 0xc3 }, /* 30: movl %eax,%dr1; ret */
152 0, 3, STATUS_PRIVILEGED_INSTRUCTION, 0 },
153 { { 0x0f, 0x23, 0xf8, 0xc3 }, /* 31: movl %eax,%dr7; ret */
154 0, 3, STATUS_PRIVILEGED_INSTRUCTION, 0 },
156 /* test memory reads */
157 { { 0xa1, 0xfc, 0xff, 0xff, 0xff, 0xc3 }, /* 32: movl 0xfffffffc,%eax; ret */
158 0, 5, STATUS_ACCESS_VIOLATION, 2, { 0, 0xfffffffc } },
159 { { 0xa1, 0xfd, 0xff, 0xff, 0xff, 0xc3 }, /* 33: movl 0xfffffffd,%eax; ret */
160 0, 5, STATUS_ACCESS_VIOLATION, 2, { 0, 0xfffffffd } },
161 { { 0xa1, 0xfe, 0xff, 0xff, 0xff, 0xc3 }, /* 34: movl 0xfffffffe,%eax; ret */
162 0, 5, STATUS_ACCESS_VIOLATION, 2, { 0, 0xfffffffe } },
163 { { 0xa1, 0xff, 0xff, 0xff, 0xff, 0xc3 }, /* 35: movl 0xffffffff,%eax; ret */
164 0, 5, STATUS_ACCESS_VIOLATION, 2, { 0, 0xffffffff } },
166 /* test memory writes */
167 { { 0xa3, 0xfc, 0xff, 0xff, 0xff, 0xc3 }, /* 36: movl %eax,0xfffffffc; ret */
168 0, 5, STATUS_ACCESS_VIOLATION, 2, { 1, 0xfffffffc } },
169 { { 0xa3, 0xfd, 0xff, 0xff, 0xff, 0xc3 }, /* 37: movl %eax,0xfffffffd; ret */
170 0, 5, STATUS_ACCESS_VIOLATION, 2, { 1, 0xfffffffd } },
171 { { 0xa3, 0xfe, 0xff, 0xff, 0xff, 0xc3 }, /* 38: movl %eax,0xfffffffe; ret */
172 0, 5, STATUS_ACCESS_VIOLATION, 2, { 1, 0xfffffffe } },
173 { { 0xa3, 0xff, 0xff, 0xff, 0xff, 0xc3 }, /* 39: movl %eax,0xffffffff; ret */
174 0, 5, STATUS_ACCESS_VIOLATION, 2, { 1, 0xffffffff } },
176 /* 40: test exception with cleared %ds and %es */
177 { { 0x1e, 0x06, 0x31, 0xc0, 0x8e, 0xd8, 0x8e, 0xc0, 0xfa, 0x07, 0x1f, 0xc3 },
178 /* push %ds; push %es; xorl %eax,%eax; mov %ax,%ds; mov %ax,%es; cli; pop %es; pop %ds; ret */
179 8, 1, STATUS_PRIVILEGED_INSTRUCTION, 0 },
182 static int got_exception;
183 static BOOL have_vectored_api;
185 static void run_exception_test(void *handler, const void* context,
186 const void *code, unsigned int code_size)
189 EXCEPTION_REGISTRATION_RECORD frame;
192 void (*func)(void) = code_mem;
194 exc_frame.frame.Handler = handler;
195 exc_frame.frame.Prev = pNtCurrentTeb()->Tib.ExceptionList;
196 exc_frame.context = context;
198 memcpy(code_mem, code, code_size);
200 pNtCurrentTeb()->Tib.ExceptionList = &exc_frame.frame;
202 pNtCurrentTeb()->Tib.ExceptionList = exc_frame.frame.Prev;
205 static LONG CALLBACK rtlraiseexception_vectored_handler(EXCEPTION_POINTERS *ExceptionInfo)
207 PCONTEXT context = ExceptionInfo->ContextRecord;
208 PEXCEPTION_RECORD rec = ExceptionInfo->ExceptionRecord;
209 trace("vect. handler %08x addr:%p context.Eip:%x\n", rec->ExceptionCode,
210 rec->ExceptionAddress, context->Eip);
212 ok(rec->ExceptionAddress == (char *)code_mem + 0xb, "ExceptionAddress at %p instead of %p\n",
213 rec->ExceptionAddress, (char *)code_mem + 0xb);
215 if (pNtCurrentTeb()->Peb->BeingDebugged)
216 ok((void *)context->Eax == pRtlRaiseException, "debugger managed to modify Eax to %x should be %p\n",
217 context->Eax, pRtlRaiseException);
219 /* check that context.Eip is fixed up only for EXCEPTION_BREAKPOINT
220 * even if raised by RtlRaiseException
222 if(rec->ExceptionCode == EXCEPTION_BREAKPOINT)
224 ok(context->Eip == (DWORD)code_mem + 0xa ||
225 broken(context->Eip == (DWORD)code_mem + 0xb), /* win2k3 */
226 "Eip at %x instead of %x or %x\n", context->Eip,
227 (DWORD)code_mem + 0xa, (DWORD)code_mem + 0xb);
231 ok(context->Eip == (DWORD)code_mem + 0xb, "Eip at %x instead of %x\n",
232 context->Eip, (DWORD)code_mem + 0xb);
235 /* test if context change is preserved from vectored handler to stack handlers */
236 context->Eax = 0xf00f00f0;
237 return EXCEPTION_CONTINUE_SEARCH;
240 static DWORD rtlraiseexception_handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
241 CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher )
243 trace( "exception: %08x flags:%x addr:%p context: Eip:%x\n",
244 rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress, context->Eip );
246 ok(rec->ExceptionAddress == (char *)code_mem + 0xb, "ExceptionAddress at %p instead of %p\n",
247 rec->ExceptionAddress, (char *)code_mem + 0xb);
249 /* check that context.Eip is fixed up only for EXCEPTION_BREAKPOINT
250 * even if raised by RtlRaiseException
252 if(rec->ExceptionCode == EXCEPTION_BREAKPOINT)
254 ok(context->Eip == (DWORD)code_mem + 0xa ||
255 broken(context->Eip == (DWORD)code_mem + 0xb), /* win2k3 */
256 "Eip at %x instead of %x or %x\n", context->Eip,
257 (DWORD)code_mem + 0xa, (DWORD)code_mem + 0xb);
261 ok(context->Eip == (DWORD)code_mem + 0xb, "Eip at %x instead of %x\n",
262 context->Eip, (DWORD)code_mem + 0xb);
265 if(have_vectored_api)
266 ok(context->Eax == 0xf00f00f0, "Eax is %x, should have been set to 0xf00f00f0 in vectored handler\n",
269 /* give the debugger a chance to examine the state a second time */
270 /* without the exception handler changing Eip */
272 return ExceptionContinueSearch;
274 /* Eip in context is decreased by 1
275 * Increase it again, else execution will continue in the middle of a instruction */
276 if(rec->ExceptionCode == EXCEPTION_BREAKPOINT && (context->Eip == (DWORD)code_mem + 0xa))
278 return ExceptionContinueExecution;
282 static const BYTE call_one_arg_code[] = {
283 0x8b, 0x44, 0x24, 0x08, /* mov 0x8(%esp),%eax */
284 0x50, /* push %eax */
285 0x8b, 0x44, 0x24, 0x08, /* mov 0x8(%esp),%eax */
286 0xff, 0xd0, /* call *%eax */
295 static void run_rtlraiseexception_test(DWORD exceptioncode)
297 EXCEPTION_REGISTRATION_RECORD frame;
298 EXCEPTION_RECORD record;
299 PVOID vectored_handler = NULL;
301 void (*func)(void* function, EXCEPTION_RECORD* record) = code_mem;
303 record.ExceptionCode = exceptioncode;
304 record.ExceptionFlags = 0;
305 record.ExceptionRecord = NULL;
306 record.ExceptionAddress = NULL; /* does not matter, copied return address */
307 record.NumberParameters = 0;
309 frame.Handler = rtlraiseexception_handler;
310 frame.Prev = pNtCurrentTeb()->Tib.ExceptionList;
312 memcpy(code_mem, call_one_arg_code, sizeof(call_one_arg_code));
314 pNtCurrentTeb()->Tib.ExceptionList = &frame;
315 if (have_vectored_api)
317 vectored_handler = pRtlAddVectoredExceptionHandler(TRUE, &rtlraiseexception_vectored_handler);
318 ok(vectored_handler != 0, "RtlAddVectoredExceptionHandler failed\n");
321 func(pRtlRaiseException, &record);
322 ok( record.ExceptionAddress == (char *)code_mem + 0x0b,
323 "address set to %p instead of %p\n", record.ExceptionAddress, (char *)code_mem + 0x0b );
325 if (have_vectored_api)
326 pRtlRemoveVectoredExceptionHandler(vectored_handler);
327 pNtCurrentTeb()->Tib.ExceptionList = frame.Prev;
330 static void test_rtlraiseexception(void)
332 if (!pRtlRaiseException)
334 skip("RtlRaiseException not found\n");
338 /* test without debugger */
339 run_rtlraiseexception_test(0x12345);
340 run_rtlraiseexception_test(EXCEPTION_BREAKPOINT);
341 run_rtlraiseexception_test(EXCEPTION_INVALID_HANDLE);
344 static DWORD handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
345 CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher )
347 const struct exception *except = *(const struct exception **)(frame + 1);
348 unsigned int i, entry = except - exceptions;
351 trace( "exception: %x flags:%x addr:%p\n",
352 rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress );
354 ok( rec->ExceptionCode == except->status,
355 "%u: Wrong exception code %x/%x\n", entry, rec->ExceptionCode, except->status );
356 ok( rec->ExceptionAddress == (char*)code_mem + except->offset,
357 "%u: Wrong exception address %p/%p\n", entry,
358 rec->ExceptionAddress, (char*)code_mem + except->offset );
360 ok( rec->NumberParameters == except->nb_params,
361 "%u: Wrong number of parameters %u/%u\n", entry, rec->NumberParameters, except->nb_params );
363 /* Most CPUs (except Intel Core apparently) report a segment limit violation */
364 /* instead of page faults for accesses beyond 0xffffffff */
365 if (except->nb_params == 2 && except->params[1] >= 0xfffffffd)
367 if (rec->ExceptionInformation[0] == 0 && rec->ExceptionInformation[1] == 0xffffffff)
371 /* Seems that both 0xbee8 and 0xfffffffff can be returned in windows */
372 if (except->nb_params == 2 && rec->NumberParameters == 2
373 && except->params[1] == 0xbee8 && rec->ExceptionInformation[1] == 0xffffffff
374 && except->params[0] == rec->ExceptionInformation[0])
379 for (i = 0; i < rec->NumberParameters; i++)
380 ok( rec->ExceptionInformation[i] == except->params[i],
381 "%u: Wrong parameter %d: %lx/%x\n",
382 entry, i, rec->ExceptionInformation[i], except->params[i] );
385 /* don't handle exception if it's not the address we expected */
386 if (rec->ExceptionAddress != (char*)code_mem + except->offset) return ExceptionContinueSearch;
388 context->Eip += except->length;
389 return ExceptionContinueExecution;
392 static void test_prot_fault(void)
396 for (i = 0; i < sizeof(exceptions)/sizeof(exceptions[0]); i++)
399 run_exception_test(handler, &exceptions[i], &exceptions[i].code,
400 sizeof(exceptions[i].code));
401 if (!i && !got_exception)
403 trace( "No exception, assuming win9x, no point in testing further\n" );
406 ok( got_exception == (exceptions[i].status != 0),
407 "%u: bad exception count %d\n", i, got_exception );
411 /* test handling of debug registers */
412 static DWORD dreg_handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
413 CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher )
415 context->Eip += 2; /* Skips the popl (%eax) */
416 context->Dr0 = 0x42424242;
421 context->Dr7 = 0x155;
422 return ExceptionContinueExecution;
425 static const BYTE segfault_code[5] = {
426 0x31, 0xc0, /* xor %eax,%eax */
427 0x8f, 0x00, /* popl (%eax) - cause exception */
431 /* test the single step exception behaviour */
432 static DWORD single_step_handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
433 CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher )
436 ok (!(context->EFlags & 0x100), "eflags has single stepping bit set\n");
438 if( got_exception < 3)
439 context->EFlags |= 0x100; /* single step until popf instruction */
441 /* show that the last single step exception on the popf instruction
442 * (which removed the TF bit), still is a EXCEPTION_SINGLE_STEP exception */
443 ok( rec->ExceptionCode == EXCEPTION_SINGLE_STEP,
444 "exception is not EXCEPTION_SINGLE_STEP: %x\n", rec->ExceptionCode);
447 return ExceptionContinueExecution;
450 static const BYTE single_stepcode[] = {
453 0x0d,0,1,0,0, /* or $0x100,%eax */
454 0x50, /* push %eax */
456 0x35,0,1,0,0, /* xor $0x100,%eax */
457 0x50, /* push %eax */
462 /* Test the alignment check (AC) flag handling. */
463 static const BYTE align_check_code[] = {
464 0x55, /* push %ebp */
465 0x89,0xe5, /* mov %esp,%ebp */
468 0x0d,0,0,4,0, /* or $0x40000,%eax */
469 0x50, /* push %eax */
471 0x89,0xe0, /* mov %esp, %eax */
472 0x8b,0x40,0x1, /* mov 0x1(%eax), %eax - cause exception */
475 0x35,0,0,4,0, /* xor $0x40000,%eax */
476 0x50, /* push %eax */
482 static DWORD align_check_handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
483 CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher )
485 ok (!(context->EFlags & 0x40000), "eflags has AC bit set\n");
487 return ExceptionContinueExecution;
490 /* Test the direction flag handling. */
491 static const BYTE direction_flag_code[] = {
492 0x55, /* push %ebp */
493 0x89,0xe5, /* mov %esp,%ebp */
495 0xfa, /* cli - cause exception */
500 static DWORD direction_flag_handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
501 CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher )
505 __asm__("pushfl; popl %0; cld" : "=r" (flags) );
506 /* older windows versions don't clear DF properly so don't test */
507 if (flags & 0x400) trace( "eflags has DF bit set\n" );
509 ok( context->EFlags & 0x400, "context eflags has DF bit cleared\n" );
511 context->Eip++; /* skip cli */
512 context->EFlags &= ~0x400; /* make sure it is cleared on return */
513 return ExceptionContinueExecution;
516 /* test single stepping over hardware breakpoint */
517 static DWORD bpx_handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
518 CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher )
521 ok( rec->ExceptionCode == EXCEPTION_SINGLE_STEP,
522 "wrong exception code: %x\n", rec->ExceptionCode);
524 if(got_exception == 1) {
525 /* hw bp exception on first nop */
526 ok( context->Eip == (DWORD)code_mem, "eip is wrong: %x instead of %x\n",
527 context->Eip, (DWORD)code_mem);
528 ok( (context->Dr6 & 0xf) == 1, "B0 flag is not set in Dr6\n");
529 ok( !(context->Dr6 & 0x4000), "BS flag is set in Dr6\n");
530 context->Dr0 = context->Dr0 + 1; /* set hw bp again on next instruction */
531 context->EFlags |= 0x100; /* enable single stepping */
532 } else if( got_exception == 2) {
533 /* single step exception on second nop */
534 ok( context->Eip == (DWORD)code_mem + 1, "eip is wrong: %x instead of %x\n",
535 context->Eip, (DWORD)code_mem + 1);
536 ok( (context->Dr6 & 0x4000), "BS flag is not set in Dr6\n");
537 /* depending on the win version the B0 bit is already set here as well
538 ok( (context->Dr6 & 0xf) == 0, "B0...3 flags in Dr6 shouldn't be set\n"); */
539 context->EFlags |= 0x100;
540 } else if( got_exception == 3) {
541 /* hw bp exception on second nop */
542 ok( context->Eip == (DWORD)code_mem + 1, "eip is wrong: %x instead of %x\n",
543 context->Eip, (DWORD)code_mem + 1);
544 ok( (context->Dr6 & 0xf) == 1, "B0 flag is not set in Dr6\n");
545 ok( !(context->Dr6 & 0x4000), "BS flag is set in Dr6\n");
546 context->Dr0 = 0; /* clear breakpoint */
547 context->EFlags |= 0x100;
549 /* single step exception on ret */
550 ok( context->Eip == (DWORD)code_mem + 2, "eip is wrong: %x instead of %x\n",
551 context->Eip, (DWORD)code_mem + 2);
552 ok( (context->Dr6 & 0xf) == 0, "B0...3 flags in Dr6 shouldn't be set\n");
553 ok( (context->Dr6 & 0x4000), "BS flag is not set in Dr6\n");
556 context->Dr6 = 0; /* clear status register */
557 return ExceptionContinueExecution;
560 static const BYTE dummy_code[] = { 0x90, 0x90, 0xc3 }; /* nop, nop, ret */
562 /* test int3 handling */
563 static DWORD int3_handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
564 CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher )
566 ok( rec->ExceptionAddress == code_mem, "exception address not at: %p, but at %p\n",
567 code_mem, rec->ExceptionAddress);
568 ok( context->Eip == (DWORD)code_mem, "eip not at: %p, but at %#x\n", code_mem, context->Eip);
569 if(context->Eip == (DWORD)code_mem) context->Eip++; /* skip breakpoint */
571 return ExceptionContinueExecution;
574 static const BYTE int3_code[] = { 0xCC, 0xc3 }; /* int 3, ret */
577 static void test_exceptions(void)
582 if (!pNtGetContextThread || !pNtSetContextThread)
584 skip( "NtGetContextThread/NtSetContextThread not found\n" );
588 /* test handling of debug registers */
589 run_exception_test(dreg_handler, NULL, &segfault_code, sizeof(segfault_code));
591 ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
592 res = pNtGetContextThread(GetCurrentThread(), &ctx);
593 ok (res == STATUS_SUCCESS,"NtGetContextThread failed with %x\n", res);
594 ok(ctx.Dr0 == 0x42424242,"failed to set debugregister 0 to 0x42424242, got %x\n", ctx.Dr0);
595 ok((ctx.Dr7 & ~0xdc00) == 0x155,"failed to set debugregister 7 to 0x155, got %x\n", ctx.Dr7);
597 /* test single stepping behavior */
599 run_exception_test(single_step_handler, NULL, &single_stepcode, sizeof(single_stepcode));
600 ok(got_exception == 3, "expected 3 single step exceptions, got %d\n", got_exception);
602 /* test alignment exceptions */
604 run_exception_test(align_check_handler, NULL, align_check_code, sizeof(align_check_code));
605 ok(got_exception == 0, "got %d alignment faults, expected 0\n", got_exception);
607 /* test direction flag */
609 run_exception_test(direction_flag_handler, NULL, direction_flag_code, sizeof(direction_flag_code));
610 ok(got_exception == 1, "got %d exceptions, expected 1\n", got_exception);
612 /* test single stepping over hardware breakpoint */
613 memset(&ctx, 0, sizeof(ctx));
614 ctx.Dr0 = (DWORD) code_mem; /* set hw bp on first nop */
616 ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
617 res = pNtSetContextThread( GetCurrentThread(), &ctx);
618 ok( res == STATUS_SUCCESS, "NtSetContextThread faild with %x\n", res);
621 run_exception_test(bpx_handler, NULL, dummy_code, sizeof(dummy_code));
622 ok( got_exception == 4,"expected 4 exceptions, got %d\n", got_exception);
624 /* test int3 handling */
625 run_exception_test(int3_handler, NULL, int3_code, sizeof(int3_code));
628 static void test_debugger(void)
630 char cmdline[MAX_PATH];
631 PROCESS_INFORMATION pi;
632 STARTUPINFO si = { 0 };
634 DWORD continuestatus;
635 PVOID code_mem_address = NULL;
642 if(!pNtGetContextThread || !pNtSetContextThread || !pNtReadVirtualMemory || !pNtTerminateProcess)
644 skip("NtGetContextThread, NtSetContextThread, NtReadVirtualMemory or NtTerminateProcess not found\n)");
648 sprintf(cmdline, "%s %s %s %p", my_argv[0], my_argv[1], "debuggee", &test_stage);
649 ret = CreateProcess(NULL, cmdline, NULL, NULL, FALSE, DEBUG_PROCESS, NULL, NULL, &si, &pi);
650 ok(ret, "could not create child process error: %u\n", GetLastError());
656 continuestatus = DBG_CONTINUE;
657 ok(WaitForDebugEvent(&de, INFINITE), "reading debug event\n");
659 if (de.dwThreadId != pi.dwThreadId)
661 trace("event %d not coming from main thread, ignoring\n", de.dwDebugEventCode);
662 ContinueDebugEvent(de.dwProcessId, de.dwThreadId, DBG_CONTINUE);
666 if (de.dwDebugEventCode == CREATE_PROCESS_DEBUG_EVENT)
668 if(de.u.CreateProcessInfo.lpBaseOfImage != pNtCurrentTeb()->Peb->ImageBaseAddress)
670 skip("child process loaded at different address, terminating it\n");
671 pNtTerminateProcess(pi.hProcess, 0);
674 else if (de.dwDebugEventCode == EXCEPTION_DEBUG_EVENT)
680 status = pNtReadVirtualMemory(pi.hProcess, &code_mem, &code_mem_address,
681 sizeof(code_mem_address), &size_read);
682 ok(!status,"NtReadVirtualMemory failed with 0x%x\n", status);
683 status = pNtReadVirtualMemory(pi.hProcess, &test_stage, &stage,
684 sizeof(stage), &size_read);
685 ok(!status,"NtReadVirtualMemory failed with 0x%x\n", status);
687 ctx.ContextFlags = CONTEXT_FULL;
688 status = pNtGetContextThread(pi.hThread, &ctx);
689 ok(!status, "NtGetContextThread failed with 0x%x\n", status);
691 trace("exception 0x%x at %p firstchance=%d Eip=0x%x, Eax=0x%x\n",
692 de.u.Exception.ExceptionRecord.ExceptionCode,
693 de.u.Exception.ExceptionRecord.ExceptionAddress, de.u.Exception.dwFirstChance, ctx.Eip, ctx.Eax);
697 ok(FALSE, "got way too many exceptions, probaby caught in a infinite loop, terminating child\n");
698 pNtTerminateProcess(pi.hProcess, 1);
700 else if (counter >= 2) /* skip startup breakpoint */
704 ok((char *)ctx.Eip == (char *)code_mem_address + 0xb, "Eip at %x instead of %p\n",
705 ctx.Eip, (char *)code_mem_address + 0xb);
706 /* setting the context from debugger does not affect the context, the exception handlers gets */
707 /* uncomment once wine is fixed */
708 /* ctx.Eip = 0x12345; */
709 ctx.Eax = 0xf00f00f1;
711 /* let the debuggee handle the exception */
712 continuestatus = DBG_EXCEPTION_NOT_HANDLED;
716 if (de.u.Exception.dwFirstChance)
718 /* debugger gets first chance exception with unmodified ctx.Eip */
719 ok((char *)ctx.Eip == (char *)code_mem_address + 0xb, "Eip at 0x%x instead of %p\n",
720 ctx.Eip, (char *)code_mem_address + 0xb);
722 /* setting the context from debugger does not affect the context, the exception handlers gets */
723 /* uncomment once wine is fixed */
724 /* ctx.Eip = 0x12345; */
725 ctx.Eax = 0xf00f00f1;
727 /* pass exception to debuggee
728 * exception will not be handled and
729 * a second chance exception will be raised */
730 continuestatus = DBG_EXCEPTION_NOT_HANDLED;
734 /* debugger gets context after exception handler has played with it */
735 /* ctx.Eip is the same value the exception handler got */
736 if (de.u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_BREAKPOINT)
738 ok((char *)ctx.Eip == (char *)code_mem_address + 0xa, "Eip at 0x%x instead of %p\n",
739 ctx.Eip, (char *)code_mem_address + 0xa);
740 /* need to fixup Eip for debuggee */
741 if ((char *)ctx.Eip == (char *)code_mem_address + 0xa)
745 ok((char *)ctx.Eip == (char *)code_mem_address + 0xb, "Eip at 0x%x instead of %p\n",
746 ctx.Eip, (char *)code_mem_address + 0xb);
747 /* here we handle exception */
751 ok(FALSE, "unexpected stage %x\n", stage);
753 status = pNtSetContextThread(pi.hThread, &ctx);
754 ok(!status, "NtSetContextThread failed with 0x%x\n", status);
758 ContinueDebugEvent(de.dwProcessId, de.dwThreadId, continuestatus);
760 } while (de.dwDebugEventCode != EXIT_PROCESS_DEBUG_EVENT);
762 winetest_wait_child_process( pi.hProcess );
763 ok(CloseHandle(pi.hThread) != 0, "error %u\n", GetLastError());
764 ok(CloseHandle(pi.hProcess) != 0, "error %u\n", GetLastError());
769 static DWORD simd_fault_handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
770 CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher )
772 int *stage = *(int **)(frame + 1);
777 /* fault while executing sse instruction */
778 context->Eip += 3; /* skip addps */
779 return ExceptionContinueExecution;
782 /* stage 2 - divide by zero fault */
783 if( rec->ExceptionCode == EXCEPTION_ILLEGAL_INSTRUCTION)
784 skip("system doesn't support SIMD exceptions\n");
786 ok( rec->ExceptionCode == STATUS_FLOAT_MULTIPLE_TRAPS,
787 "exception code: %#x, should be %#x\n",
788 rec->ExceptionCode, STATUS_FLOAT_MULTIPLE_TRAPS);
789 ok( rec->NumberParameters == 1, "# of params: %i, should be 1\n",
790 rec->NumberParameters);
791 if( rec->NumberParameters == 1 )
792 ok( rec->ExceptionInformation[0] == 0, "param #1: %lx, should be 0\n", rec->ExceptionInformation[0]);
795 context->Eip += 3; /* skip divps */
797 return ExceptionContinueExecution;
800 static const BYTE simd_exception_test[] = {
801 0x83, 0xec, 0x4, /* sub $0x4, %esp */
802 0x0f, 0xae, 0x1c, 0x24, /* stmxcsr (%esp) */
803 0x66, 0x81, 0x24, 0x24, 0xff, 0xfd, /* andw $0xfdff,(%esp) * enable divide by */
804 0x0f, 0xae, 0x14, 0x24, /* ldmxcsr (%esp) * zero exceptions */
805 0x6a, 0x01, /* push $0x1 */
806 0x6a, 0x01, /* push $0x1 */
807 0x6a, 0x01, /* push $0x1 */
808 0x6a, 0x01, /* push $0x1 */
809 0x0f, 0x10, 0x0c, 0x24, /* movups (%esp),%xmm1 * fill dividend */
810 0x0f, 0x57, 0xc0, /* xorps %xmm0,%xmm0 * clear divisor */
811 0x0f, 0x5e, 0xc8, /* divps %xmm0,%xmm1 * generate fault */
812 0x83, 0xc4, 0x10, /* add $0x10,%esp */
813 0x66, 0x81, 0x0c, 0x24, 0x00, 0x02, /* orw $0x200,(%esp) * disable exceptions */
814 0x0f, 0xae, 0x14, 0x24, /* ldmxcsr (%esp) */
815 0x83, 0xc4, 0x04, /* add $0x4,%esp */
819 static const BYTE sse_check[] = {
820 0x0f, 0x58, 0xc8, /* addps %xmm0,%xmm1 */
824 static void test_simd_exceptions(void)
828 /* test if CPU & OS can do sse */
831 run_exception_test(simd_fault_handler, &stage, sse_check, sizeof(sse_check));
833 skip("system doesn't support SSE\n");
837 /* generate a SIMD exception */
840 run_exception_test(simd_fault_handler, &stage, simd_exception_test,
841 sizeof(simd_exception_test));
842 ok( got_exception == 1, "got exception: %i, should be 1\n", got_exception);
845 struct fpu_exception_info
847 DWORD exception_code;
848 DWORD exception_offset;
852 static DWORD fpu_exception_handler(EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
853 CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher)
855 struct fpu_exception_info *info = *(struct fpu_exception_info **)(frame + 1);
857 info->exception_code = rec->ExceptionCode;
858 info->exception_offset = (BYTE *)rec->ExceptionAddress - (BYTE *)code_mem;
859 info->eip_offset = context->Eip - (DWORD)code_mem;
862 return ExceptionContinueExecution;
865 static void test_fpu_exceptions(void)
867 static const BYTE fpu_exception_test_ie[] =
869 0x83, 0xec, 0x04, /* sub $0x4,%esp */
870 0x66, 0xc7, 0x04, 0x24, 0xfe, 0x03, /* movw $0x3fe,(%esp) */
871 0x9b, 0xd9, 0x7c, 0x24, 0x02, /* fstcw 0x2(%esp) */
872 0xd9, 0x2c, 0x24, /* fldcw (%esp) */
873 0xd9, 0xee, /* fldz */
874 0xd9, 0xe8, /* fld1 */
875 0xde, 0xf1, /* fdivp */
876 0xdd, 0xd8, /* fstp %st(0) */
877 0xdd, 0xd8, /* fstp %st(0) */
879 0xdb, 0xe2, /* fnclex */
880 0xd9, 0x6c, 0x24, 0x02, /* fldcw 0x2(%esp) */
881 0x83, 0xc4, 0x04, /* add $0x4,%esp */
885 static const BYTE fpu_exception_test_de[] =
887 0x83, 0xec, 0x04, /* sub $0x4,%esp */
888 0x66, 0xc7, 0x04, 0x24, 0xfb, 0x03, /* movw $0x3fb,(%esp) */
889 0x9b, 0xd9, 0x7c, 0x24, 0x02, /* fstcw 0x2(%esp) */
890 0xd9, 0x2c, 0x24, /* fldcw (%esp) */
891 0xdd, 0xd8, /* fstp %st(0) */
892 0xd9, 0xee, /* fldz */
893 0xd9, 0xe8, /* fld1 */
894 0xde, 0xf1, /* fdivp */
896 0xdb, 0xe2, /* fnclex */
897 0xdd, 0xd8, /* fstp %st(0) */
898 0xdd, 0xd8, /* fstp %st(0) */
899 0xd9, 0x6c, 0x24, 0x02, /* fldcw 0x2(%esp) */
900 0x83, 0xc4, 0x04, /* add $0x4,%esp */
904 struct fpu_exception_info info;
906 memset(&info, 0, sizeof(info));
907 run_exception_test(fpu_exception_handler, &info, fpu_exception_test_ie, sizeof(fpu_exception_test_ie));
908 ok(info.exception_code == EXCEPTION_FLT_STACK_CHECK,
909 "Got exception code %#x, expected EXCEPTION_FLT_STACK_CHECK\n", info.exception_code);
910 ok(info.exception_offset == 0x19, "Got exception offset %#x, expected 0x19\n", info.exception_offset);
911 ok(info.eip_offset == 0x1b, "Got EIP offset %#x, expected 0x1b\n", info.eip_offset);
913 memset(&info, 0, sizeof(info));
914 run_exception_test(fpu_exception_handler, &info, fpu_exception_test_de, sizeof(fpu_exception_test_de));
915 ok(info.exception_code == EXCEPTION_FLT_DIVIDE_BY_ZERO,
916 "Got exception code %#x, expected EXCEPTION_FLT_DIVIDE_BY_ZERO\n", info.exception_code);
917 ok(info.exception_offset == 0x17, "Got exception offset %#x, expected 0x17\n", info.exception_offset);
918 ok(info.eip_offset == 0x19, "Got EIP offset %#x, expected 0x19\n", info.eip_offset);
921 #elif defined(__x86_64__)
923 #define UNW_FLAG_NHANDLER 0
924 #define UNW_FLAG_EHANDLER 1
925 #define UNW_FLAG_UHANDLER 2
926 #define UNW_FLAG_CHAININFO 4
928 #define UWOP_PUSH_NONVOL 0
929 #define UWOP_ALLOC_LARGE 1
930 #define UWOP_ALLOC_SMALL 2
931 #define UWOP_SET_FPREG 3
932 #define UWOP_SAVE_NONVOL 4
933 #define UWOP_SAVE_NONVOL_FAR 5
934 #define UWOP_SAVE_XMM128 8
935 #define UWOP_SAVE_XMM128_FAR 9
936 #define UWOP_PUSH_MACHFRAME 10
940 int rip_offset; /* rip offset from code start */
941 int rbp_offset; /* rbp offset from stack pointer */
942 int handler; /* expect handler to be set? */
943 int rip; /* expected final rip value */
944 int regs[8][2]; /* expected values for registers */
949 const BYTE *function;
950 size_t function_size;
951 const BYTE *unwind_info;
952 const struct results *results;
953 unsigned int nb_results;
958 rax, rcx, rdx, rbx, rsp, rbp, rsi, rdi,
959 r8, r9, r10, r11, r12, r13, r14, r15
962 static const char * const reg_names[16] =
964 "rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi",
965 "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15"
968 #define UWOP(code,info) (UWOP_##code | ((info) << 4))
970 static void call_virtual_unwind( int testnum, const struct unwind_test *test )
972 static const int code_offset = 1024;
973 static const int unwind_offset = 2048;
974 void *handler, *data;
976 RUNTIME_FUNCTION runtime_func;
977 KNONVOLATILE_CONTEXT_POINTERS ctx_ptr;
979 ULONG64 fake_stack[256];
980 ULONG64 frame, orig_rip, orig_rbp, unset_reg;
981 UINT unwind_size = 4 + 2 * test->unwind_info[2] + 8;
983 memcpy( (char *)code_mem + code_offset, test->function, test->function_size );
984 memcpy( (char *)code_mem + unwind_offset, test->unwind_info, unwind_size );
986 runtime_func.BeginAddress = code_offset;
987 runtime_func.EndAddress = code_offset + test->function_size;
988 runtime_func.UnwindData = unwind_offset;
990 trace( "code: %p stack: %p\n", code_mem, fake_stack );
992 for (i = 0; i < test->nb_results; i++)
994 memset( &ctx_ptr, 0, sizeof(ctx_ptr) );
995 memset( &context, 0x55, sizeof(context) );
996 memset( &unset_reg, 0x55, sizeof(unset_reg) );
997 for (j = 0; j < 256; j++) fake_stack[j] = j * 8;
999 context.Rsp = (ULONG_PTR)fake_stack;
1000 context.Rbp = (ULONG_PTR)fake_stack + test->results[i].rbp_offset;
1001 orig_rbp = context.Rbp;
1002 orig_rip = (ULONG64)code_mem + code_offset + test->results[i].rip_offset;
1004 trace( "%u/%u: rip=%p (%02x) rbp=%p rsp=%p\n", testnum, i,
1005 (void *)orig_rip, *(BYTE *)orig_rip, (void *)orig_rbp, (void *)context.Rsp );
1007 data = (void *)0xdeadbeef;
1008 handler = RtlVirtualUnwind( UNW_FLAG_EHANDLER, (ULONG64)code_mem, orig_rip,
1009 &runtime_func, &context, &data, &frame, &ctx_ptr );
1010 if (test->results[i].handler)
1012 ok( (char *)handler == (char *)code_mem + 0x200,
1013 "%u/%u: wrong handler %p/%p\n", testnum, i, handler, (char *)code_mem + 0x200 );
1014 if (handler) ok( *(DWORD *)data == 0x08070605,
1015 "%u/%u: wrong handler data %p\n", testnum, i, data );
1019 ok( handler == NULL, "%u/%u: handler %p instead of NULL\n", testnum, i, handler );
1020 ok( data == (void *)0xdeadbeef, "%u/%u: handler data set to %p\n", testnum, i, data );
1023 ok( context.Rip == test->results[i].rip, "%u/%u: wrong rip %p/%x\n",
1024 testnum, i, (void *)context.Rip, test->results[i].rip );
1026 for (j = 0; j < 16; j++)
1028 static const UINT nb_regs = sizeof(test->results[i].regs) / sizeof(test->results[i].regs[0]);
1030 for (k = 0; k < nb_regs; k++)
1032 if (test->results[i].regs[k][0] == -1)
1037 if (test->results[i].regs[k][0] == j) break;
1040 if (j == rsp) /* rsp is special */
1042 ok( !ctx_ptr.u2.IntegerContext[j],
1043 "%u/%u: rsp should not be set in ctx_ptr\n", testnum, i );
1044 ok( context.Rsp == (ULONG64)fake_stack + test->results[i].regs[k][1],
1045 "%u/%u: register rsp wrong %p/%p\n",
1046 testnum, i, (void *)context.Rsp, (char *)fake_stack + test->results[i].regs[k][1] );
1050 if (ctx_ptr.u2.IntegerContext[j])
1052 ok( k < nb_regs, "%u/%u: register %s should not be set to %lx\n",
1053 testnum, i, reg_names[j], *(&context.Rax + j) );
1055 ok( *(&context.Rax + j) == test->results[i].regs[k][1],
1056 "%u/%u: register %s wrong %p/%x\n",
1057 testnum, i, reg_names[j], (void *)*(&context.Rax + j), test->results[i].regs[k][1] );
1061 ok( k == nb_regs, "%u/%u: register %s should be set\n", testnum, i, reg_names[j] );
1063 ok( context.Rbp == orig_rbp, "%u/%u: register rbp wrong %p/unset\n",
1064 testnum, i, (void *)context.Rbp );
1066 ok( *(&context.Rax + j) == unset_reg,
1067 "%u/%u: register %s wrong %p/unset\n",
1068 testnum, i, reg_names[j], (void *)*(&context.Rax + j));
1074 static void test_virtual_unwind(void)
1076 static const BYTE function_0[] =
1078 0xff, 0xf5, /* 00: push %rbp */
1079 0x48, 0x81, 0xec, 0x10, 0x01, 0x00, 0x00, /* 02: sub $0x110,%rsp */
1080 0x48, 0x8d, 0x6c, 0x24, 0x30, /* 09: lea 0x30(%rsp),%rbp */
1081 0x48, 0x89, 0x9d, 0xf0, 0x00, 0x00, 0x00, /* 0e: mov %rbx,0xf0(%rbp) */
1082 0x48, 0x89, 0xb5, 0xf8, 0x00, 0x00, 0x00, /* 15: mov %rsi,0xf8(%rbp) */
1084 0x48, 0x8b, 0x9d, 0xf0, 0x00, 0x00, 0x00, /* 1d: mov 0xf0(%rbp),%rbx */
1085 0x48, 0x8b, 0xb5, 0xf8, 0x00, 0x00, 0x00, /* 24: mov 0xf8(%rbp),%rsi */
1086 0x48, 0x8d, 0xa5, 0xe0, 0x00, 0x00, 0x00, /* 2b: lea 0xe0(%rbp),%rsp */
1087 0x5d, /* 32: pop %rbp */
1091 static const BYTE unwind_info_0[] =
1093 1 | (UNW_FLAG_EHANDLER << 3), /* version + flags */
1094 0x1c, /* prolog size */
1095 8, /* opcode count */
1096 (0x03 << 4) | rbp, /* frame reg rbp offset 0x30 */
1098 0x1c, UWOP(SAVE_NONVOL, rsi), 0x25, 0, /* 1c: mov %rsi,0x128(%rsp) */
1099 0x15, UWOP(SAVE_NONVOL, rbx), 0x24, 0, /* 15: mov %rbx,0x120(%rsp) */
1100 0x0e, UWOP(SET_FPREG, rbp), /* 0e: lea 0x30(%rsp),rbp */
1101 0x09, UWOP(ALLOC_LARGE, 0), 0x22, 0, /* 09: sub $0x110,%rsp */
1102 0x02, UWOP(PUSH_NONVOL, rbp), /* 02: push %rbp */
1104 0x00, 0x02, 0x00, 0x00, /* handler */
1105 0x05, 0x06, 0x07, 0x08, /* data */
1108 static const struct results results_0[] =
1110 /* offset rbp handler rip registers */
1111 { 0x00, 0x40, FALSE, 0x000, { {rsp,0x008}, {-1,-1} }},
1112 { 0x02, 0x40, FALSE, 0x008, { {rsp,0x010}, {rbp,0x000}, {-1,-1} }},
1113 { 0x09, 0x40, FALSE, 0x118, { {rsp,0x120}, {rbp,0x110}, {-1,-1} }},
1114 { 0x0e, 0x40, FALSE, 0x128, { {rsp,0x130}, {rbp,0x120}, {-1,-1} }},
1115 { 0x15, 0x40, FALSE, 0x128, { {rsp,0x130}, {rbp,0x120}, {rbx,0x130}, {-1,-1} }},
1116 { 0x1c, 0x40, TRUE, 0x128, { {rsp,0x130}, {rbp,0x120}, {rbx,0x130}, {rsi,0x138}, {-1,-1}}},
1117 { 0x1d, 0x40, TRUE, 0x128, { {rsp,0x130}, {rbp,0x120}, {rbx,0x130}, {rsi,0x138}, {-1,-1}}},
1118 { 0x24, 0x40, TRUE, 0x128, { {rsp,0x130}, {rbp,0x120}, {rbx,0x130}, {rsi,0x138}, {-1,-1}}},
1119 { 0x2b, 0x40, FALSE, 0x128, { {rsp,0x130}, {rbp,0x120}, {-1,-1}}},
1120 { 0x32, 0x40, FALSE, 0x008, { {rsp,0x010}, {rbp,0x000}, {-1,-1}}},
1121 { 0x33, 0x40, FALSE, 0x000, { {rsp,0x008}, {-1,-1}}},
1125 static const BYTE function_1[] =
1127 0x53, /* 00: push %rbx */
1128 0x55, /* 01: push %rbp */
1129 0x56, /* 02: push %rsi */
1130 0x57, /* 03: push %rdi */
1131 0x41, 0x54, /* 04: push %r12 */
1132 0x48, 0x83, 0xec, 0x30, /* 06: sub $0x30,%rsp */
1133 0x90, 0x90, /* 0a: nop; nop */
1134 0x48, 0x83, 0xc4, 0x30, /* 0c: add $0x30,%rsp */
1135 0x41, 0x5c, /* 10: pop %r12 */
1136 0x5f, /* 12: pop %rdi */
1137 0x5e, /* 13: pop %rsi */
1138 0x5d, /* 14: pop %rbp */
1139 0x5b, /* 15: pop %rbx */
1143 static const BYTE unwind_info_1[] =
1145 1 | (UNW_FLAG_EHANDLER << 3), /* version + flags */
1146 0x0a, /* prolog size */
1147 6, /* opcode count */
1150 0x0a, UWOP(ALLOC_SMALL, 5), /* 0a: sub $0x30,%rsp */
1151 0x06, UWOP(PUSH_NONVOL, r12), /* 06: push %r12 */
1152 0x04, UWOP(PUSH_NONVOL, rdi), /* 04: push %rdi */
1153 0x03, UWOP(PUSH_NONVOL, rsi), /* 03: push %rsi */
1154 0x02, UWOP(PUSH_NONVOL, rbp), /* 02: push %rbp */
1155 0x01, UWOP(PUSH_NONVOL, rbx), /* 01: push %rbx */
1157 0x00, 0x02, 0x00, 0x00, /* handler */
1158 0x05, 0x06, 0x07, 0x08, /* data */
1161 static const struct results results_1[] =
1163 /* offset rbp handler rip registers */
1164 { 0x00, 0x50, FALSE, 0x000, { {rsp,0x008}, {-1,-1} }},
1165 { 0x01, 0x50, FALSE, 0x008, { {rsp,0x010}, {rbx,0x000}, {-1,-1} }},
1166 { 0x02, 0x50, FALSE, 0x010, { {rsp,0x018}, {rbx,0x008}, {rbp,0x000}, {-1,-1} }},
1167 { 0x03, 0x50, FALSE, 0x018, { {rsp,0x020}, {rbx,0x010}, {rbp,0x008}, {rsi,0x000}, {-1,-1} }},
1168 { 0x04, 0x50, FALSE, 0x020, { {rsp,0x028}, {rbx,0x018}, {rbp,0x010}, {rsi,0x008}, {rdi,0x000}, {-1,-1} }},
1169 { 0x06, 0x50, FALSE, 0x028, { {rsp,0x030}, {rbx,0x020}, {rbp,0x018}, {rsi,0x010}, {rdi,0x008}, {r12,0x000}, {-1,-1} }},
1170 { 0x0a, 0x50, TRUE, 0x058, { {rsp,0x060}, {rbx,0x050}, {rbp,0x048}, {rsi,0x040}, {rdi,0x038}, {r12,0x030}, {-1,-1} }},
1171 { 0x0c, 0x50, FALSE, 0x058, { {rsp,0x060}, {rbx,0x050}, {rbp,0x048}, {rsi,0x040}, {rdi,0x038}, {r12,0x030}, {-1,-1} }},
1172 { 0x10, 0x50, FALSE, 0x028, { {rsp,0x030}, {rbx,0x020}, {rbp,0x018}, {rsi,0x010}, {rdi,0x008}, {r12,0x000}, {-1,-1} }},
1173 { 0x12, 0x50, FALSE, 0x020, { {rsp,0x028}, {rbx,0x018}, {rbp,0x010}, {rsi,0x008}, {rdi,0x000}, {-1,-1} }},
1174 { 0x13, 0x50, FALSE, 0x018, { {rsp,0x020}, {rbx,0x010}, {rbp,0x008}, {rsi,0x000}, {-1,-1} }},
1175 { 0x14, 0x50, FALSE, 0x010, { {rsp,0x018}, {rbx,0x008}, {rbp,0x000}, {-1,-1} }},
1176 { 0x15, 0x50, FALSE, 0x008, { {rsp,0x010}, {rbx,0x000}, {-1,-1} }},
1177 { 0x16, 0x50, FALSE, 0x000, { {rsp,0x008}, {-1,-1} }},
1180 static const struct unwind_test tests[] =
1182 { function_0, sizeof(function_0), unwind_info_0,
1183 results_0, sizeof(results_0)/sizeof(results_0[0]) },
1184 { function_1, sizeof(function_1), unwind_info_1,
1185 results_1, sizeof(results_1)/sizeof(results_1[0]) }
1189 for (i = 0; i < sizeof(tests)/sizeof(tests[0]); i++)
1190 call_virtual_unwind( i, &tests[i] );
1193 #endif /* __x86_64__ */
1195 START_TEST(exception)
1197 HMODULE hntdll = GetModuleHandleA("ntdll.dll");
1199 code_mem = VirtualAlloc(NULL, 65536, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
1201 trace("VirtualAlloc failed\n");
1205 pNtCurrentTeb = (void *)GetProcAddress( hntdll, "NtCurrentTeb" );
1206 pNtGetContextThread = (void *)GetProcAddress( hntdll, "NtGetContextThread" );
1207 pNtSetContextThread = (void *)GetProcAddress( hntdll, "NtSetContextThread" );
1208 pNtReadVirtualMemory = (void *)GetProcAddress( hntdll, "NtReadVirtualMemory" );
1209 pRtlRaiseException = (void *)GetProcAddress( hntdll, "RtlRaiseException" );
1210 pNtTerminateProcess = (void *)GetProcAddress( hntdll, "NtTerminateProcess" );
1211 pRtlAddVectoredExceptionHandler = (void *)GetProcAddress( hntdll,
1212 "RtlAddVectoredExceptionHandler" );
1213 pRtlRemoveVectoredExceptionHandler = (void *)GetProcAddress( hntdll,
1214 "RtlRemoveVectoredExceptionHandler" );
1219 skip( "NtCurrentTeb not found\n" );
1223 if (pRtlAddVectoredExceptionHandler && pRtlRemoveVectoredExceptionHandler)
1224 have_vectored_api = TRUE;
1226 skip("RtlAddVectoredExceptionHandler or RtlRemoveVectoredExceptionHandler not found\n");
1228 my_argc = winetest_get_mainargs( &my_argv );
1232 sscanf( my_argv[3], "%p", &addr );
1234 if (addr != &test_stage)
1236 skip( "child process not mapped at same address (%p/%p)\n", &test_stage, addr);
1240 /* child must be run under a debugger */
1241 if (!pNtCurrentTeb()->Peb->BeingDebugged)
1243 ok(FALSE, "child process not being debugged?\n");
1247 if (pRtlRaiseException)
1250 run_rtlraiseexception_test(0x12345);
1251 run_rtlraiseexception_test(EXCEPTION_BREAKPOINT);
1252 run_rtlraiseexception_test(EXCEPTION_INVALID_HANDLE);
1254 run_rtlraiseexception_test(0x12345);
1255 run_rtlraiseexception_test(EXCEPTION_BREAKPOINT);
1256 run_rtlraiseexception_test(EXCEPTION_INVALID_HANDLE);
1259 skip( "RtlRaiseException not found\n" );
1261 /* rest of tests only run in parent */
1267 test_rtlraiseexception();
1269 test_simd_exceptions();
1270 test_fpu_exceptions();
1272 #elif defined(__x86_64__)
1274 test_virtual_unwind();
1278 VirtualFree(code_mem, 0, MEM_FREE);