ReactOS 0.4.15-dev-8241-g63935f8
exception.c
Go to the documentation of this file.
1/*
2 * Unit test suite for ntdll exceptions
3 *
4 * Copyright 2005 Alexandre Julliard
5 *
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.
10 *
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.
15 *
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
19 */
20
21#include <stdarg.h>
22#include <stdio.h>
23
24#include "ntstatus.h"
25#define WIN32_NO_STATUS
26#define NONAMELESSUNION
27#include "windef.h"
28#include "winbase.h"
29#include "winnt.h"
30#include "winreg.h"
31#include "winternl.h"
32#ifdef __REACTOS__
33#include <wine/exception.h>
34#else
35#include "excpt.h"
36#endif
37#include "wine/test.h"
38
39static void *code_mem;
40
41static NTSTATUS (WINAPI *pNtGetContextThread)(HANDLE,CONTEXT*);
42static NTSTATUS (WINAPI *pNtSetContextThread)(HANDLE,CONTEXT*);
43static NTSTATUS (WINAPI *pRtlRaiseException)(EXCEPTION_RECORD *rec);
44static PVOID (WINAPI *pRtlUnwind)(PVOID, PVOID, PEXCEPTION_RECORD, PVOID);
45static VOID (WINAPI *pRtlCaptureContext)(CONTEXT*);
46static PVOID (WINAPI *pRtlAddVectoredExceptionHandler)(ULONG first, PVECTORED_EXCEPTION_HANDLER func);
47static ULONG (WINAPI *pRtlRemoveVectoredExceptionHandler)(PVOID handler);
48static PVOID (WINAPI *pRtlAddVectoredContinueHandler)(ULONG first, PVECTORED_EXCEPTION_HANDLER func);
49static ULONG (WINAPI *pRtlRemoveVectoredContinueHandler)(PVOID handler);
50static NTSTATUS (WINAPI *pNtReadVirtualMemory)(HANDLE, const void*, void*, SIZE_T, SIZE_T*);
51static NTSTATUS (WINAPI *pNtTerminateProcess)(HANDLE handle, LONG exit_code);
52static NTSTATUS (WINAPI *pNtQueryInformationProcess)(HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG);
53static NTSTATUS (WINAPI *pNtSetInformationProcess)(HANDLE, PROCESSINFOCLASS, PVOID, ULONG);
54static BOOL (WINAPI *pIsWow64Process)(HANDLE, PBOOL);
55static NTSTATUS (WINAPI *pNtClose)(HANDLE);
56
57#if defined(__x86_64__)
58#ifndef __REACTOS__
59typedef struct
60{
62 struct
63 {
64 ULONG BeginAddress;
65 ULONG EndAddress;
66 ULONG HandlerAddress;
67 ULONG JumpTarget;
68 } ScopeRecord[1];
69} SCOPE_TABLE;
70
71typedef struct
72{
73 ULONG64 ControlPc;
74 ULONG64 ImageBase;
75 PRUNTIME_FUNCTION FunctionEntry;
77 ULONG64 TargetIp;
79 void* /*PEXCEPTION_ROUTINE*/ LanguageHandler;
80 PVOID HandlerData;
81 PUNWIND_HISTORY_TABLE HistoryTable;
82 ULONG ScopeIndex;
84
85typedef struct _SETJMP_FLOAT128
86{
87 unsigned __int64 DECLSPEC_ALIGN(16) Part[2];
88} SETJMP_FLOAT128;
89
90typedef struct _JUMP_BUFFER
91{
92 unsigned __int64 Frame;
93 unsigned __int64 Rbx;
94 unsigned __int64 Rsp;
95 unsigned __int64 Rbp;
96 unsigned __int64 Rsi;
97 unsigned __int64 Rdi;
98 unsigned __int64 R12;
99 unsigned __int64 R13;
100 unsigned __int64 R14;
101 unsigned __int64 R15;
102 unsigned __int64 Rip;
103 unsigned __int64 Spare;
104 SETJMP_FLOAT128 Xmm6;
105 SETJMP_FLOAT128 Xmm7;
106 SETJMP_FLOAT128 Xmm8;
107 SETJMP_FLOAT128 Xmm9;
108 SETJMP_FLOAT128 Xmm10;
109 SETJMP_FLOAT128 Xmm11;
110 SETJMP_FLOAT128 Xmm12;
111 SETJMP_FLOAT128 Xmm13;
112 SETJMP_FLOAT128 Xmm14;
113 SETJMP_FLOAT128 Xmm15;
114} _JUMP_BUFFER;
115#endif // __REACTOS__
116
117static BOOLEAN (CDECL *pRtlAddFunctionTable)(RUNTIME_FUNCTION*, DWORD, DWORD64);
118static BOOLEAN (CDECL *pRtlDeleteFunctionTable)(RUNTIME_FUNCTION*);
119static BOOLEAN (CDECL *pRtlInstallFunctionTableCallback)(DWORD64, DWORD64, DWORD, PGET_RUNTIME_FUNCTION_CALLBACK, PVOID, PCWSTR);
120static PRUNTIME_FUNCTION (WINAPI *pRtlLookupFunctionEntry)(ULONG64, ULONG64*, UNWIND_HISTORY_TABLE*);
122static VOID (WINAPI *pRtlCaptureContext)(CONTEXT*);
123static VOID (CDECL *pRtlRestoreContext)(CONTEXT*, EXCEPTION_RECORD*);
124static VOID (CDECL *pRtlUnwindEx)(VOID*, VOID*, EXCEPTION_RECORD*, VOID*, CONTEXT*, UNWIND_HISTORY_TABLE*);
125static int (CDECL *p_setjmp)(_JUMP_BUFFER*);
126#endif
127
128#ifdef __i386__
129
130#ifndef __WINE_WINTRNL_H
131#define ProcessExecuteFlags 0x22
132#define MEM_EXECUTE_OPTION_DISABLE 0x01
133#define MEM_EXECUTE_OPTION_ENABLE 0x02
134#define MEM_EXECUTE_OPTION_PERMANENT 0x08
135#endif
136
137static int my_argc;
138static char** my_argv;
139static int test_stage;
140
141static BOOL is_wow64;
142
143/* Test various instruction combinations that cause a protection fault on the i386,
144 * and check what the resulting exception looks like.
145 */
146
147static const struct exception
148{
149 BYTE code[18]; /* asm code */
150 BYTE offset; /* offset of faulting instruction */
151 BYTE length; /* length of faulting instruction */
152 BOOL wow64_broken; /* broken on Wow64, should be skipped */
153 NTSTATUS status; /* expected status code */
154 DWORD nb_params; /* expected number of parameters */
155 DWORD params[4]; /* expected parameters */
156 NTSTATUS alt_status; /* alternative status code */
157 DWORD alt_nb_params; /* alternative number of parameters */
158 DWORD alt_params[4]; /* alternative parameters */
159} exceptions[] =
160{
161/* 0 */
162 /* test some privileged instructions */
163 { { 0xfb, 0xc3 }, /* 0: sti; ret */
165 { { 0x6c, 0xc3 }, /* 1: insb (%dx); ret */
167 { { 0x6d, 0xc3 }, /* 2: insl (%dx); ret */
169 { { 0x6e, 0xc3 }, /* 3: outsb (%dx); ret */
171 { { 0x6f, 0xc3 }, /* 4: outsl (%dx); ret */
173/* 5 */
174 { { 0xe4, 0x11, 0xc3 }, /* 5: inb $0x11,%al; ret */
176 { { 0xe5, 0x11, 0xc3 }, /* 6: inl $0x11,%eax; ret */
178 { { 0xe6, 0x11, 0xc3 }, /* 7: outb %al,$0x11; ret */
180 { { 0xe7, 0x11, 0xc3 }, /* 8: outl %eax,$0x11; ret */
182 { { 0xed, 0xc3 }, /* 9: inl (%dx),%eax; ret */
184/* 10 */
185 { { 0xee, 0xc3 }, /* 10: outb %al,(%dx); ret */
187 { { 0xef, 0xc3 }, /* 11: outl %eax,(%dx); ret */
189 { { 0xf4, 0xc3 }, /* 12: hlt; ret */
191 { { 0xfa, 0xc3 }, /* 13: cli; ret */
193
194 /* test long jump to invalid selector */
195 { { 0xea, 0, 0, 0, 0, 0, 0, 0xc3 }, /* 14: ljmp $0,$0; ret */
196 0, 7, FALSE, STATUS_ACCESS_VIOLATION, 2, { 0, 0xffffffff } },
197
198/* 15 */
199 /* test iret to invalid selector */
200 { { 0x6a, 0x00, 0x6a, 0x00, 0x6a, 0x00, 0xcf, 0x83, 0xc4, 0x0c, 0xc3 },
201 /* 15: pushl $0; pushl $0; pushl $0; iret; addl $12,%esp; ret */
202 6, 1, FALSE, STATUS_ACCESS_VIOLATION, 2, { 0, 0xffffffff } },
203
204 /* test loading an invalid selector */
205 { { 0xb8, 0xef, 0xbe, 0x00, 0x00, 0x8e, 0xe8, 0xc3 }, /* 16: mov $beef,%ax; mov %ax,%gs; ret */
206 5, 2, FALSE, STATUS_ACCESS_VIOLATION, 2, { 0, 0xbee8 } }, /* 0xbee8 or 0xffffffff */
207
208 /* test accessing a zero selector (%es broken on Wow64) */
209 { { 0x06, 0x31, 0xc0, 0x8e, 0xc0, 0x26, 0xa1, 0, 0, 0, 0, 0x07, 0xc3 },
210 /* push %es; xor %eax,%eax; mov %ax,%es; mov %es:(0),%ax; pop %es; ret */
211 5, 6, TRUE, STATUS_ACCESS_VIOLATION, 2, { 0, 0xffffffff } },
212 { { 0x0f, 0xa8, 0x31, 0xc0, 0x8e, 0xe8, 0x65, 0xa1, 0, 0, 0, 0, 0x0f, 0xa9, 0xc3 },
213 /* push %gs; xor %eax,%eax; mov %ax,%gs; mov %gs:(0),%ax; pop %gs; ret */
214 6, 6, FALSE, STATUS_ACCESS_VIOLATION, 2, { 0, 0xffffffff } },
215
216 /* test moving %cs -> %ss */
217 { { 0x0e, 0x17, 0x58, 0xc3 }, /* pushl %cs; popl %ss; popl %eax; ret */
218 1, 1, FALSE, STATUS_ACCESS_VIOLATION, 2, { 0, 0xffffffff } },
219
220/* 20 */
221 /* test overlong instruction (limit is 15 bytes, 5 on Win7) */
222 { { 0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0xfa,0xc3 },
223 0, 16, TRUE, STATUS_ILLEGAL_INSTRUCTION, 0, { 0 },
224 STATUS_ACCESS_VIOLATION, 2, { 0, 0xffffffff } },
225 { { 0x64,0x64,0x64,0x64,0xfa,0xc3 },
227
228 /* test invalid interrupt */
229 { { 0xcd, 0xff, 0xc3 }, /* int $0xff; ret */
230 0, 2, FALSE, STATUS_ACCESS_VIOLATION, 2, { 0, 0xffffffff } },
231
232 /* test moves to/from Crx */
233 { { 0x0f, 0x20, 0xc0, 0xc3 }, /* movl %cr0,%eax; ret */
235 { { 0x0f, 0x20, 0xe0, 0xc3 }, /* movl %cr4,%eax; ret */
237/* 25 */
238 { { 0x0f, 0x22, 0xc0, 0xc3 }, /* movl %eax,%cr0; ret */
240 { { 0x0f, 0x22, 0xe0, 0xc3 }, /* movl %eax,%cr4; ret */
242
243 /* test moves to/from Drx */
244 { { 0x0f, 0x21, 0xc0, 0xc3 }, /* movl %dr0,%eax; ret */
246 { { 0x0f, 0x21, 0xc8, 0xc3 }, /* movl %dr1,%eax; ret */
248 { { 0x0f, 0x21, 0xf8, 0xc3 }, /* movl %dr7,%eax; ret */
250/* 30 */
251 { { 0x0f, 0x23, 0xc0, 0xc3 }, /* movl %eax,%dr0; ret */
253 { { 0x0f, 0x23, 0xc8, 0xc3 }, /* movl %eax,%dr1; ret */
255 { { 0x0f, 0x23, 0xf8, 0xc3 }, /* movl %eax,%dr7; ret */
257
258 /* test memory reads */
259 { { 0xa1, 0xfc, 0xff, 0xff, 0xff, 0xc3 }, /* movl 0xfffffffc,%eax; ret */
260 0, 5, FALSE, STATUS_ACCESS_VIOLATION, 2, { 0, 0xfffffffc } },
261 { { 0xa1, 0xfd, 0xff, 0xff, 0xff, 0xc3 }, /* movl 0xfffffffd,%eax; ret */
262 0, 5, FALSE, STATUS_ACCESS_VIOLATION, 2, { 0, 0xfffffffd } },
263/* 35 */
264 { { 0xa1, 0xfe, 0xff, 0xff, 0xff, 0xc3 }, /* movl 0xfffffffe,%eax; ret */
265 0, 5, FALSE, STATUS_ACCESS_VIOLATION, 2, { 0, 0xfffffffe } },
266 { { 0xa1, 0xff, 0xff, 0xff, 0xff, 0xc3 }, /* movl 0xffffffff,%eax; ret */
267 0, 5, FALSE, STATUS_ACCESS_VIOLATION, 2, { 0, 0xffffffff } },
268
269 /* test memory writes */
270 { { 0xa3, 0xfc, 0xff, 0xff, 0xff, 0xc3 }, /* movl %eax,0xfffffffc; ret */
271 0, 5, FALSE, STATUS_ACCESS_VIOLATION, 2, { 1, 0xfffffffc } },
272 { { 0xa3, 0xfd, 0xff, 0xff, 0xff, 0xc3 }, /* movl %eax,0xfffffffd; ret */
273 0, 5, FALSE, STATUS_ACCESS_VIOLATION, 2, { 1, 0xfffffffd } },
274 { { 0xa3, 0xfe, 0xff, 0xff, 0xff, 0xc3 }, /* movl %eax,0xfffffffe; ret */
275 0, 5, FALSE, STATUS_ACCESS_VIOLATION, 2, { 1, 0xfffffffe } },
276/* 40 */
277 { { 0xa3, 0xff, 0xff, 0xff, 0xff, 0xc3 }, /* movl %eax,0xffffffff; ret */
278 0, 5, FALSE, STATUS_ACCESS_VIOLATION, 2, { 1, 0xffffffff } },
279
280 /* test exception with cleared %ds and %es (broken on Wow64) */
281 { { 0x1e, 0x06, 0x31, 0xc0, 0x8e, 0xd8, 0x8e, 0xc0, 0xfa, 0x07, 0x1f, 0xc3 },
282 /* push %ds; push %es; xorl %eax,%eax; mov %ax,%ds; mov %ax,%es; cli; pop %es; pop %ds; ret */
284
285 { { 0xf1, 0x90, 0xc3 }, /* icebp; nop; ret */
286 1, 1, FALSE, STATUS_SINGLE_STEP, 0 },
287 { { 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, /* mov $0xb8b8b8b8, %eax */
288 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, /* mov $0xb9b9b9b9, %ecx */
289 0xba, 0xba, 0xba, 0xba, 0xba, /* mov $0xbabababa, %edx */
290 0xcd, 0x2d, 0xc3 }, /* int $0x2d; ret */
291 17, 0, FALSE, STATUS_BREAKPOINT, 3, { 0xb8b8b8b8, 0xb9b9b9b9, 0xbabababa } },
292};
293
294static int got_exception;
295static BOOL have_vectored_api;
296
297static void run_exception_test(void *handler, const void* context,
298 const void *code, unsigned int code_size,
300{
301 struct {
303 const void *context;
304 } exc_frame;
305 void (*func)(void) = code_mem;
306 DWORD oldaccess, oldaccess2;
307
308 exc_frame.frame.Handler = handler;
309 exc_frame.frame.Prev = NtCurrentTeb()->Tib.ExceptionList;
310 exc_frame.context = context;
311
312 memcpy(code_mem, code, code_size);
313 if(access)
314 VirtualProtect(code_mem, code_size, access, &oldaccess);
315
316 NtCurrentTeb()->Tib.ExceptionList = &exc_frame.frame;
317 func();
318 NtCurrentTeb()->Tib.ExceptionList = exc_frame.frame.Prev;
319
320 if(access)
321 VirtualProtect(code_mem, code_size, oldaccess, &oldaccess2);
322}
323
324static LONG CALLBACK rtlraiseexception_vectored_handler(EXCEPTION_POINTERS *ExceptionInfo)
325{
326 PCONTEXT context = ExceptionInfo->ContextRecord;
327 PEXCEPTION_RECORD rec = ExceptionInfo->ExceptionRecord;
328 trace("vect. handler %08x addr:%p context.Eip:%x\n", rec->ExceptionCode,
329 rec->ExceptionAddress, context->Eip);
330
331 ok(rec->ExceptionAddress == (char *)code_mem + 0xb, "ExceptionAddress at %p instead of %p\n",
332 rec->ExceptionAddress, (char *)code_mem + 0xb);
333
335 ok((void *)context->Eax == pRtlRaiseException ||
336 broken( is_wow64 && context->Eax == 0xf00f00f1 ), /* broken on vista */
337 "debugger managed to modify Eax to %x should be %p\n",
338 context->Eax, pRtlRaiseException);
339
340 /* check that context.Eip is fixed up only for EXCEPTION_BREAKPOINT
341 * even if raised by RtlRaiseException
342 */
344 {
345 ok(context->Eip == (DWORD)code_mem + 0xa ||
346 broken(context->Eip == (DWORD)code_mem + 0xb), /* win2k3 */
347 "Eip at %x instead of %x or %x\n", context->Eip,
348 (DWORD)code_mem + 0xa, (DWORD)code_mem + 0xb);
349 }
350 else
351 {
352 ok(context->Eip == (DWORD)code_mem + 0xb, "Eip at %x instead of %x\n",
353 context->Eip, (DWORD)code_mem + 0xb);
354 }
355
356 /* test if context change is preserved from vectored handler to stack handlers */
357 context->Eax = 0xf00f00f0;
359}
360
361static DWORD rtlraiseexception_handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
363{
364 trace( "exception: %08x flags:%x addr:%p context: Eip:%x\n",
365 rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress, context->Eip );
366
367 ok(rec->ExceptionAddress == (char *)code_mem + 0xb, "ExceptionAddress at %p instead of %p\n",
368 rec->ExceptionAddress, (char *)code_mem + 0xb);
369
370 /* check that context.Eip is fixed up only for EXCEPTION_BREAKPOINT
371 * even if raised by RtlRaiseException
372 */
374 {
375 ok(context->Eip == (DWORD)code_mem + 0xa ||
376 broken(context->Eip == (DWORD)code_mem + 0xb), /* win2k3 */
377 "Eip at %x instead of %x or %x\n", context->Eip,
378 (DWORD)code_mem + 0xa, (DWORD)code_mem + 0xb);
379 }
380 else
381 {
382 ok(context->Eip == (DWORD)code_mem + 0xb, "Eip at %x instead of %x\n",
383 context->Eip, (DWORD)code_mem + 0xb);
384 }
385
386 if(have_vectored_api)
387 ok(context->Eax == 0xf00f00f0, "Eax is %x, should have been set to 0xf00f00f0 in vectored handler\n",
388 context->Eax);
389
390 /* give the debugger a chance to examine the state a second time */
391 /* without the exception handler changing Eip */
392 if (test_stage == 2)
394
395 /* Eip in context is decreased by 1
396 * Increase it again, else execution will continue in the middle of an instruction */
397 if(rec->ExceptionCode == EXCEPTION_BREAKPOINT && (context->Eip == (DWORD)code_mem + 0xa))
398 context->Eip += 1;
400}
401
402
403static const BYTE call_one_arg_code[] = {
404 0x8b, 0x44, 0x24, 0x08, /* mov 0x8(%esp),%eax */
405 0x50, /* push %eax */
406 0x8b, 0x44, 0x24, 0x08, /* mov 0x8(%esp),%eax */
407 0xff, 0xd0, /* call *%eax */
408 0x90, /* nop */
409 0x90, /* nop */
410 0x90, /* nop */
411 0x90, /* nop */
412 0xc3, /* ret */
413};
414
415
416static void run_rtlraiseexception_test(DWORD exceptioncode)
417{
420 PVOID vectored_handler = NULL;
421
422 void (*func)(void* function, EXCEPTION_RECORD* record) = code_mem;
423
424 record.ExceptionCode = exceptioncode;
425 record.ExceptionFlags = 0;
426 record.ExceptionRecord = NULL;
427 record.ExceptionAddress = NULL; /* does not matter, copied return address */
428 record.NumberParameters = 0;
429
430 frame.Handler = rtlraiseexception_handler;
431 frame.Prev = NtCurrentTeb()->Tib.ExceptionList;
432
433 memcpy(code_mem, call_one_arg_code, sizeof(call_one_arg_code));
434
435 NtCurrentTeb()->Tib.ExceptionList = &frame;
436 if (have_vectored_api)
437 {
438 vectored_handler = pRtlAddVectoredExceptionHandler(TRUE, &rtlraiseexception_vectored_handler);
439 ok(vectored_handler != 0, "RtlAddVectoredExceptionHandler failed\n");
440 }
441
442 func(pRtlRaiseException, &record);
443 ok( record.ExceptionAddress == (char *)code_mem + 0x0b,
444 "address set to %p instead of %p\n", record.ExceptionAddress, (char *)code_mem + 0x0b );
445
446 if (have_vectored_api)
447 pRtlRemoveVectoredExceptionHandler(vectored_handler);
448 NtCurrentTeb()->Tib.ExceptionList = frame.Prev;
449}
450
451static void test_rtlraiseexception(void)
452{
453 if (!pRtlRaiseException)
454 {
455 skip("RtlRaiseException not found\n");
456 return;
457 }
458
459 /* test without debugger */
460 run_rtlraiseexception_test(0x12345);
461 run_rtlraiseexception_test(EXCEPTION_BREAKPOINT);
462 run_rtlraiseexception_test(EXCEPTION_INVALID_HANDLE);
463}
464
465static DWORD unwind_expected_eax;
466
467static DWORD unwind_handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
469{
470 trace("exception: %08x flags:%x addr:%p context: Eip:%x\n",
472
473 ok(rec->ExceptionCode == STATUS_UNWIND, "ExceptionCode is %08x instead of %08x\n",
475 ok(rec->ExceptionAddress == (char *)code_mem + 0x22, "ExceptionAddress at %p instead of %p\n",
476 rec->ExceptionAddress, (char *)code_mem + 0x22);
477 ok(context->Eax == unwind_expected_eax, "context->Eax is %08x instead of %08x\n",
478 context->Eax, unwind_expected_eax);
479
480 context->Eax += 1;
482}
483
484static const BYTE call_unwind_code[] = {
485 0x55, /* push %ebp */
486 0x53, /* push %ebx */
487 0x56, /* push %esi */
488 0x57, /* push %edi */
489 0xe8, 0x00, 0x00, 0x00, 0x00, /* call 0 */
490 0x58, /* 0: pop %eax */
491 0x05, 0x1e, 0x00, 0x00, 0x00, /* add $0x1e,%eax */
492 0xff, 0x74, 0x24, 0x20, /* push 0x20(%esp) */
493 0xff, 0x74, 0x24, 0x20, /* push 0x20(%esp) */
494 0x50, /* push %eax */
495 0xff, 0x74, 0x24, 0x24, /* push 0x24(%esp) */
496 0x8B, 0x44, 0x24, 0x24, /* mov 0x24(%esp),%eax */
497 0xff, 0xd0, /* call *%eax */
498 0x5f, /* pop %edi */
499 0x5e, /* pop %esi */
500 0x5b, /* pop %ebx */
501 0x5d, /* pop %ebp */
502 0xc3, /* ret */
503 0xcc, /* int $3 */
504};
505
506static void test_unwind(void)
507{
508 EXCEPTION_REGISTRATION_RECORD frames[2], *frame2 = &frames[0], *frame1 = &frames[1];
509 DWORD (*func)(void* function, EXCEPTION_REGISTRATION_RECORD *pEndFrame, EXCEPTION_RECORD* record, DWORD retval) = code_mem;
510 DWORD retval;
511
512 memcpy(code_mem, call_unwind_code, sizeof(call_unwind_code));
513
514 /* add first unwind handler */
515 frame1->Handler = unwind_handler;
516 frame1->Prev = NtCurrentTeb()->Tib.ExceptionList;
517 NtCurrentTeb()->Tib.ExceptionList = frame1;
518
519 /* add second unwind handler */
520 frame2->Handler = unwind_handler;
521 frame2->Prev = NtCurrentTeb()->Tib.ExceptionList;
522 NtCurrentTeb()->Tib.ExceptionList = frame2;
523
524 /* test unwind to current frame */
525 unwind_expected_eax = 0xDEAD0000;
526 retval = func(pRtlUnwind, frame2, NULL, 0xDEAD0000);
527 ok(retval == 0xDEAD0000, "RtlUnwind returned eax %08x instead of %08x\n", retval, 0xDEAD0000);
528 ok(NtCurrentTeb()->Tib.ExceptionList == frame2, "Exception record points to %p instead of %p\n",
529 NtCurrentTeb()->Tib.ExceptionList, frame2);
530
531 /* unwind to frame1 */
532 unwind_expected_eax = 0xDEAD0000;
533 retval = func(pRtlUnwind, frame1, NULL, 0xDEAD0000);
534 ok(retval == 0xDEAD0001, "RtlUnwind returned eax %08x instead of %08x\n", retval, 0xDEAD0001);
535 ok(NtCurrentTeb()->Tib.ExceptionList == frame1, "Exception record points to %p instead of %p\n",
536 NtCurrentTeb()->Tib.ExceptionList, frame1);
537
538 /* restore original handler */
539 NtCurrentTeb()->Tib.ExceptionList = frame1->Prev;
540}
541
544{
545 const struct exception *except = *(const struct exception **)(frame + 1);
546 unsigned int i, parameter_count, entry = except - exceptions;
547
548 got_exception++;
549 trace( "exception %u: %x flags:%x addr:%p\n",
551
552 ok( rec->ExceptionCode == except->status ||
553 (except->alt_status != 0 && rec->ExceptionCode == except->alt_status),
554 "%u: Wrong exception code %x/%x\n", entry, rec->ExceptionCode, except->status );
555 ok( context->Eip == (DWORD_PTR)code_mem + except->offset,
556 "%u: Unexpected eip %#x/%#lx\n", entry,
557 context->Eip, (DWORD_PTR)code_mem + except->offset );
558 ok( rec->ExceptionAddress == (char*)context->Eip ||
559 (rec->ExceptionCode == STATUS_BREAKPOINT && rec->ExceptionAddress == (char*)context->Eip + 1),
560 "%u: Unexpected exception address %p/%p\n", entry,
561 rec->ExceptionAddress, (char*)context->Eip );
562
563 if (except->status == STATUS_BREAKPOINT && is_wow64)
564 parameter_count = 1;
565 else if (except->alt_status == 0 || rec->ExceptionCode != except->alt_status)
566 parameter_count = except->nb_params;
567 else
568 parameter_count = except->alt_nb_params;
569
570 ok( rec->NumberParameters == parameter_count,
571 "%u: Unexpected parameter count %u/%u\n", entry, rec->NumberParameters, parameter_count );
572
573 /* Most CPUs (except Intel Core apparently) report a segment limit violation */
574 /* instead of page faults for accesses beyond 0xffffffff */
575 if (except->nb_params == 2 && except->params[1] >= 0xfffffffd)
576 {
577 if (rec->ExceptionInformation[0] == 0 && rec->ExceptionInformation[1] == 0xffffffff)
578 goto skip_params;
579 }
580
581 /* Seems that both 0xbee8 and 0xfffffffff can be returned in windows */
582 if (except->nb_params == 2 && rec->NumberParameters == 2
583 && except->params[1] == 0xbee8 && rec->ExceptionInformation[1] == 0xffffffff
584 && except->params[0] == rec->ExceptionInformation[0])
585 {
586 goto skip_params;
587 }
588
589 if (except->alt_status == 0 || rec->ExceptionCode != except->alt_status)
590 {
591 for (i = 0; i < rec->NumberParameters; i++)
592 ok( rec->ExceptionInformation[i] == except->params[i],
593 "%u: Wrong parameter %d: %lx/%x\n",
594 entry, i, rec->ExceptionInformation[i], except->params[i] );
595 }
596 else
597 {
598 for (i = 0; i < rec->NumberParameters; i++)
599 ok( rec->ExceptionInformation[i] == except->alt_params[i],
600 "%u: Wrong parameter %d: %lx/%x\n",
601 entry, i, rec->ExceptionInformation[i], except->alt_params[i] );
602 }
603
604skip_params:
605 /* don't handle exception if it's not the address we expected */
606 if (context->Eip != (DWORD_PTR)code_mem + except->offset) return ExceptionContinueSearch;
607
608 context->Eip += except->length;
610}
611
612static void test_prot_fault(void)
613{
614 unsigned int i;
615
616 for (i = 0; i < sizeof(exceptions)/sizeof(exceptions[0]); i++)
617 {
618 if (is_wow64 && exceptions[i].wow64_broken && !strcmp( winetest_platform, "windows" ))
619 {
620 skip( "Exception %u broken on Wow64\n", i );
621 continue;
622 }
623 got_exception = 0;
624 run_exception_test(handler, &exceptions[i], &exceptions[i].code,
625 sizeof(exceptions[i].code), 0);
626 if (!i && !got_exception)
627 {
628 trace( "No exception, assuming win9x, no point in testing further\n" );
629 break;
630 }
631 ok( got_exception == (exceptions[i].status != 0),
632 "%u: bad exception count %d\n", i, got_exception );
633 }
634}
635
636struct dbgreg_test {
637 DWORD dr0, dr1, dr2, dr3, dr6, dr7;
638};
639
640/* test handling of debug registers */
641static DWORD dreg_handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
643{
644 const struct dbgreg_test *test = *(const struct dbgreg_test **)(frame + 1);
645
646 context->Eip += 2; /* Skips the popl (%eax) */
647 context->Dr0 = test->dr0;
648 context->Dr1 = test->dr1;
649 context->Dr2 = test->dr2;
650 context->Dr3 = test->dr3;
651 context->Dr6 = test->dr6;
652 context->Dr7 = test->dr7;
654}
655
656#define CHECK_DEBUG_REG(n, m) \
657 ok((ctx.Dr##n & m) == test->dr##n, "(%d) failed to set debug register " #n " to %x, got %x\n", \
658 test_num, test->dr##n, ctx.Dr##n)
659
660static void check_debug_registers(int test_num, const struct dbgreg_test *test)
661{
662 CONTEXT ctx;
664
665 ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
666 status = pNtGetContextThread(GetCurrentThread(), &ctx);
667 ok(status == STATUS_SUCCESS, "NtGetContextThread failed with %x\n", status);
668
669 CHECK_DEBUG_REG(0, ~0);
670 CHECK_DEBUG_REG(1, ~0);
671 CHECK_DEBUG_REG(2, ~0);
672 CHECK_DEBUG_REG(3, ~0);
673 CHECK_DEBUG_REG(6, 0x0f);
674 CHECK_DEBUG_REG(7, ~0xdc00);
675}
676
677static const BYTE segfault_code[5] = {
678 0x31, 0xc0, /* xor %eax,%eax */
679 0x8f, 0x00, /* popl (%eax) - cause exception */
680 0xc3 /* ret */
681};
682
683/* test the single step exception behaviour */
684static DWORD single_step_handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
686{
687 got_exception++;
688 ok (!(context->EFlags & 0x100), "eflags has single stepping bit set\n");
689
690 if( got_exception < 3)
691 context->EFlags |= 0x100; /* single step until popf instruction */
692 else {
693 /* show that the last single step exception on the popf instruction
694 * (which removed the TF bit), still is a EXCEPTION_SINGLE_STEP exception */
696 "exception is not EXCEPTION_SINGLE_STEP: %x\n", rec->ExceptionCode);
697 }
698
700}
701
702static const BYTE single_stepcode[] = {
703 0x9c, /* pushf */
704 0x58, /* pop %eax */
705 0x0d,0,1,0,0, /* or $0x100,%eax */
706 0x50, /* push %eax */
707 0x9d, /* popf */
708 0x35,0,1,0,0, /* xor $0x100,%eax */
709 0x50, /* push %eax */
710 0x9d, /* popf */
711 0xc3
712};
713
714/* Test the alignment check (AC) flag handling. */
715static const BYTE align_check_code[] = {
716 0x55, /* push %ebp */
717 0x89,0xe5, /* mov %esp,%ebp */
718 0x9c, /* pushf */
719 0x58, /* pop %eax */
720 0x0d,0,0,4,0, /* or $0x40000,%eax */
721 0x50, /* push %eax */
722 0x9d, /* popf */
723 0x89,0xe0, /* mov %esp, %eax */
724 0x8b,0x40,0x1, /* mov 0x1(%eax), %eax - cause exception */
725 0x9c, /* pushf */
726 0x58, /* pop %eax */
727 0x35,0,0,4,0, /* xor $0x40000,%eax */
728 0x50, /* push %eax */
729 0x9d, /* popf */
730 0x5d, /* pop %ebp */
731 0xc3, /* ret */
732};
733
734static DWORD align_check_handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
736{
737 ok (!(context->EFlags & 0x40000), "eflags has AC bit set\n");
738 got_exception++;
740}
741
742/* Test the direction flag handling. */
743static const BYTE direction_flag_code[] = {
744 0x55, /* push %ebp */
745 0x89,0xe5, /* mov %esp,%ebp */
746 0xfd, /* std */
747 0xfa, /* cli - cause exception */
748 0x5d, /* pop %ebp */
749 0xc3, /* ret */
750};
751
752static DWORD direction_flag_handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
754{
755#ifdef __GNUC__
756 unsigned int flags;
757 __asm__("pushfl; popl %0; cld" : "=r" (flags) );
758 /* older windows versions don't clear DF properly so don't test */
759 if (flags & 0x400) trace( "eflags has DF bit set\n" );
760#endif
761 ok( context->EFlags & 0x400, "context eflags has DF bit cleared\n" );
762 got_exception++;
763 context->Eip++; /* skip cli */
764 context->EFlags &= ~0x400; /* make sure it is cleared on return */
766}
767
768/* test single stepping over hardware breakpoint */
769static DWORD bpx_handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
771{
772 got_exception++;
774 "wrong exception code: %x\n", rec->ExceptionCode);
775
776 if(got_exception == 1) {
777 /* hw bp exception on first nop */
778 ok( context->Eip == (DWORD)code_mem, "eip is wrong: %x instead of %x\n",
779 context->Eip, (DWORD)code_mem);
780 ok( (context->Dr6 & 0xf) == 1, "B0 flag is not set in Dr6\n");
782 ok( !(context->Dr6 & 0x4000), "BS flag is set in Dr6\n");
783 context->Dr0 = context->Dr0 + 1; /* set hw bp again on next instruction */
784 context->EFlags |= 0x100; /* enable single stepping */
785 } else if( got_exception == 2) {
786 /* single step exception on second nop */
787 ok( context->Eip == (DWORD)code_mem + 1, "eip is wrong: %x instead of %x\n",
788 context->Eip, (DWORD)code_mem + 1);
789 ok( (context->Dr6 & 0x4000), "BS flag is not set in Dr6\n");
790 /* depending on the win version the B0 bit is already set here as well
791 ok( (context->Dr6 & 0xf) == 0, "B0...3 flags in Dr6 shouldn't be set\n"); */
792 context->EFlags |= 0x100;
793 } else if( got_exception == 3) {
794 /* hw bp exception on second nop */
795 ok( context->Eip == (DWORD)code_mem + 1, "eip is wrong: %x instead of %x\n",
796 context->Eip, (DWORD)code_mem + 1);
797 ok( (context->Dr6 & 0xf) == 1, "B0 flag is not set in Dr6\n");
798 ok( !(context->Dr6 & 0x4000), "BS flag is set in Dr6\n");
799 context->Dr0 = 0; /* clear breakpoint */
800 context->EFlags |= 0x100;
801 } else {
802 /* single step exception on ret */
803 ok( context->Eip == (DWORD)code_mem + 2, "eip is wrong: %x instead of %x\n",
804 context->Eip, (DWORD)code_mem + 2);
805 ok( (context->Dr6 & 0xf) == 0, "B0...3 flags in Dr6 shouldn't be set\n");
806 ok( (context->Dr6 & 0x4000), "BS flag is not set in Dr6\n");
807 }
808
809 context->Dr6 = 0; /* clear status register */
811}
812
813static const BYTE dummy_code[] = { 0x90, 0x90, 0xc3 }; /* nop, nop, ret */
814
815/* test int3 handling */
816static DWORD int3_handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
818{
819 ok( rec->ExceptionAddress == code_mem, "exception address not at: %p, but at %p\n",
821 ok( context->Eip == (DWORD)code_mem, "eip not at: %p, but at %#x\n", code_mem, context->Eip);
822 if(context->Eip == (DWORD)code_mem) context->Eip++; /* skip breakpoint */
823
825}
826
827static const BYTE int3_code[] = { 0xCC, 0xc3 }; /* int 3, ret */
828
829static DWORD WINAPI hw_reg_exception_thread( void *arg )
830{
831 int expect = (ULONG_PTR)arg;
832 got_exception = 0;
833 run_exception_test( bpx_handler, NULL, dummy_code, sizeof(dummy_code), 0 );
834 ok( got_exception == expect, "expected %u exceptions, got %d\n", expect, got_exception );
835 return 0;
836}
837
838static void test_exceptions(void)
839{
840 CONTEXT ctx;
842 struct dbgreg_test dreg_test;
843 HANDLE h;
844
845 if (!pNtGetContextThread || !pNtSetContextThread)
846 {
847 skip( "NtGetContextThread/NtSetContextThread not found\n" );
848 return;
849 }
850
851 /* test handling of debug registers */
852 memset(&dreg_test, 0, sizeof(dreg_test));
853
854 dreg_test.dr0 = 0x42424240;
855 dreg_test.dr2 = 0x126bb070;
856 dreg_test.dr3 = 0x0badbad0;
857 dreg_test.dr7 = 0xffff0115;
858 run_exception_test(dreg_handler, &dreg_test, &segfault_code, sizeof(segfault_code), 0);
859 check_debug_registers(1, &dreg_test);
860
861 dreg_test.dr0 = 0x42424242;
862 dreg_test.dr2 = 0x100f0fe7;
863 dreg_test.dr3 = 0x0abebabe;
864 dreg_test.dr7 = 0x115;
865 run_exception_test(dreg_handler, &dreg_test, &segfault_code, sizeof(segfault_code), 0);
866 check_debug_registers(2, &dreg_test);
867
868 /* test single stepping behavior */
869 got_exception = 0;
870 run_exception_test(single_step_handler, NULL, &single_stepcode, sizeof(single_stepcode), 0);
871 ok(got_exception == 3, "expected 3 single step exceptions, got %d\n", got_exception);
872
873 /* test alignment exceptions */
874 got_exception = 0;
875 run_exception_test(align_check_handler, NULL, align_check_code, sizeof(align_check_code), 0);
876 ok(got_exception == 0, "got %d alignment faults, expected 0\n", got_exception);
877
878 /* test direction flag */
879 got_exception = 0;
880 run_exception_test(direction_flag_handler, NULL, direction_flag_code, sizeof(direction_flag_code), 0);
881 ok(got_exception == 1, "got %d exceptions, expected 1\n", got_exception);
882
883 /* test single stepping over hardware breakpoint */
884 memset(&ctx, 0, sizeof(ctx));
885 ctx.Dr0 = (DWORD) code_mem; /* set hw bp on first nop */
886 ctx.Dr7 = 3;
887 ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
888 res = pNtSetContextThread( GetCurrentThread(), &ctx);
889 ok( res == STATUS_SUCCESS, "NtSetContextThread failed with %x\n", res);
890
891 got_exception = 0;
892 run_exception_test(bpx_handler, NULL, dummy_code, sizeof(dummy_code), 0);
893 ok( got_exception == 4,"expected 4 exceptions, got %d\n", got_exception);
894
895 /* test int3 handling */
896 run_exception_test(int3_handler, NULL, int3_code, sizeof(int3_code), 0);
897
898 /* test that hardware breakpoints are not inherited by created threads */
899 res = pNtSetContextThread( GetCurrentThread(), &ctx );
900 ok( res == STATUS_SUCCESS, "NtSetContextThread failed with %x\n", res );
901
902 h = CreateThread( NULL, 0, hw_reg_exception_thread, 0, 0, NULL );
903 WaitForSingleObject( h, 10000 );
904 CloseHandle( h );
905
906 h = CreateThread( NULL, 0, hw_reg_exception_thread, (void *)4, CREATE_SUSPENDED, NULL );
907 ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
908 res = pNtGetContextThread( h, &ctx );
909 ok( res == STATUS_SUCCESS, "NtGetContextThread failed with %x\n", res );
910 ok( ctx.Dr0 == 0, "dr0 %x\n", ctx.Dr0 );
911 ok( ctx.Dr7 == 0, "dr7 %x\n", ctx.Dr7 );
912 ctx.Dr0 = (DWORD)code_mem;
913 ctx.Dr7 = 3;
914 res = pNtSetContextThread( h, &ctx );
915 ok( res == STATUS_SUCCESS, "NtSetContextThread failed with %x\n", res );
916 ResumeThread( h );
917 WaitForSingleObject( h, 10000 );
918 CloseHandle( h );
919
920 ctx.Dr0 = 0;
921 ctx.Dr7 = 0;
922 res = pNtSetContextThread( GetCurrentThread(), &ctx );
923 ok( res == STATUS_SUCCESS, "NtSetContextThread failed with %x\n", res );
924}
925
926static void test_debugger(void)
927{
928 char cmdline[MAX_PATH];
930 STARTUPINFOA si = { 0 };
931 DEBUG_EVENT de;
932 DWORD continuestatus;
933 PVOID code_mem_address = NULL;
935 SIZE_T size_read;
936 BOOL ret;
937 int counter = 0;
938 si.cb = sizeof(si);
939
940 if(!pNtGetContextThread || !pNtSetContextThread || !pNtReadVirtualMemory || !pNtTerminateProcess)
941 {
942 skip("NtGetContextThread, NtSetContextThread, NtReadVirtualMemory or NtTerminateProcess not found\n");
943 return;
944 }
945
946 sprintf(cmdline, "%s %s %s %p", my_argv[0], my_argv[1], "debuggee", &test_stage);
948 ok(ret, "could not create child process error: %u\n", GetLastError());
949 if (!ret)
950 return;
951
952 do
953 {
954 continuestatus = DBG_CONTINUE;
955 ok(WaitForDebugEvent(&de, INFINITE), "reading debug event\n");
956
957 if (de.dwThreadId != pi.dwThreadId)
958 {
959 trace("event %d not coming from main thread, ignoring\n", de.dwDebugEventCode);
961 continue;
962 }
963
965 {
967 {
968 skip("child process loaded at different address, terminating it\n");
969 pNtTerminateProcess(pi.hProcess, 0);
970 }
971 }
973 {
974 CONTEXT ctx;
975 int stage;
976
977 counter++;
978 status = pNtReadVirtualMemory(pi.hProcess, &code_mem, &code_mem_address,
979 sizeof(code_mem_address), &size_read);
980 ok(!status,"NtReadVirtualMemory failed with 0x%x\n", status);
981 status = pNtReadVirtualMemory(pi.hProcess, &test_stage, &stage,
982 sizeof(stage), &size_read);
983 ok(!status,"NtReadVirtualMemory failed with 0x%x\n", status);
984
985 ctx.ContextFlags = CONTEXT_FULL;
986 status = pNtGetContextThread(pi.hThread, &ctx);
987 ok(!status, "NtGetContextThread failed with 0x%x\n", status);
988
989 trace("exception 0x%x at %p firstchance=%d Eip=0x%x, Eax=0x%x\n",
992
993 if (counter > 100)
994 {
995 ok(FALSE, "got way too many exceptions, probably caught in an infinite loop, terminating child\n");
996 pNtTerminateProcess(pi.hProcess, 1);
997 }
998 else if (counter >= 2) /* skip startup breakpoint */
999 {
1000 if (stage == 1)
1001 {
1002 ok((char *)ctx.Eip == (char *)code_mem_address + 0xb, "Eip at %x instead of %p\n",
1003 ctx.Eip, (char *)code_mem_address + 0xb);
1004 /* setting the context from debugger does not affect the context, the exception handlers gets */
1005 /* uncomment once wine is fixed */
1006 /* ctx.Eip = 0x12345; */
1007 ctx.Eax = 0xf00f00f1;
1008
1009 /* let the debuggee handle the exception */
1010 continuestatus = DBG_EXCEPTION_NOT_HANDLED;
1011 }
1012 else if (stage == 2)
1013 {
1014 if (de.u.Exception.dwFirstChance)
1015 {
1016 /* debugger gets first chance exception with unmodified ctx.Eip */
1017 ok((char *)ctx.Eip == (char *)code_mem_address + 0xb, "Eip at 0x%x instead of %p\n",
1018 ctx.Eip, (char *)code_mem_address + 0xb);
1019
1020 /* setting the context from debugger does not affect the context, the exception handlers gets */
1021 /* uncomment once wine is fixed */
1022 /* ctx.Eip = 0x12345; */
1023 ctx.Eax = 0xf00f00f1;
1024
1025 /* pass exception to debuggee
1026 * exception will not be handled and
1027 * a second chance exception will be raised */
1028 continuestatus = DBG_EXCEPTION_NOT_HANDLED;
1029 }
1030 else
1031 {
1032 /* debugger gets context after exception handler has played with it */
1033 /* ctx.Eip is the same value the exception handler got */
1035 {
1036 ok((char *)ctx.Eip == (char *)code_mem_address + 0xa ||
1037 broken(is_wow64 && (char *)ctx.Eip == (char *)code_mem_address + 0xb),
1038 "Eip at 0x%x instead of %p\n",
1039 ctx.Eip, (char *)code_mem_address + 0xa);
1040 /* need to fixup Eip for debuggee */
1041 if ((char *)ctx.Eip == (char *)code_mem_address + 0xa)
1042 ctx.Eip += 1;
1043 }
1044 else
1045 ok((char *)ctx.Eip == (char *)code_mem_address + 0xb, "Eip at 0x%x instead of %p\n",
1046 ctx.Eip, (char *)code_mem_address + 0xb);
1047 /* here we handle exception */
1048 }
1049 }
1050 else if (stage == 7 || stage == 8)
1051 {
1053 "expected EXCEPTION_BREAKPOINT, got %08x\n", de.u.Exception.ExceptionRecord.ExceptionCode);
1054 ok((char *)ctx.Eip == (char *)code_mem_address + 0x1d,
1055 "expected Eip = %p, got 0x%x\n", (char *)code_mem_address + 0x1d, ctx.Eip);
1056
1057 if (stage == 8) continuestatus = DBG_EXCEPTION_NOT_HANDLED;
1058 }
1059 else if (stage == 9 || stage == 10)
1060 {
1062 "expected EXCEPTION_BREAKPOINT, got %08x\n", de.u.Exception.ExceptionRecord.ExceptionCode);
1063 ok((char *)ctx.Eip == (char *)code_mem_address + 2,
1064 "expected Eip = %p, got 0x%x\n", (char *)code_mem_address + 2, ctx.Eip);
1065
1066 if (stage == 10) continuestatus = DBG_EXCEPTION_NOT_HANDLED;
1067 }
1068 else if (stage == 11 || stage == 12)
1069 {
1071 "unexpected exception code %08x, expected %08x\n", de.u.Exception.ExceptionRecord.ExceptionCode,
1074 "unexpected number of parameters %d, expected 0\n", de.u.Exception.ExceptionRecord.NumberParameters);
1075
1076 if (stage == 12) continuestatus = DBG_EXCEPTION_NOT_HANDLED;
1077 }
1078 else
1079 ok(FALSE, "unexpected stage %x\n", stage);
1080
1081 status = pNtSetContextThread(pi.hThread, &ctx);
1082 ok(!status, "NtSetContextThread failed with 0x%x\n", status);
1083 }
1084 }
1086 {
1087 int stage;
1088#ifdef __REACTOS__
1089 /* This will catch our DPRINTs, such as
1090 * "WARNING: RtlpDphTargetDllsLogicInitialize at ..\..\lib\rtl\heappage.c:1283 is UNIMPLEMENTED!"
1091 * so we need a full-size buffer to avoid a stack overflow
1092 */
1093 char buffer[513];
1094#else
1095 char buffer[64];
1096#endif
1097
1098 status = pNtReadVirtualMemory(pi.hProcess, &test_stage, &stage,
1099 sizeof(stage), &size_read);
1100 ok(!status,"NtReadVirtualMemory failed with 0x%x\n", status);
1101
1102 ok(!de.u.DebugString.fUnicode, "unexpected unicode debug string event\n");
1103 ok(de.u.DebugString.nDebugStringLength < sizeof(buffer) - 1, "buffer not large enough to hold %d bytes\n",
1105
1106 memset(buffer, 0, sizeof(buffer));
1107 status = pNtReadVirtualMemory(pi.hProcess, de.u.DebugString.lpDebugStringData, buffer,
1108 de.u.DebugString.nDebugStringLength, &size_read);
1109 ok(!status,"NtReadVirtualMemory failed with 0x%x\n", status);
1110
1111 if (stage == 3 || stage == 4)
1112 ok(!strcmp(buffer, "Hello World"), "got unexpected debug string '%s'\n", buffer);
1113 else /* ignore unrelated debug strings like 'SHIMVIEW: ShimInfo(Complete)' */
1114 ok(strstr(buffer, "SHIMVIEW") != NULL, "unexpected stage %x, got debug string event '%s'\n", stage, buffer);
1115
1116 if (stage == 4) continuestatus = DBG_EXCEPTION_NOT_HANDLED;
1117 }
1118 else if (de.dwDebugEventCode == RIP_EVENT)
1119 {
1120 int stage;
1121
1122 status = pNtReadVirtualMemory(pi.hProcess, &test_stage, &stage,
1123 sizeof(stage), &size_read);
1124 ok(!status,"NtReadVirtualMemory failed with 0x%x\n", status);
1125
1126 if (stage == 5 || stage == 6)
1127 {
1128 ok(de.u.RipInfo.dwError == 0x11223344, "got unexpected rip error code %08x, expected %08x\n",
1129 de.u.RipInfo.dwError, 0x11223344);
1130 ok(de.u.RipInfo.dwType == 0x55667788, "got unexpected rip type %08x, expected %08x\n",
1131 de.u.RipInfo.dwType, 0x55667788);
1132 }
1133 else
1134 ok(FALSE, "unexpected stage %x\n", stage);
1135
1136 if (stage == 6) continuestatus = DBG_EXCEPTION_NOT_HANDLED;
1137 }
1138
1139 ContinueDebugEvent(de.dwProcessId, de.dwThreadId, continuestatus);
1140
1142
1143 winetest_wait_child_process( pi.hProcess );
1144 ret = CloseHandle(pi.hThread);
1145 ok(ret, "error %u\n", GetLastError());
1146 ret = CloseHandle(pi.hProcess);
1147 ok(ret, "error %u\n", GetLastError());
1148
1149 return;
1150}
1151
1152static DWORD simd_fault_handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
1154{
1155 int *stage = *(int **)(frame + 1);
1156
1157 got_exception++;
1158
1159 if( *stage == 1) {
1160 /* fault while executing sse instruction */
1161 context->Eip += 3; /* skip addps */
1163 }
1164 else if ( *stage == 2 || *stage == 3 ) {
1165 /* stage 2 - divide by zero fault */
1166 /* stage 3 - invalid operation fault */
1168 skip("system doesn't support SIMD exceptions\n");
1169 else {
1171 "exception code: %#x, should be %#x\n",
1173 ok( rec->NumberParameters == 1 || broken(is_wow64 && rec->NumberParameters == 2),
1174 "# of params: %i, should be 1\n",
1175 rec->NumberParameters);
1176 if( rec->NumberParameters == 1 )
1177 ok( rec->ExceptionInformation[0] == 0, "param #1: %lx, should be 0\n", rec->ExceptionInformation[0]);
1178 }
1179 context->Eip += 3; /* skip divps */
1180 }
1181 else
1182 ok(FALSE, "unexpected stage %x\n", *stage);
1183
1185}
1186
1187static const BYTE simd_exception_test[] = {
1188 0x83, 0xec, 0x4, /* sub $0x4, %esp */
1189 0x0f, 0xae, 0x1c, 0x24, /* stmxcsr (%esp) */
1190 0x8b, 0x04, 0x24, /* mov (%esp),%eax * store mxcsr */
1191 0x66, 0x81, 0x24, 0x24, 0xff, 0xfd, /* andw $0xfdff,(%esp) * enable divide by */
1192 0x0f, 0xae, 0x14, 0x24, /* ldmxcsr (%esp) * zero exceptions */
1193 0x6a, 0x01, /* push $0x1 */
1194 0x6a, 0x01, /* push $0x1 */
1195 0x6a, 0x01, /* push $0x1 */
1196 0x6a, 0x01, /* push $0x1 */
1197 0x0f, 0x10, 0x0c, 0x24, /* movups (%esp),%xmm1 * fill dividend */
1198 0x0f, 0x57, 0xc0, /* xorps %xmm0,%xmm0 * clear divisor */
1199 0x0f, 0x5e, 0xc8, /* divps %xmm0,%xmm1 * generate fault */
1200 0x83, 0xc4, 0x10, /* add $0x10,%esp */
1201 0x89, 0x04, 0x24, /* mov %eax,(%esp) * restore to old mxcsr */
1202 0x0f, 0xae, 0x14, 0x24, /* ldmxcsr (%esp) */
1203 0x83, 0xc4, 0x04, /* add $0x4,%esp */
1204 0xc3, /* ret */
1205};
1206
1207static const BYTE simd_exception_test2[] = {
1208 0x83, 0xec, 0x4, /* sub $0x4, %esp */
1209 0x0f, 0xae, 0x1c, 0x24, /* stmxcsr (%esp) */
1210 0x8b, 0x04, 0x24, /* mov (%esp),%eax * store mxcsr */
1211 0x66, 0x81, 0x24, 0x24, 0x7f, 0xff, /* andw $0xff7f,(%esp) * enable invalid */
1212 0x0f, 0xae, 0x14, 0x24, /* ldmxcsr (%esp) * operation exceptions */
1213 0x0f, 0x57, 0xc9, /* xorps %xmm1,%xmm1 * clear dividend */
1214 0x0f, 0x57, 0xc0, /* xorps %xmm0,%xmm0 * clear divisor */
1215 0x0f, 0x5e, 0xc8, /* divps %xmm0,%xmm1 * generate fault */
1216 0x89, 0x04, 0x24, /* mov %eax,(%esp) * restore to old mxcsr */
1217 0x0f, 0xae, 0x14, 0x24, /* ldmxcsr (%esp) */
1218 0x83, 0xc4, 0x04, /* add $0x4,%esp */
1219 0xc3, /* ret */
1220};
1221
1222static const BYTE sse_check[] = {
1223 0x0f, 0x58, 0xc8, /* addps %xmm0,%xmm1 */
1224 0xc3, /* ret */
1225};
1226
1227static void test_simd_exceptions(void)
1228{
1229 int stage;
1230
1231 /* test if CPU & OS can do sse */
1232 stage = 1;
1233 got_exception = 0;
1234 run_exception_test(simd_fault_handler, &stage, sse_check, sizeof(sse_check), 0);
1235 if(got_exception) {
1236 skip("system doesn't support SSE\n");
1237 return;
1238 }
1239
1240 /* generate a SIMD exception */
1241 stage = 2;
1242 got_exception = 0;
1243 run_exception_test(simd_fault_handler, &stage, simd_exception_test,
1244 sizeof(simd_exception_test), 0);
1245 ok(got_exception == 1, "got exception: %i, should be 1\n", got_exception);
1246
1247 /* generate a SIMD exception, test FPE_FLTINV */
1248 stage = 3;
1249 got_exception = 0;
1250 run_exception_test(simd_fault_handler, &stage, simd_exception_test2,
1251 sizeof(simd_exception_test2), 0);
1252 ok(got_exception == 1, "got exception: %i, should be 1\n", got_exception);
1253}
1254
1255struct fpu_exception_info
1256{
1257 DWORD exception_code;
1258 DWORD exception_offset;
1259 DWORD eip_offset;
1260};
1261
1262static DWORD fpu_exception_handler(EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
1264{
1265 struct fpu_exception_info *info = *(struct fpu_exception_info **)(frame + 1);
1266
1267 info->exception_code = rec->ExceptionCode;
1268 info->exception_offset = (BYTE *)rec->ExceptionAddress - (BYTE *)code_mem;
1269 info->eip_offset = context->Eip - (DWORD)code_mem;
1270
1271 ++context->Eip;
1273}
1274
1275static void test_fpu_exceptions(void)
1276{
1277 static const BYTE fpu_exception_test_ie[] =
1278 {
1279 0x83, 0xec, 0x04, /* sub $0x4,%esp */
1280 0x66, 0xc7, 0x04, 0x24, 0xfe, 0x03, /* movw $0x3fe,(%esp) */
1281 0x9b, 0xd9, 0x7c, 0x24, 0x02, /* fstcw 0x2(%esp) */
1282 0xd9, 0x2c, 0x24, /* fldcw (%esp) */
1283 0xd9, 0xee, /* fldz */
1284 0xd9, 0xe8, /* fld1 */
1285 0xde, 0xf1, /* fdivp */
1286 0xdd, 0xd8, /* fstp %st(0) */
1287 0xdd, 0xd8, /* fstp %st(0) */
1288 0x9b, /* fwait */
1289 0xdb, 0xe2, /* fnclex */
1290 0xd9, 0x6c, 0x24, 0x02, /* fldcw 0x2(%esp) */
1291 0x83, 0xc4, 0x04, /* add $0x4,%esp */
1292 0xc3, /* ret */
1293 };
1294
1295 static const BYTE fpu_exception_test_de[] =
1296 {
1297 0x83, 0xec, 0x04, /* sub $0x4,%esp */
1298 0x66, 0xc7, 0x04, 0x24, 0xfb, 0x03, /* movw $0x3fb,(%esp) */
1299 0x9b, 0xd9, 0x7c, 0x24, 0x02, /* fstcw 0x2(%esp) */
1300 0xd9, 0x2c, 0x24, /* fldcw (%esp) */
1301 0xdd, 0xd8, /* fstp %st(0) */
1302 0xd9, 0xee, /* fldz */
1303 0xd9, 0xe8, /* fld1 */
1304 0xde, 0xf1, /* fdivp */
1305 0x9b, /* fwait */
1306 0xdb, 0xe2, /* fnclex */
1307 0xdd, 0xd8, /* fstp %st(0) */
1308 0xdd, 0xd8, /* fstp %st(0) */
1309 0xd9, 0x6c, 0x24, 0x02, /* fldcw 0x2(%esp) */
1310 0x83, 0xc4, 0x04, /* add $0x4,%esp */
1311 0xc3, /* ret */
1312 };
1313
1314 struct fpu_exception_info info;
1315
1316 memset(&info, 0, sizeof(info));
1317 run_exception_test(fpu_exception_handler, &info, fpu_exception_test_ie, sizeof(fpu_exception_test_ie), 0);
1318 ok(info.exception_code == EXCEPTION_FLT_STACK_CHECK,
1319 "Got exception code %#x, expected EXCEPTION_FLT_STACK_CHECK\n", info.exception_code);
1320 ok(info.exception_offset == 0x19 ||
1321 broken( info.exception_offset == info.eip_offset ),
1322 "Got exception offset %#x, expected 0x19\n", info.exception_offset);
1323 ok(info.eip_offset == 0x1b, "Got EIP offset %#x, expected 0x1b\n", info.eip_offset);
1324
1325 memset(&info, 0, sizeof(info));
1326 run_exception_test(fpu_exception_handler, &info, fpu_exception_test_de, sizeof(fpu_exception_test_de), 0);
1327 ok(info.exception_code == EXCEPTION_FLT_DIVIDE_BY_ZERO,
1328 "Got exception code %#x, expected EXCEPTION_FLT_DIVIDE_BY_ZERO\n", info.exception_code);
1329 ok(info.exception_offset == 0x17 ||
1330 broken( info.exception_offset == info.eip_offset ),
1331 "Got exception offset %#x, expected 0x17\n", info.exception_offset);
1332 ok(info.eip_offset == 0x19, "Got EIP offset %#x, expected 0x19\n", info.eip_offset);
1333}
1334
1335struct dpe_exception_info {
1336 BOOL exception_caught;
1337 DWORD exception_info;
1338};
1339
1340static DWORD dpe_exception_handler(EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
1342{
1343 DWORD old_prot;
1344 struct dpe_exception_info *info = *(struct dpe_exception_info **)(frame + 1);
1345
1347 "Exception code %08x\n", rec->ExceptionCode);
1348 ok(rec->NumberParameters == 2,
1349 "Parameter count: %d\n", rec->NumberParameters);
1351 "Exception address: %p, expected %p\n",
1353
1354 info->exception_info = rec->ExceptionInformation[0];
1355 info->exception_caught = TRUE;
1356
1359}
1360
1361static void test_dpe_exceptions(void)
1362{
1363 static const BYTE single_ret[] = {0xC3};
1364 struct dpe_exception_info info;
1365 NTSTATUS stat;
1366 BOOL has_hw_support;
1367 BOOL is_permanent = FALSE, can_test_without = TRUE, can_test_with = TRUE;
1368 DWORD val;
1369 ULONG len;
1370
1371 /* Query DEP with len too small */
1372 stat = pNtQueryInformationProcess(GetCurrentProcess(), ProcessExecuteFlags, &val, sizeof val - 1, &len);
1374 {
1375 skip("This software platform does not support DEP\n");
1376 return;
1377 }
1378 ok(stat == STATUS_INFO_LENGTH_MISMATCH, "buffer too small: %08x\n", stat);
1379
1380 /* Query DEP */
1381 stat = pNtQueryInformationProcess(GetCurrentProcess(), ProcessExecuteFlags, &val, sizeof val, &len);
1382 ok(stat == STATUS_SUCCESS, "querying DEP: status %08x\n", stat);
1383 if(stat == STATUS_SUCCESS)
1384 {
1385 ok(len == sizeof val, "returned length: %d\n", len);
1387 {
1388 skip("toggling DEP impossible - status locked\n");
1389 is_permanent = TRUE;
1391 can_test_without = FALSE;
1392 else
1393 can_test_with = FALSE;
1394 }
1395 }
1396
1397 if(!is_permanent)
1398 {
1399 /* Enable DEP */
1401 stat = pNtSetInformationProcess(GetCurrentProcess(), ProcessExecuteFlags, &val, sizeof val);
1402 ok(stat == STATUS_SUCCESS, "enabling DEP: status %08x\n", stat);
1403 }
1404
1405 if(can_test_with)
1406 {
1407 /* Try access to locked page with DEP on*/
1408 info.exception_caught = FALSE;
1409 run_exception_test(dpe_exception_handler, &info, single_ret, sizeof(single_ret), PAGE_NOACCESS);
1410 ok(info.exception_caught == TRUE, "Execution of disabled memory succeeded\n");
1411 ok(info.exception_info == EXCEPTION_READ_FAULT ||
1412 info.exception_info == EXCEPTION_EXECUTE_FAULT,
1413 "Access violation type: %08x\n", (unsigned)info.exception_info);
1414 has_hw_support = info.exception_info == EXCEPTION_EXECUTE_FAULT;
1415 trace("DEP hardware support: %s\n", has_hw_support?"Yes":"No");
1416
1417 /* Try execution of data with DEP on*/
1418 info.exception_caught = FALSE;
1419 run_exception_test(dpe_exception_handler, &info, single_ret, sizeof(single_ret), PAGE_READWRITE);
1420 if(has_hw_support)
1421 {
1422 ok(info.exception_caught == TRUE, "Execution of data memory succeeded\n");
1423 ok(info.exception_info == EXCEPTION_EXECUTE_FAULT,
1424 "Access violation type: %08x\n", (unsigned)info.exception_info);
1425 }
1426 else
1427 ok(info.exception_caught == FALSE, "Execution trapped without hardware support\n");
1428 }
1429 else
1430 skip("DEP is in AlwaysOff state\n");
1431
1432 if(!is_permanent)
1433 {
1434 /* Disable DEP */
1436 stat = pNtSetInformationProcess(GetCurrentProcess(), ProcessExecuteFlags, &val, sizeof val);
1437 ok(stat == STATUS_SUCCESS, "disabling DEP: status %08x\n", stat);
1438 }
1439
1440 /* page is read without exec here */
1441 if(can_test_without)
1442 {
1443 /* Try execution of data with DEP off */
1444 info.exception_caught = FALSE;
1445 run_exception_test(dpe_exception_handler, &info, single_ret, sizeof(single_ret), PAGE_READWRITE);
1446 ok(info.exception_caught == FALSE, "Execution trapped with DEP turned off\n");
1447
1448 /* Try access to locked page with DEP off - error code is different than
1449 with hardware DEP on */
1450 info.exception_caught = FALSE;
1451 run_exception_test(dpe_exception_handler, &info, single_ret, sizeof(single_ret), PAGE_NOACCESS);
1452 ok(info.exception_caught == TRUE, "Execution of disabled memory succeeded\n");
1453 ok(info.exception_info == EXCEPTION_READ_FAULT,
1454 "Access violation type: %08x\n", (unsigned)info.exception_info);
1455 }
1456 else
1457 skip("DEP is in AlwaysOn state\n");
1458
1459 if(!is_permanent)
1460 {
1461 /* Turn off DEP permanently */
1463 stat = pNtSetInformationProcess(GetCurrentProcess(), ProcessExecuteFlags, &val, sizeof val);
1464 ok(stat == STATUS_SUCCESS, "disabling DEP permanently: status %08x\n", stat);
1465 }
1466
1467 /* Try to turn off DEP */
1469 stat = pNtSetInformationProcess(GetCurrentProcess(), ProcessExecuteFlags, &val, sizeof val);
1470 ok(stat == STATUS_ACCESS_DENIED, "disabling DEP while permanent: status %08x\n", stat);
1471
1472 /* Try to turn on DEP */
1474 stat = pNtSetInformationProcess(GetCurrentProcess(), ProcessExecuteFlags, &val, sizeof val);
1475 ok(stat == STATUS_ACCESS_DENIED, "enabling DEP while permanent: status %08x\n", stat);
1476}
1477
1478static void test_thread_context(void)
1479{
1482 struct expected
1483 {
1484 DWORD Eax, Ebx, Ecx, Edx, Esi, Edi, Ebp, Esp, Eip,
1485 SegCs, SegDs, SegEs, SegFs, SegGs, SegSs, EFlags, prev_frame;
1486 } expect;
1487 NTSTATUS (*func_ptr)( struct expected *res, void *func, void *arg1, void *arg2 ) = (void *)code_mem;
1488
1489 static const BYTE call_func[] =
1490 {
1491 0x55, /* pushl %ebp */
1492 0x89, 0xe5, /* mov %esp,%ebp */
1493 0x50, /* pushl %eax ; add a bit of offset to the stack */
1494 0x50, /* pushl %eax */
1495 0x50, /* pushl %eax */
1496 0x50, /* pushl %eax */
1497 0x8b, 0x45, 0x08, /* mov 0x8(%ebp),%eax */
1498 0x8f, 0x00, /* popl (%eax) */
1499 0x89, 0x58, 0x04, /* mov %ebx,0x4(%eax) */
1500 0x89, 0x48, 0x08, /* mov %ecx,0x8(%eax) */
1501 0x89, 0x50, 0x0c, /* mov %edx,0xc(%eax) */
1502 0x89, 0x70, 0x10, /* mov %esi,0x10(%eax) */
1503 0x89, 0x78, 0x14, /* mov %edi,0x14(%eax) */
1504 0x89, 0x68, 0x18, /* mov %ebp,0x18(%eax) */
1505 0x89, 0x60, 0x1c, /* mov %esp,0x1c(%eax) */
1506 0xff, 0x75, 0x04, /* pushl 0x4(%ebp) */
1507 0x8f, 0x40, 0x20, /* popl 0x20(%eax) */
1508 0x8c, 0x48, 0x24, /* mov %cs,0x24(%eax) */
1509 0x8c, 0x58, 0x28, /* mov %ds,0x28(%eax) */
1510 0x8c, 0x40, 0x2c, /* mov %es,0x2c(%eax) */
1511 0x8c, 0x60, 0x30, /* mov %fs,0x30(%eax) */
1512 0x8c, 0x68, 0x34, /* mov %gs,0x34(%eax) */
1513 0x8c, 0x50, 0x38, /* mov %ss,0x38(%eax) */
1514 0x9c, /* pushf */
1515 0x8f, 0x40, 0x3c, /* popl 0x3c(%eax) */
1516 0xff, 0x75, 0x00, /* pushl 0x0(%ebp) ; previous stack frame */
1517 0x8f, 0x40, 0x40, /* popl 0x40(%eax) */
1518 0x8b, 0x00, /* mov (%eax),%eax */
1519 0xff, 0x75, 0x14, /* pushl 0x14(%ebp) */
1520 0xff, 0x75, 0x10, /* pushl 0x10(%ebp) */
1521 0xff, 0x55, 0x0c, /* call *0xc(%ebp) */
1522 0xc9, /* leave */
1523 0xc3, /* ret */
1524 };
1525
1526 memcpy( func_ptr, call_func, sizeof(call_func) );
1527
1528#define COMPARE(reg) \
1529 ok( context.reg == expect.reg, "wrong " #reg " %08x/%08x\n", context.reg, expect.reg )
1530
1531 memset( &context, 0xcc, sizeof(context) );
1532 memset( &expect, 0xcc, sizeof(expect) );
1533 func_ptr( &expect, pRtlCaptureContext, &context, 0 );
1534 trace( "expect: eax=%08x ebx=%08x ecx=%08x edx=%08x esi=%08x edi=%08x ebp=%08x esp=%08x "
1535 "eip=%08x cs=%04x ds=%04x es=%04x fs=%04x gs=%04x ss=%04x flags=%08x prev=%08x\n",
1536 expect.Eax, expect.Ebx, expect.Ecx, expect.Edx, expect.Esi, expect.Edi,
1537 expect.Ebp, expect.Esp, expect.Eip, expect.SegCs, expect.SegDs, expect.SegEs,
1538 expect.SegFs, expect.SegGs, expect.SegSs, expect.EFlags, expect.prev_frame );
1539 trace( "actual: eax=%08x ebx=%08x ecx=%08x edx=%08x esi=%08x edi=%08x ebp=%08x esp=%08x "
1540 "eip=%08x cs=%04x ds=%04x es=%04x fs=%04x gs=%04x ss=%04x flags=%08x\n",
1541 context.Eax, context.Ebx, context.Ecx, context.Edx, context.Esi, context.Edi,
1542 context.Ebp, context.Esp, context.Eip, context.SegCs, context.SegDs, context.SegEs,
1543 context.SegFs, context.SegGs, context.SegSs, context.EFlags );
1544
1546 broken( context.ContextFlags == 0xcccccccc ), /* <= vista */
1547 "wrong flags %08x\n", context.ContextFlags );
1548 COMPARE( Eax );
1549 COMPARE( Ebx );
1550 COMPARE( Ecx );
1551 COMPARE( Edx );
1552 COMPARE( Esi );
1553 COMPARE( Edi );
1554 COMPARE( Eip );
1555 COMPARE( SegCs );
1556 COMPARE( SegDs );
1557 COMPARE( SegEs );
1558 COMPARE( SegFs );
1559 COMPARE( SegGs );
1560 COMPARE( SegSs );
1561 COMPARE( EFlags );
1562 /* Ebp is from the previous stackframe */
1563 ok( context.Ebp == expect.prev_frame, "wrong Ebp %08x/%08x\n", context.Ebp, expect.prev_frame );
1564 /* Esp is the value on entry to the previous stackframe */
1565 ok( context.Esp == expect.Ebp + 8, "wrong Esp %08x/%08x\n", context.Esp, expect.Ebp + 8 );
1566
1567 memset( &context, 0xcc, sizeof(context) );
1568 memset( &expect, 0xcc, sizeof(expect) );
1570 status = func_ptr( &expect, pNtGetContextThread, (void *)GetCurrentThread(), &context );
1571 ok( status == STATUS_SUCCESS, "NtGetContextThread failed %08x\n", status );
1572 trace( "expect: eax=%08x ebx=%08x ecx=%08x edx=%08x esi=%08x edi=%08x ebp=%08x esp=%08x "
1573 "eip=%08x cs=%04x ds=%04x es=%04x fs=%04x gs=%04x ss=%04x flags=%08x prev=%08x\n",
1574 expect.Eax, expect.Ebx, expect.Ecx, expect.Edx, expect.Esi, expect.Edi,
1575 expect.Ebp, expect.Esp, expect.Eip, expect.SegCs, expect.SegDs, expect.SegEs,
1576 expect.SegFs, expect.SegGs, expect.SegSs, expect.EFlags, expect.prev_frame );
1577 trace( "actual: eax=%08x ebx=%08x ecx=%08x edx=%08x esi=%08x edi=%08x ebp=%08x esp=%08x "
1578 "eip=%08x cs=%04x ds=%04x es=%04x fs=%04x gs=%04x ss=%04x flags=%08x\n",
1579 context.Eax, context.Ebx, context.Ecx, context.Edx, context.Esi, context.Edi,
1580 context.Ebp, context.Esp, context.Eip, context.SegCs, context.SegDs, context.SegEs,
1581 context.SegFs, context.SegGs, context.SegSs, context.EFlags );
1582 /* Eax, Ecx, Edx, EFlags are not preserved */
1583 COMPARE( Ebx );
1584 COMPARE( Esi );
1585 COMPARE( Edi );
1586 COMPARE( Ebp );
1587 /* Esp is the stack upon entry to NtGetContextThread */
1588 ok( context.Esp == expect.Esp - 12 || context.Esp == expect.Esp - 16,
1589 "wrong Esp %08x/%08x\n", context.Esp, expect.Esp );
1590 /* Eip is somewhere close to the NtGetContextThread implementation */
1591 ok( (char *)context.Eip >= (char *)pNtGetContextThread - 0x10000 &&
1592 (char *)context.Eip <= (char *)pNtGetContextThread + 0x10000,
1593 "wrong Eip %08x/%08x\n", context.Eip, (DWORD)pNtGetContextThread );
1594 ok( *(WORD *)context.Eip == 0xc483 || *(WORD *)context.Eip == 0x08c2 || *(WORD *)context.Eip == 0x8dc3,
1595 "expected 0xc483 or 0x08c2 or 0x8dc3, got %04x\n", *(WORD *)context.Eip );
1596 /* segment registers clear the high word */
1597 ok( context.SegCs == LOWORD(expect.SegCs), "wrong SegCs %08x/%08x\n", context.SegCs, expect.SegCs );
1598 ok( context.SegDs == LOWORD(expect.SegDs), "wrong SegDs %08x/%08x\n", context.SegDs, expect.SegDs );
1599 ok( context.SegEs == LOWORD(expect.SegEs), "wrong SegEs %08x/%08x\n", context.SegEs, expect.SegEs );
1600 ok( context.SegFs == LOWORD(expect.SegFs), "wrong SegFs %08x/%08x\n", context.SegFs, expect.SegFs );
1601 ok( context.SegGs == LOWORD(expect.SegGs), "wrong SegGs %08x/%08x\n", context.SegGs, expect.SegGs );
1602 ok( context.SegSs == LOWORD(expect.SegSs), "wrong SegSs %08x/%08x\n", context.SegSs, expect.SegGs );
1603#undef COMPARE
1604}
1605
1606#elif defined(__x86_64__)
1607
1608#define is_wow64 0
1609
1610#ifndef __REACTOS__
1611#define UNW_FLAG_NHANDLER 0
1612#define UNW_FLAG_EHANDLER 1
1613#define UNW_FLAG_UHANDLER 2
1614#define UNW_FLAG_CHAININFO 4
1615#endif // __REACTOS__
1616
1617#define UWOP_PUSH_NONVOL 0
1618#define UWOP_ALLOC_LARGE 1
1619#define UWOP_ALLOC_SMALL 2
1620#define UWOP_SET_FPREG 3
1621#define UWOP_SAVE_NONVOL 4
1622#define UWOP_SAVE_NONVOL_FAR 5
1623#define UWOP_SAVE_XMM128 8
1624#define UWOP_SAVE_XMM128_FAR 9
1625#define UWOP_PUSH_MACHFRAME 10
1626
1627struct results
1628{
1629 int rip_offset; /* rip offset from code start */
1630 int rbp_offset; /* rbp offset from stack pointer */
1631 int handler; /* expect handler to be set? */
1632 int rip; /* expected final rip value */
1633 int frame; /* expected frame return value */
1634 int regs[8][2]; /* expected values for registers */
1635};
1636
1637struct unwind_test
1638{
1639 const BYTE *function;
1640 size_t function_size;
1641 const BYTE *unwind_info;
1642 const struct results *results;
1643 unsigned int nb_results;
1644};
1645
1646enum regs
1647{
1648 rax, rcx, rdx, rbx, rsp, rbp, rsi, rdi,
1649 r8, r9, r10, r11, r12, r13, r14, r15
1650};
1651
1652static const char * const reg_names[16] =
1653{
1654 "rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi",
1655 "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15"
1656};
1657
1658#define UWOP(code,info) (UWOP_##code | ((info) << 4))
1659
1660static void call_virtual_unwind( int testnum, const struct unwind_test *test )
1661{
1662 static const int code_offset = 1024;
1663 static const int unwind_offset = 2048;
1664 void *handler, *data;
1666 RUNTIME_FUNCTION runtime_func;
1667 KNONVOLATILE_CONTEXT_POINTERS ctx_ptr;
1668 UINT i, j, k;
1669 ULONG64 fake_stack[256];
1670 ULONG64 frame, orig_rip, orig_rbp, unset_reg;
1671 UINT unwind_size = 4 + 2 * test->unwind_info[2] + 8;
1672
1673 memcpy( (char *)code_mem + code_offset, test->function, test->function_size );
1674 memcpy( (char *)code_mem + unwind_offset, test->unwind_info, unwind_size );
1675
1676 runtime_func.BeginAddress = code_offset;
1677 runtime_func.EndAddress = code_offset + test->function_size;
1678 runtime_func.UnwindData = unwind_offset;
1679
1680 trace( "code: %p stack: %p\n", code_mem, fake_stack );
1681
1682 for (i = 0; i < test->nb_results; i++)
1683 {
1684 memset( &ctx_ptr, 0, sizeof(ctx_ptr) );
1685 memset( &context, 0x55, sizeof(context) );
1686 memset( &unset_reg, 0x55, sizeof(unset_reg) );
1687 for (j = 0; j < 256; j++) fake_stack[j] = j * 8;
1688
1689 context.Rsp = (ULONG_PTR)fake_stack;
1690 context.Rbp = (ULONG_PTR)fake_stack + test->results[i].rbp_offset;
1691 orig_rbp = context.Rbp;
1692 orig_rip = (ULONG64)code_mem + code_offset + test->results[i].rip_offset;
1693
1694 trace( "%u/%u: rip=%p (%02x) rbp=%p rsp=%p\n", testnum, i,
1695 (void *)orig_rip, *(BYTE *)orig_rip, (void *)orig_rbp, (void *)context.Rsp );
1696
1697 data = (void *)0xdeadbeef;
1699 &runtime_func, &context, &data, &frame, &ctx_ptr );
1700 if (test->results[i].handler)
1701 {
1702 ok( (char *)handler == (char *)code_mem + 0x200,
1703 "%u/%u: wrong handler %p/%p\n", testnum, i, handler, (char *)code_mem + 0x200 );
1704 if (handler) ok( *(DWORD *)data == 0x08070605,
1705 "%u/%u: wrong handler data %p\n", testnum, i, data );
1706 }
1707 else
1708 {
1709 ok( handler == NULL, "%u/%u: handler %p instead of NULL\n", testnum, i, handler );
1710 ok( data == (void *)0xdeadbeef, "%u/%u: handler data set to %p\n", testnum, i, data );
1711 }
1712
1713 ok( context.Rip == test->results[i].rip, "%u/%u: wrong rip %p/%x\n",
1714 testnum, i, (void *)context.Rip, test->results[i].rip );
1715 ok( frame == (ULONG64)fake_stack + test->results[i].frame, "%u/%u: wrong frame %p/%p\n",
1716 testnum, i, (void *)frame, (char *)fake_stack + test->results[i].frame );
1717
1718 for (j = 0; j < 16; j++)
1719 {
1720 static const UINT nb_regs = sizeof(test->results[i].regs) / sizeof(test->results[i].regs[0]);
1721
1722 for (k = 0; k < nb_regs; k++)
1723 {
1724 if (test->results[i].regs[k][0] == -1)
1725 {
1726 k = nb_regs;
1727 break;
1728 }
1729 if (test->results[i].regs[k][0] == j) break;
1730 }
1731
1732 if (j == rsp) /* rsp is special */
1733 {
1734 ok( !ctx_ptr.u2.IntegerContext[j],
1735 "%u/%u: rsp should not be set in ctx_ptr\n", testnum, i );
1736
1737 ok( context.Rsp == (ULONG64)fake_stack + test->results[i].regs[k][1],
1738 "%u/%u: register rsp wrong %p/%p\n",
1739 testnum, i, (void *)context.Rsp, (char *)fake_stack + test->results[i].regs[k][1] );
1740 continue;
1741 }
1742
1743 if (ctx_ptr.u2.IntegerContext[j])
1744 {
1745 ok( k < nb_regs, "%u/%u: register %s should not be set to %lx\n",
1746 testnum, i, reg_names[j], *(&context.Rax + j) );
1747 if (k < nb_regs)
1748 ok( *(&context.Rax + j) == test->results[i].regs[k][1],
1749 "%u/%u: register %s wrong %p/%x\n",
1750 testnum, i, reg_names[j], (void *)*(&context.Rax + j), test->results[i].regs[k][1] );
1751 }
1752 else
1753 {
1754 ok( k == nb_regs, "%u/%u: register %s should be set\n", testnum, i, reg_names[j] );
1755 if (j == rbp)
1756 ok( context.Rbp == orig_rbp, "%u/%u: register rbp wrong %p/unset\n",
1757 testnum, i, (void *)context.Rbp );
1758 else
1759 ok( *(&context.Rax + j) == unset_reg,
1760 "%u/%u: register %s wrong %p/unset\n",
1761 testnum, i, reg_names[j], (void *)*(&context.Rax + j));
1762 }
1763 }
1764 }
1765}
1766
1767static void test_virtual_unwind(void)
1768{
1769 static const BYTE function_0[] =
1770 {
1771 0xff, 0xf5, /* 00: push %rbp */
1772 0x48, 0x81, 0xec, 0x10, 0x01, 0x00, 0x00, /* 02: sub $0x110,%rsp */
1773 0x48, 0x8d, 0x6c, 0x24, 0x30, /* 09: lea 0x30(%rsp),%rbp */
1774 0x48, 0x89, 0x9d, 0xf0, 0x00, 0x00, 0x00, /* 0e: mov %rbx,0xf0(%rbp) */
1775 0x48, 0x89, 0xb5, 0xf8, 0x00, 0x00, 0x00, /* 15: mov %rsi,0xf8(%rbp) */
1776 0x90, /* 1c: nop */
1777 0x48, 0x8b, 0x9d, 0xf0, 0x00, 0x00, 0x00, /* 1d: mov 0xf0(%rbp),%rbx */
1778 0x48, 0x8b, 0xb5, 0xf8, 0x00, 0x00, 0x00, /* 24: mov 0xf8(%rbp),%rsi */
1779 0x48, 0x8d, 0xa5, 0xe0, 0x00, 0x00, 0x00, /* 2b: lea 0xe0(%rbp),%rsp */
1780 0x5d, /* 32: pop %rbp */
1781 0xc3 /* 33: ret */
1782 };
1783
1784 static const BYTE unwind_info_0[] =
1785 {
1786 1 | (UNW_FLAG_EHANDLER << 3), /* version + flags */
1787 0x1c, /* prolog size */
1788 8, /* opcode count */
1789 (0x03 << 4) | rbp, /* frame reg rbp offset 0x30 */
1790
1791 0x1c, UWOP(SAVE_NONVOL, rsi), 0x25, 0, /* 1c: mov %rsi,0x128(%rsp) */
1792 0x15, UWOP(SAVE_NONVOL, rbx), 0x24, 0, /* 15: mov %rbx,0x120(%rsp) */
1793 0x0e, UWOP(SET_FPREG, rbp), /* 0e: lea 0x30(%rsp),rbp */
1794 0x09, UWOP(ALLOC_LARGE, 0), 0x22, 0, /* 09: sub $0x110,%rsp */
1795 0x02, UWOP(PUSH_NONVOL, rbp), /* 02: push %rbp */
1796
1797 0x00, 0x02, 0x00, 0x00, /* handler */
1798 0x05, 0x06, 0x07, 0x08, /* data */
1799 };
1800
1801 static const struct results results_0[] =
1802 {
1803 /* offset rbp handler rip frame registers */
1804 { 0x00, 0x40, FALSE, 0x000, 0x000, { {rsp,0x008}, {-1,-1} }},
1805 { 0x02, 0x40, FALSE, 0x008, 0x000, { {rsp,0x010}, {rbp,0x000}, {-1,-1} }},
1806 { 0x09, 0x40, FALSE, 0x118, 0x000, { {rsp,0x120}, {rbp,0x110}, {-1,-1} }},
1807 { 0x0e, 0x40, FALSE, 0x128, 0x010, { {rsp,0x130}, {rbp,0x120}, {-1,-1} }},
1808 { 0x15, 0x40, FALSE, 0x128, 0x010, { {rsp,0x130}, {rbp,0x120}, {rbx,0x130}, {-1,-1} }},
1809 { 0x1c, 0x40, TRUE, 0x128, 0x010, { {rsp,0x130}, {rbp,0x120}, {rbx,0x130}, {rsi,0x138}, {-1,-1}}},
1810 { 0x1d, 0x40, TRUE, 0x128, 0x010, { {rsp,0x130}, {rbp,0x120}, {rbx,0x130}, {rsi,0x138}, {-1,-1}}},
1811 { 0x24, 0x40, TRUE, 0x128, 0x010, { {rsp,0x130}, {rbp,0x120}, {rbx,0x130}, {rsi,0x138}, {-1,-1}}},
1812 { 0x2b, 0x40, FALSE, 0x128, 0x010, { {rsp,0x130}, {rbp,0x120}, {-1,-1}}},
1813 { 0x32, 0x40, FALSE, 0x008, 0x010, { {rsp,0x010}, {rbp,0x000}, {-1,-1}}},
1814 { 0x33, 0x40, FALSE, 0x000, 0x010, { {rsp,0x008}, {-1,-1}}},
1815 };
1816
1817
1818 static const BYTE function_1[] =
1819 {
1820 0x53, /* 00: push %rbx */
1821 0x55, /* 01: push %rbp */
1822 0x56, /* 02: push %rsi */
1823 0x57, /* 03: push %rdi */
1824 0x41, 0x54, /* 04: push %r12 */
1825 0x48, 0x83, 0xec, 0x30, /* 06: sub $0x30,%rsp */
1826 0x90, 0x90, /* 0a: nop; nop */
1827 0x48, 0x83, 0xc4, 0x30, /* 0c: add $0x30,%rsp */
1828 0x41, 0x5c, /* 10: pop %r12 */
1829 0x5f, /* 12: pop %rdi */
1830 0x5e, /* 13: pop %rsi */
1831 0x5d, /* 14: pop %rbp */
1832 0x5b, /* 15: pop %rbx */
1833 0xc3 /* 16: ret */
1834 };
1835
1836 static const BYTE unwind_info_1[] =
1837 {
1838 1 | (UNW_FLAG_EHANDLER << 3), /* version + flags */
1839 0x0a, /* prolog size */
1840 6, /* opcode count */
1841 0, /* frame reg */
1842
1843 0x0a, UWOP(ALLOC_SMALL, 5), /* 0a: sub $0x30,%rsp */
1844 0x06, UWOP(PUSH_NONVOL, r12), /* 06: push %r12 */
1845 0x04, UWOP(PUSH_NONVOL, rdi), /* 04: push %rdi */
1846 0x03, UWOP(PUSH_NONVOL, rsi), /* 03: push %rsi */
1847 0x02, UWOP(PUSH_NONVOL, rbp), /* 02: push %rbp */
1848 0x01, UWOP(PUSH_NONVOL, rbx), /* 01: push %rbx */
1849
1850 0x00, 0x02, 0x00, 0x00, /* handler */
1851 0x05, 0x06, 0x07, 0x08, /* data */
1852 };
1853
1854 static const struct results results_1[] =
1855 {
1856 /* offset rbp handler rip frame registers */
1857 { 0x00, 0x50, FALSE, 0x000, 0x000, { {rsp,0x008}, {-1,-1} }},
1858 { 0x01, 0x50, FALSE, 0x008, 0x000, { {rsp,0x010}, {rbx,0x000}, {-1,-1} }},
1859 { 0x02, 0x50, FALSE, 0x010, 0x000, { {rsp,0x018}, {rbx,0x008}, {rbp,0x000}, {-1,-1} }},
1860 { 0x03, 0x50, FALSE, 0x018, 0x000, { {rsp,0x020}, {rbx,0x010}, {rbp,0x008}, {rsi,0x000}, {-1,-1} }},
1861 { 0x04, 0x50, FALSE, 0x020, 0x000, { {rsp,0x028}, {rbx,0x018}, {rbp,0x010}, {rsi,0x008}, {rdi,0x000}, {-1,-1} }},
1862 { 0x06, 0x50, FALSE, 0x028, 0x000, { {rsp,0x030}, {rbx,0x020}, {rbp,0x018}, {rsi,0x010}, {rdi,0x008}, {r12,0x000}, {-1,-1} }},
1863 { 0x0a, 0x50, TRUE, 0x058, 0x000, { {rsp,0x060}, {rbx,0x050}, {rbp,0x048}, {rsi,0x040}, {rdi,0x038}, {r12,0x030}, {-1,-1} }},
1864 { 0x0c, 0x50, FALSE, 0x058, 0x000, { {rsp,0x060}, {rbx,0x050}, {rbp,0x048}, {rsi,0x040}, {rdi,0x038}, {r12,0x030}, {-1,-1} }},
1865 { 0x10, 0x50, FALSE, 0x028, 0x000, { {rsp,0x030}, {rbx,0x020}, {rbp,0x018}, {rsi,0x010}, {rdi,0x008}, {r12,0x000}, {-1,-1} }},
1866 { 0x12, 0x50, FALSE, 0x020, 0x000, { {rsp,0x028}, {rbx,0x018}, {rbp,0x010}, {rsi,0x008}, {rdi,0x000}, {-1,-1} }},
1867 { 0x13, 0x50, FALSE, 0x018, 0x000, { {rsp,0x020}, {rbx,0x010}, {rbp,0x008}, {rsi,0x000}, {-1,-1} }},
1868 { 0x14, 0x50, FALSE, 0x010, 0x000, { {rsp,0x018}, {rbx,0x008}, {rbp,0x000}, {-1,-1} }},
1869 { 0x15, 0x50, FALSE, 0x008, 0x000, { {rsp,0x010}, {rbx,0x000}, {-1,-1} }},
1870 { 0x16, 0x50, FALSE, 0x000, 0x000, { {rsp,0x008}, {-1,-1} }},
1871 };
1872
1873 static const struct unwind_test tests[] =
1874 {
1875 { function_0, sizeof(function_0), unwind_info_0,
1876 results_0, sizeof(results_0)/sizeof(results_0[0]) },
1877 { function_1, sizeof(function_1), unwind_info_1,
1878 results_1, sizeof(results_1)/sizeof(results_1[0]) }
1879 };
1880 unsigned int i;
1881
1882 for (i = 0; i < sizeof(tests)/sizeof(tests[0]); i++)
1883 call_virtual_unwind( i, &tests[i] );
1884}
1885
1886static int consolidate_dummy_called;
1887static PVOID CALLBACK test_consolidate_dummy(EXCEPTION_RECORD *rec)
1888{
1890 consolidate_dummy_called = 1;
1891 ok(ctx->Rip == 0xdeadbeef, "test_consolidate_dummy failed for Rip, expected: 0xdeadbeef, got: %lx\n", ctx->Rip);
1892 return (PVOID)rec->ExceptionInformation[2];
1893}
1894
1895static void test_restore_context(void)
1896{
1897 SETJMP_FLOAT128 *fltsave;
1898 EXCEPTION_RECORD rec;
1899 _JUMP_BUFFER buf;
1900 CONTEXT ctx;
1901 int i, pass;
1902
1903 if (!pRtlUnwindEx || !pRtlRestoreContext || !pRtlCaptureContext || !p_setjmp)
1904 {
1905 skip("RtlUnwindEx/RtlCaptureContext/RtlRestoreContext/_setjmp not found\n");
1906 return;
1907 }
1908
1909 /* RtlRestoreContext(NULL, NULL); crashes on Windows */
1910
1911 /* test simple case of capture and restore context */
1912 pass = 0;
1913 InterlockedIncrement(&pass); /* interlocked to prevent compiler from moving after capture */
1914 pRtlCaptureContext(&ctx);
1915 if (InterlockedIncrement(&pass) == 2) /* interlocked to prevent compiler from moving before capture */
1916 {
1917 pRtlRestoreContext(&ctx, NULL);
1918 ok(0, "shouldn't be reached\n");
1919 }
1920 else
1921 ok(pass < 4, "unexpected pass %d\n", pass);
1922
1923 /* test with jmp using RltRestoreContext */
1924 pass = 0;
1927 InterlockedIncrement(&pass); /* only called once */
1928 p_setjmp(&buf);
1930 if (pass == 3)
1931 {
1933 rec.NumberParameters = 1;
1934 rec.ExceptionInformation[0] = (DWORD64)&buf;
1935
1936 /* uses buf.Rip instead of ctx.Rip */
1937 pRtlRestoreContext(&ctx, &rec);
1938 ok(0, "shouldn't be reached\n");
1939 }
1940 else if (pass == 4)
1941 {
1942 ok(buf.Rbx == ctx.Rbx, "longjmp failed for Rbx, expected: %lx, got: %lx\n", buf.Rbx, ctx.Rbx);
1943 ok(buf.Rsp == ctx.Rsp, "longjmp failed for Rsp, expected: %lx, got: %lx\n", buf.Rsp, ctx.Rsp);
1944 ok(buf.Rbp == ctx.Rbp, "longjmp failed for Rbp, expected: %lx, got: %lx\n", buf.Rbp, ctx.Rbp);
1945 ok(buf.Rsi == ctx.Rsi, "longjmp failed for Rsi, expected: %lx, got: %lx\n", buf.Rsi, ctx.Rsi);
1946 ok(buf.Rdi == ctx.Rdi, "longjmp failed for Rdi, expected: %lx, got: %lx\n", buf.Rdi, ctx.Rdi);
1947 ok(buf.R12 == ctx.R12, "longjmp failed for R12, expected: %lx, got: %lx\n", buf.R12, ctx.R12);
1948 ok(buf.R13 == ctx.R13, "longjmp failed for R13, expected: %lx, got: %lx\n", buf.R13, ctx.R13);
1949 ok(buf.R14 == ctx.R14, "longjmp failed for R14, expected: %lx, got: %lx\n", buf.R14, ctx.R14);
1950 ok(buf.R15 == ctx.R15, "longjmp failed for R15, expected: %lx, got: %lx\n", buf.R15, ctx.R15);
1951
1952 fltsave = &buf.Xmm6;
1953 for (i = 0; i < 10; i++)
1954 {
1955 ok(fltsave[i].Part[0] == ctx.u.FltSave.XmmRegisters[i + 6].Low,
1956 "longjmp failed for Xmm%d, expected %lx, got %lx\n", i + 6,
1957 fltsave[i].Part[0], ctx.u.FltSave.XmmRegisters[i + 6].Low);
1958
1959 ok(fltsave[i].Part[1] == ctx.u.FltSave.XmmRegisters[i + 6].High,
1960 "longjmp failed for Xmm%d, expected %lx, got %lx\n", i + 6,
1961 fltsave[i].Part[1], ctx.u.FltSave.XmmRegisters[i + 6].High);
1962 }
1963 }
1964 else
1965 ok(0, "unexpected pass %d\n", pass);
1966
1967 /* test with jmp through RtlUnwindEx */
1968 pass = 0;
1970 pRtlCaptureContext(&ctx);
1971 InterlockedIncrement(&pass); /* only called once */
1972 p_setjmp(&buf);
1974 if (pass == 3)
1975 {
1977 rec.NumberParameters = 1;
1978 rec.ExceptionInformation[0] = (DWORD64)&buf;
1979
1980 /* uses buf.Rip instead of bogus 0xdeadbeef */
1981 pRtlUnwindEx((void*)buf.Rsp, (void*)0xdeadbeef, &rec, NULL, &ctx, NULL);
1982 ok(0, "shouldn't be reached\n");
1983 }
1984 else
1985 ok(pass == 4, "unexpected pass %d\n", pass);
1986
1987
1988 /* test with consolidate */
1989 pass = 0;
1993 if (pass == 2)
1994 {
1996 rec.NumberParameters = 3;
1997 rec.ExceptionInformation[0] = (DWORD64)test_consolidate_dummy;
1998 rec.ExceptionInformation[1] = (DWORD64)&ctx;
1999 rec.ExceptionInformation[2] = ctx.Rip;
2000 ctx.Rip = 0xdeadbeef;
2001
2002 pRtlRestoreContext(&ctx, &rec);
2003 ok(0, "shouldn't be reached\n");
2004 }
2005 else if (pass == 3)
2006 ok(consolidate_dummy_called, "test_consolidate_dummy not called\n");
2007 else
2008 ok(0, "unexpected pass %d\n", pass);
2009}
2010
2011static RUNTIME_FUNCTION* CALLBACK dynamic_unwind_callback( DWORD64 pc, PVOID context )
2012{
2013 static const int code_offset = 1024;
2014 static RUNTIME_FUNCTION runtime_func;
2015 (*(DWORD *)context)++;
2016
2017 runtime_func.BeginAddress = code_offset + 16;
2018 runtime_func.EndAddress = code_offset + 32;
2019 runtime_func.UnwindData = 0;
2020 return &runtime_func;
2021}
2022
2023static void test_dynamic_unwind(void)
2024{
2025 static const int code_offset = 1024;
2026 char buf[sizeof(RUNTIME_FUNCTION) + 4];
2027 RUNTIME_FUNCTION *runtime_func, *func;
2029 DWORD count;
2030
2031 /* Test RtlAddFunctionTable with aligned RUNTIME_FUNCTION pointer */
2032 runtime_func = (RUNTIME_FUNCTION *)buf;
2033 runtime_func->BeginAddress = code_offset;
2034 runtime_func->EndAddress = code_offset + 16;
2035 runtime_func->UnwindData = 0;
2036 ok( pRtlAddFunctionTable( runtime_func, 1, (ULONG_PTR)code_mem ),
2037 "RtlAddFunctionTable failed for runtime_func = %p (aligned)\n", runtime_func );
2038
2039 /* Lookup function outside of any function table */
2040 base = 0xdeadbeef;
2041 func = pRtlLookupFunctionEntry( (ULONG_PTR)code_mem + code_offset + 16, &base, NULL );
2042 ok( func == NULL,
2043 "RtlLookupFunctionEntry returned unexpected function, expected: NULL, got: %p\n", func );
2044 ok( !base || broken(base == 0xdeadbeef),
2045 "RtlLookupFunctionEntry modified base address, expected: 0, got: %lx\n", base );
2046
2047 /* Test with pointer inside of our function */
2048 base = 0xdeadbeef;
2049 func = pRtlLookupFunctionEntry( (ULONG_PTR)code_mem + code_offset + 8, &base, NULL );
2050 ok( func == runtime_func,
2051 "RtlLookupFunctionEntry didn't return expected function, expected: %p, got: %p\n", runtime_func, func );
2053 "RtlLookupFunctionEntry returned invalid base, expected: %lx, got: %lx\n", (ULONG_PTR)code_mem, base );
2054
2055 /* Test RtlDeleteFunctionTable */
2056 ok( pRtlDeleteFunctionTable( runtime_func ),
2057 "RtlDeleteFunctionTable failed for runtime_func = %p (aligned)\n", runtime_func );
2058 ok( !pRtlDeleteFunctionTable( runtime_func ),
2059 "RtlDeleteFunctionTable returned success for nonexistent table runtime_func = %p\n", runtime_func );
2060
2061 /* Unaligned RUNTIME_FUNCTION pointer */
2062 runtime_func = (RUNTIME_FUNCTION *)((ULONG_PTR)buf | 0x3);
2063 runtime_func->BeginAddress = code_offset;
2064 runtime_func->EndAddress = code_offset + 16;
2065 runtime_func->UnwindData = 0;
2066 ok( pRtlAddFunctionTable( runtime_func, 1, (ULONG_PTR)code_mem ),
2067 "RtlAddFunctionTable failed for runtime_func = %p (unaligned)\n", runtime_func );
2068 ok( pRtlDeleteFunctionTable( runtime_func ),
2069 "RtlDeleteFunctionTable failed for runtime_func = %p (unaligned)\n", runtime_func );
2070
2071 /* Attempt to insert the same entry twice */
2072 runtime_func = (RUNTIME_FUNCTION *)buf;
2073 runtime_func->BeginAddress = code_offset;
2074 runtime_func->EndAddress = code_offset + 16;
2075 runtime_func->UnwindData = 0;
2076 ok( pRtlAddFunctionTable( runtime_func, 1, (ULONG_PTR)code_mem ),
2077 "RtlAddFunctionTable failed for runtime_func = %p (first attempt)\n", runtime_func );
2078 ok( pRtlAddFunctionTable( runtime_func, 1, (ULONG_PTR)code_mem ),
2079 "RtlAddFunctionTable failed for runtime_func = %p (second attempt)\n", runtime_func );
2080 ok( pRtlDeleteFunctionTable( runtime_func ),
2081 "RtlDeleteFunctionTable failed for runtime_func = %p (first attempt)\n", runtime_func );
2082 ok( pRtlDeleteFunctionTable( runtime_func ),
2083 "RtlDeleteFunctionTable failed for runtime_func = %p (second attempt)\n", runtime_func );
2084 ok( !pRtlDeleteFunctionTable( runtime_func ),
2085 "RtlDeleteFunctionTable returned success for nonexistent table runtime_func = %p\n", runtime_func );
2086
2087 /* Test RtlInstallFunctionTableCallback with both low bits unset */
2089 ok( !pRtlInstallFunctionTableCallback( table, (ULONG_PTR)code_mem, code_offset + 32, &dynamic_unwind_callback, (PVOID*)&count, NULL ),
2090 "RtlInstallFunctionTableCallback returned success for table = %lx\n", table );
2091
2092 /* Test RtlInstallFunctionTableCallback with both low bits set */
2093 table = (ULONG_PTR)code_mem | 0x3;
2094 ok( pRtlInstallFunctionTableCallback( table, (ULONG_PTR)code_mem, code_offset + 32, &dynamic_unwind_callback, (PVOID*)&count, NULL ),
2095 "RtlInstallFunctionTableCallback failed for table = %lx\n", table );
2096
2097 /* Lookup function outside of any function table */
2098 count = 0;
2099 base = 0xdeadbeef;
2100 func = pRtlLookupFunctionEntry( (ULONG_PTR)code_mem + code_offset + 32, &base, NULL );
2101 ok( func == NULL,
2102 "RtlLookupFunctionEntry returned unexpected function, expected: NULL, got: %p\n", func );
2103 ok( !base || broken(base == 0xdeadbeef),
2104 "RtlLookupFunctionEntry modified base address, expected: 0, got: %lx\n", base );
2105 ok( !count,
2106 "RtlLookupFunctionEntry issued %d unexpected calls to dynamic_unwind_callback\n", count );
2107
2108 /* Test with pointer inside of our function */
2109 count = 0;
2110 base = 0xdeadbeef;
2111 func = pRtlLookupFunctionEntry( (ULONG_PTR)code_mem + code_offset + 24, &base, NULL );
2112 ok( func != NULL && func->BeginAddress == code_offset + 16 && func->EndAddress == code_offset + 32,
2113 "RtlLookupFunctionEntry didn't return expected function, got: %p\n", func );
2115 "RtlLookupFunctionEntry returned invalid base, expected: %lx, got: %lx\n", (ULONG_PTR)code_mem, base );
2116 ok( count == 1,
2117 "RtlLookupFunctionEntry issued %d calls to dynamic_unwind_callback, expected: 1\n", count );
2118
2119 /* Clean up again */
2120 ok( pRtlDeleteFunctionTable( (PRUNTIME_FUNCTION)table ),
2121 "RtlDeleteFunctionTable failed for table = %p\n", (PVOID)table );
2122 ok( !pRtlDeleteFunctionTable( (PRUNTIME_FUNCTION)table ),
2123 "RtlDeleteFunctionTable returned success for nonexistent table = %p\n", (PVOID)table );
2124
2125}
2126
2127static int termination_handler_called;
2128static void WINAPI termination_handler(ULONG flags, ULONG64 frame)
2129{
2130 termination_handler_called++;
2131
2132 ok(flags == 1 || broken(flags == 0x401), "flags = %x\n", flags);
2133 ok(frame == 0x1234, "frame = %p\n", (void*)frame);
2134}
2135
2136static void test___C_specific_handler(void)
2137{
2139 EXCEPTION_RECORD rec;
2141 ULONG64 frame;
2143 SCOPE_TABLE scope_table;
2144
2145 if (!p__C_specific_handler)
2146 {
2147 win_skip("__C_specific_handler not available\n");
2148 return;
2149 }
2150
2151 memset(&rec, 0, sizeof(rec));
2152 rec.ExceptionFlags = 2; /* EH_UNWINDING */
2153 frame = 0x1234;
2154 memset(&dispatch, 0, sizeof(dispatch));
2156 dispatch.ControlPc = dispatch.ImageBase + 0x200;
2157 dispatch.HandlerData = &scope_table;
2158 dispatch.ContextRecord = &context;
2159 scope_table.Count = 1;
2160 scope_table.ScopeRecord[0].BeginAddress = 0x200;
2161 scope_table.ScopeRecord[0].EndAddress = 0x400;
2162#ifndef __REACTOS__
2163 scope_table.ScopeRecord[0].HandlerAddress = (ULONG_PTR)termination_handler-dispatch.ImageBase;
2164#else
2165 scope_table.ScopeRecord[0].HandlerAddress = ((ULONG_PTR)termination_handler - (ULONG_PTR)dispatch.ImageBase);
2166#endif
2167 scope_table.ScopeRecord[0].JumpTarget = 0;
2168 memset(&context, 0, sizeof(context));
2169
2170 termination_handler_called = 0;
2171 ret = p__C_specific_handler(&rec, frame, &context, &dispatch);
2172 ok(ret == ExceptionContinueSearch, "__C_specific_handler returned %x\n", ret);
2173 ok(termination_handler_called == 1, "termination_handler_called = %d\n",
2174 termination_handler_called);
2175 ok(dispatch.ScopeIndex == 1, "dispatch.ScopeIndex = %d\n", dispatch.ScopeIndex);
2176
2177 ret = p__C_specific_handler(&rec, frame, &context, &dispatch);
2178 ok(ret == ExceptionContinueSearch, "__C_specific_handler returned %x\n", ret);
2179 ok(termination_handler_called == 1, "termination_handler_called = %d\n",
2180 termination_handler_called);
2181 ok(dispatch.ScopeIndex == 1, "dispatch.ScopeIndex = %d\n", dispatch.ScopeIndex);
2182}
2183
2184#endif /* __x86_64__ */
2185
2186#if defined(__i386__) || defined(__x86_64__)
2187
2188static DWORD WINAPI register_check_thread(void *arg)
2189{
2191 CONTEXT ctx;
2192
2193 memset(&ctx, 0, sizeof(ctx));
2194 ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
2195
2196 status = pNtGetContextThread(GetCurrentThread(), &ctx);
2197 ok(status == STATUS_SUCCESS, "NtGetContextThread failed with %x\n", status);
2198 ok(!ctx.Dr0, "expected 0, got %lx\n", (DWORD_PTR)ctx.Dr0);
2199 ok(!ctx.Dr1, "expected 0, got %lx\n", (DWORD_PTR)ctx.Dr1);
2200 ok(!ctx.Dr2, "expected 0, got %lx\n", (DWORD_PTR)ctx.Dr2);
2201 ok(!ctx.Dr3, "expected 0, got %lx\n", (DWORD_PTR)ctx.Dr3);
2202 ok(!ctx.Dr6, "expected 0, got %lx\n", (DWORD_PTR)ctx.Dr6);
2203 ok(!ctx.Dr7, "expected 0, got %lx\n", (DWORD_PTR)ctx.Dr7);
2204
2205 return 0;
2206}
2207
2208static void test_debug_registers(void)
2209{
2210 static const struct
2211 {
2212 ULONG_PTR dr0, dr1, dr2, dr3, dr6, dr7;
2213 }
2214 tests[] =
2215 {
2216 { 0x42424240, 0, 0x126bb070, 0x0badbad0, 0, 0xffff0115 },
2217 { 0x42424242, 0, 0x100f0fe7, 0x0abebabe, 0, 0x115 },
2218 };
2220 CONTEXT ctx;
2221 HANDLE thread;
2222 int i;
2223
2224 for (i = 0; i < sizeof(tests)/sizeof(tests[0]); i++)
2225 {
2226 memset(&ctx, 0, sizeof(ctx));
2227 ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
2228 ctx.Dr0 = tests[i].dr0;
2229 ctx.Dr1 = tests[i].dr1;
2230 ctx.Dr2 = tests[i].dr2;
2231 ctx.Dr3 = tests[i].dr3;
2232 ctx.Dr6 = tests[i].dr6;
2233 ctx.Dr7 = tests[i].dr7;
2234
2235 status = pNtSetContextThread(GetCurrentThread(), &ctx);
2236 ok(status == STATUS_SUCCESS, "NtGetContextThread failed with %08x\n", status);
2237
2238 memset(&ctx, 0, sizeof(ctx));
2239 ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
2240
2241 status = pNtGetContextThread(GetCurrentThread(), &ctx);
2242 ok(status == STATUS_SUCCESS, "NtGetContextThread failed with %08x\n", status);
2243 ok(ctx.Dr0 == tests[i].dr0, "test %d: expected %lx, got %lx\n", i, tests[i].dr0, (DWORD_PTR)ctx.Dr0);
2244 ok(ctx.Dr1 == tests[i].dr1, "test %d: expected %lx, got %lx\n", i, tests[i].dr1, (DWORD_PTR)ctx.Dr1);
2245 ok(ctx.Dr2 == tests[i].dr2, "test %d: expected %lx, got %lx\n", i, tests[i].dr2, (DWORD_PTR)ctx.Dr2);
2246 ok(ctx.Dr3 == tests[i].dr3, "test %d: expected %lx, got %lx\n", i, tests[i].dr3, (DWORD_PTR)ctx.Dr3);
2247 ok((ctx.Dr6 & 0xf00f) == tests[i].dr6, "test %d: expected %lx, got %lx\n", i, tests[i].dr6, (DWORD_PTR)ctx.Dr6);
2248 ok((ctx.Dr7 & ~0xdc00) == tests[i].dr7, "test %d: expected %lx, got %lx\n", i, tests[i].dr7, (DWORD_PTR)ctx.Dr7);
2249 }
2250
2251 memset(&ctx, 0, sizeof(ctx));
2252 ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
2253 ctx.Dr0 = 0xffffffff;
2254 ctx.Dr1 = 0xffffffff;
2255 ctx.Dr2 = 0xffffffff;
2256 ctx.Dr3 = 0xffffffff;
2257 ctx.Dr6 = 0xffffffff;
2258 ctx.Dr7 = 0x00000400;
2259 status = pNtSetContextThread(GetCurrentThread(), &ctx);
2260 ok(status == STATUS_SUCCESS, "NtSetContextThread failed with %x\n", status);
2261
2262 thread = CreateThread(NULL, 0, register_check_thread, NULL, CREATE_SUSPENDED, NULL);
2263 ok(thread != INVALID_HANDLE_VALUE, "CreateThread failed with %d\n", GetLastError());
2264
2265 ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
2266 status = pNtGetContextThread(thread, &ctx);
2267 ok(status == STATUS_SUCCESS, "NtGetContextThread failed with %x\n", status);
2268 ok(!ctx.Dr0, "expected 0, got %lx\n", (DWORD_PTR)ctx.Dr0);
2269 ok(!ctx.Dr1, "expected 0, got %lx\n", (DWORD_PTR)ctx.Dr1);
2270 ok(!ctx.Dr2, "expected 0, got %lx\n", (DWORD_PTR)ctx.Dr2);
2271 ok(!ctx.Dr3, "expected 0, got %lx\n", (DWORD_PTR)ctx.Dr3);
2272 ok(!ctx.Dr6, "expected 0, got %lx\n", (DWORD_PTR)ctx.Dr6);
2273 ok(!ctx.Dr7, "expected 0, got %lx\n", (DWORD_PTR)ctx.Dr7);
2274
2278}
2279
2280static DWORD outputdebugstring_exceptions;
2281
2282static LONG CALLBACK outputdebugstring_vectored_handler(EXCEPTION_POINTERS *ExceptionInfo)
2283{
2284 PEXCEPTION_RECORD rec = ExceptionInfo->ExceptionRecord;
2285 trace("vect. handler %08x addr:%p\n", rec->ExceptionCode, rec->ExceptionAddress);
2286
2287 ok(rec->ExceptionCode == DBG_PRINTEXCEPTION_C, "ExceptionCode is %08x instead of %08x\n",
2289 ok(rec->NumberParameters == 2, "ExceptionParameters is %d instead of 2\n", rec->NumberParameters);
2290 ok(rec->ExceptionInformation[0] == 12, "ExceptionInformation[0] = %d instead of 12\n", (DWORD)rec->ExceptionInformation[0]);
2291 ok(!strcmp((char *)rec->ExceptionInformation[1], "Hello World"),
2292 "ExceptionInformation[1] = '%s' instead of 'Hello World'\n", (char *)rec->ExceptionInformation[1]);
2293
2294 outputdebugstring_exceptions++;
2296}
2297
2298static void test_outputdebugstring(DWORD numexc)
2299{
2300 PVOID vectored_handler;
2301
2302 if (!pRtlAddVectoredExceptionHandler || !pRtlRemoveVectoredExceptionHandler)
2303 {
2304 skip("RtlAddVectoredExceptionHandler or RtlRemoveVectoredExceptionHandler not found\n");
2305 return;
2306 }
2307
2308 vectored_handler = pRtlAddVectoredExceptionHandler(TRUE, &outputdebugstring_vectored_handler);
2309 ok(vectored_handler != 0, "RtlAddVectoredExceptionHandler failed\n");
2310
2311 outputdebugstring_exceptions = 0;
2312 OutputDebugStringA("Hello World");
2313
2314 ok(outputdebugstring_exceptions == numexc, "OutputDebugStringA generated %d exceptions, expected %d\n",
2315 outputdebugstring_exceptions, numexc);
2316
2317 pRtlRemoveVectoredExceptionHandler(vectored_handler);
2318}
2319
2320static DWORD ripevent_exceptions;
2321
2322static LONG CALLBACK ripevent_vectored_handler(EXCEPTION_POINTERS *ExceptionInfo)
2323{
2324 PEXCEPTION_RECORD rec = ExceptionInfo->ExceptionRecord;
2325 trace("vect. handler %08x addr:%p\n", rec->ExceptionCode, rec->ExceptionAddress);
2326
2327 ok(rec->ExceptionCode == DBG_RIPEXCEPTION, "ExceptionCode is %08x instead of %08x\n",
2329 ok(rec->NumberParameters == 2, "ExceptionParameters is %d instead of 2\n", rec->NumberParameters);
2330 ok(rec->ExceptionInformation[0] == 0x11223344, "ExceptionInformation[0] = %08x instead of %08x\n",
2331 (NTSTATUS)rec->ExceptionInformation[0], 0x11223344);
2332 ok(rec->ExceptionInformation[1] == 0x55667788, "ExceptionInformation[1] = %08x instead of %08x\n",
2333 (NTSTATUS)rec->ExceptionInformation[1], 0x55667788);
2334
2335 ripevent_exceptions++;
2337}
2338
2339static void test_ripevent(DWORD numexc)
2340{
2342 PVOID vectored_handler;
2343
2344 if (!pRtlAddVectoredExceptionHandler || !pRtlRemoveVectoredExceptionHandler || !pRtlRaiseException)
2345 {
2346 skip("RtlAddVectoredExceptionHandler or RtlRemoveVectoredExceptionHandler or RtlRaiseException not found\n");
2347 return;
2348 }
2349
2350 vectored_handler = pRtlAddVectoredExceptionHandler(TRUE, &ripevent_vectored_handler);
2351 ok(vectored_handler != 0, "RtlAddVectoredExceptionHandler failed\n");
2352
2353 record.ExceptionCode = DBG_RIPEXCEPTION;
2354 record.ExceptionFlags = 0;
2355 record.ExceptionRecord = NULL;
2356 record.ExceptionAddress = NULL;
2357 record.NumberParameters = 2;
2358 record.ExceptionInformation[0] = 0x11223344;
2359 record.ExceptionInformation[1] = 0x55667788;
2360
2361 ripevent_exceptions = 0;
2362 pRtlRaiseException(&record);
2363 ok(ripevent_exceptions == numexc, "RtlRaiseException generated %d exceptions, expected %d\n",
2364 ripevent_exceptions, numexc);
2365
2366 pRtlRemoveVectoredExceptionHandler(vectored_handler);
2367}
2368
2369static DWORD debug_service_exceptions;
2370
2371static LONG CALLBACK debug_service_handler(EXCEPTION_POINTERS *ExceptionInfo)
2372{
2373 EXCEPTION_RECORD *rec = ExceptionInfo->ExceptionRecord;
2374 trace("vect. handler %08x addr:%p\n", rec->ExceptionCode, rec->ExceptionAddress);
2375
2376 ok(rec->ExceptionCode == EXCEPTION_BREAKPOINT, "ExceptionCode is %08x instead of %08x\n",
2378
2379#ifdef __i386__
2380 ok(ExceptionInfo->ContextRecord->Eip == (DWORD)code_mem + 0x1c,
2381 "expected Eip = %x, got %x\n", (DWORD)code_mem + 0x1c, ExceptionInfo->ContextRecord->Eip);
2382 ok(rec->NumberParameters == (is_wow64 ? 1 : 3),
2383 "ExceptionParameters is %d instead of %d\n", rec->NumberParameters, is_wow64 ? 1 : 3);
2384 ok(rec->ExceptionInformation[0] == ExceptionInfo->ContextRecord->Eax,
2385 "expected ExceptionInformation[0] = %x, got %lx\n",
2386 ExceptionInfo->ContextRecord->Eax, rec->ExceptionInformation[0]);
2387 if (!is_wow64)
2388 {
2389 ok(rec->ExceptionInformation[1] == 0x11111111,
2390 "got ExceptionInformation[1] = %lx\n", rec->ExceptionInformation[1]);
2391 ok(rec->ExceptionInformation[2] == 0x22222222,
2392 "got ExceptionInformation[2] = %lx\n", rec->ExceptionInformation[2]);
2393 }
2394#else
2395 ok(ExceptionInfo->ContextRecord->Rip == (DWORD_PTR)code_mem + 0x2f,
2396 "expected Rip = %lx, got %lx\n", (DWORD_PTR)code_mem + 0x2f, ExceptionInfo->ContextRecord->Rip);
2397 ok(rec->NumberParameters == 1,
2398 "ExceptionParameters is %d instead of 1\n", rec->NumberParameters);
2399 ok(rec->ExceptionInformation[0] == ExceptionInfo->ContextRecord->Rax,
2400 "expected ExceptionInformation[0] = %lx, got %lx\n",
2401 ExceptionInfo->ContextRecord->Rax, rec->ExceptionInformation[0]);
2402#endif
2403
2404 debug_service_exceptions++;
2406}
2407
2408#ifdef __i386__
2409
2410static const BYTE call_debug_service_code[] = {
2411 0x53, /* pushl %ebx */
2412 0x57, /* pushl %edi */
2413 0x8b, 0x44, 0x24, 0x0c, /* movl 12(%esp),%eax */
2414 0xb9, 0x11, 0x11, 0x11, 0x11, /* movl $0x11111111,%ecx */
2415 0xba, 0x22, 0x22, 0x22, 0x22, /* movl $0x22222222,%edx */
2416 0xbb, 0x33, 0x33, 0x33, 0x33, /* movl $0x33333333,%ebx */
2417 0xbf, 0x44, 0x44, 0x44, 0x44, /* movl $0x44444444,%edi */
2418 0xcd, 0x2d, /* int $0x2d */
2419 0xeb, /* jmp $+17 */
2420 0x0f, 0x1f, 0x00, /* nop */
2421 0x31, 0xc0, /* xorl %eax,%eax */
2422 0xeb, 0x0c, /* jmp $+14 */
2423 0x90, 0x90, 0x90, 0x90, /* nop */
2424 0x90, 0x90, 0x90, 0x90,
2425 0x90,
2426 0x31, 0xc0, /* xorl %eax,%eax */
2427 0x40, /* incl %eax */
2428 0x5f, /* popl %edi */
2429 0x5b, /* popl %ebx */
2430 0xc3, /* ret */
2431};
2432
2433#else
2434
2435static const BYTE call_debug_service_code[] = {
2436 0x53, /* push %rbx */
2437 0x57, /* push %rdi */
2438 0x48, 0x89, 0xc8, /* movl %rcx,%rax */
2439 0x48, 0xb9, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, /* movabs $0x1111111111111111,%rcx */
2440 0x48, 0xba, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, /* movabs $0x2222222222222222,%rdx */
2441 0x48, 0xbb, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, /* movabs $0x3333333333333333,%rbx */
2442 0x48, 0xbf, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, /* movabs $0x4444444444444444,%rdi */
2443 0xcd, 0x2d, /* int $0x2d */
2444 0xeb, /* jmp $+17 */
2445 0x0f, 0x1f, 0x00, /* nop */
2446 0x48, 0x31, 0xc0, /* xor %rax,%rax */
2447 0xeb, 0x0e, /* jmp $+16 */
2448 0x90, 0x90, 0x90, 0x90, /* nop */
2449 0x90, 0x90, 0x90, 0x90,
2450 0x48, 0x31, 0xc0, /* xor %rax,%rax */
2451 0x48, 0xff, 0xc0, /* inc %rax */
2452 0x5f, /* pop %rdi */
2453 0x5b, /* pop %rbx */
2454 0xc3, /* ret */
2455};
2456
2457#endif
2458
2459static void test_debug_service(DWORD numexc)
2460{
2462 DWORD expected_exc, expected_ret;
2463 void *vectored_handler;
2464 DWORD ret;
2465
2466 /* code will return 0 if execution resumes immediately after "int $0x2d", otherwise 1 */
2467 memcpy(code_mem, call_debug_service_code, sizeof(call_debug_service_code));
2468
2469 vectored_handler = pRtlAddVectoredExceptionHandler(TRUE, &debug_service_handler);
2470 ok(vectored_handler != 0, "RtlAddVectoredExceptionHandler failed\n");
2471
2472 expected_exc = numexc;
2473 expected_ret = (numexc != 0);
2474
2475 /* BREAKPOINT_BREAK */
2476 debug_service_exceptions = 0;
2477 ret = func(0);
2478 ok(debug_service_exceptions == expected_exc,
2479 "BREAKPOINT_BREAK generated %u exceptions, expected %u\n",
2480 debug_service_exceptions, expected_exc);
2481 ok(ret == expected_ret,
2482 "BREAKPOINT_BREAK returned %u, expected %u\n", ret, expected_ret);
2483
2484 /* BREAKPOINT_PROMPT */
2485 debug_service_exceptions = 0;
2486 ret = func(2);
2487 ok(debug_service_exceptions == expected_exc,
2488 "BREAKPOINT_PROMPT generated %u exceptions, expected %u\n",
2489 debug_service_exceptions, expected_exc);
2490 ok(ret == expected_ret,
2491 "BREAKPOINT_PROMPT returned %u, expected %u\n", ret, expected_ret);
2492
2493 /* invalid debug service */
2494 debug_service_exceptions = 0;
2495 ret = func(6);
2496 ok(debug_service_exceptions == expected_exc,
2497 "invalid debug service generated %u exceptions, expected %u\n",
2498 debug_service_exceptions, expected_exc);
2499 ok(ret == expected_ret,
2500 "invalid debug service returned %u, expected %u\n", ret, expected_ret);
2501
2502 expected_exc = (is_wow64 ? numexc : 0);
2503 expected_ret = (is_wow64 && numexc);
2504
2505 /* BREAKPOINT_PRINT */
2506 debug_service_exceptions = 0;
2507 ret = func(1);
2508 ok(debug_service_exceptions == expected_exc,
2509 "BREAKPOINT_PRINT generated %u exceptions, expected %u\n",
2510 debug_service_exceptions, expected_exc);
2511 ok(ret == expected_ret,
2512 "BREAKPOINT_PRINT returned %u, expected %u\n", ret, expected_ret);
2513
2514 /* BREAKPOINT_LOAD_SYMBOLS */
2515 debug_service_exceptions = 0;
2516 ret = func(3);
2517 ok(debug_service_exceptions == expected_exc,
2518 "BREAKPOINT_LOAD_SYMBOLS generated %u exceptions, expected %u\n",
2519 debug_service_exceptions, expected_exc);
2520 ok(ret == expected_ret,
2521 "BREAKPOINT_LOAD_SYMBOLS returned %u, expected %u\n", ret, expected_ret);
2522
2523 /* BREAKPOINT_UNLOAD_SYMBOLS */
2524 debug_service_exceptions = 0;
2525 ret = func(4);
2526 ok(debug_service_exceptions == expected_exc,
2527 "BREAKPOINT_UNLOAD_SYMBOLS generated %u exceptions, expected %u\n",
2528 debug_service_exceptions, expected_exc);
2529 ok(ret == expected_ret,
2530 "BREAKPOINT_UNLOAD_SYMBOLS returned %u, expected %u\n", ret, expected_ret);
2531
2532 /* BREAKPOINT_COMMAND_STRING */
2533 debug_service_exceptions = 0;
2534 ret = func(5);
2535 ok(debug_service_exceptions == expected_exc || broken(debug_service_exceptions == numexc),
2536 "BREAKPOINT_COMMAND_STRING generated %u exceptions, expected %u\n",
2537 debug_service_exceptions, expected_exc);
2538 ok(ret == expected_ret || broken(ret == (numexc != 0)),
2539 "BREAKPOINT_COMMAND_STRING returned %u, expected %u\n", ret, expected_ret);
2540
2541 pRtlRemoveVectoredExceptionHandler(vectored_handler);
2542}
2543
2544static DWORD breakpoint_exceptions;
2545
2546static LONG CALLBACK breakpoint_handler(EXCEPTION_POINTERS *ExceptionInfo)
2547{
2548 EXCEPTION_RECORD *rec = ExceptionInfo->ExceptionRecord;
2549 trace("vect. handler %08x addr:%p\n", rec->ExceptionCode, rec->ExceptionAddress);
2550
2551 ok(rec->ExceptionCode == EXCEPTION_BREAKPOINT, "ExceptionCode is %08x instead of %08x\n",
2553
2554#ifdef __i386__
2555 ok(ExceptionInfo->ContextRecord->Eip == (DWORD)code_mem + 1,
2556 "expected Eip = %x, got %x\n", (DWORD)code_mem + 1, ExceptionInfo->ContextRecord->Eip);
2557 ok(rec->NumberParameters == (is_wow64 ? 1 : 3),
2558 "ExceptionParameters is %d instead of %d\n", rec->NumberParameters, is_wow64 ? 1 : 3);
2559 ok(rec->ExceptionInformation[0] == 0,
2560 "got ExceptionInformation[0] = %lx\n", rec->ExceptionInformation[0]);
2561 ExceptionInfo->ContextRecord->Eip = (DWORD)code_mem + 2;
2562#else
2563 ok(ExceptionInfo->ContextRecord->Rip == (DWORD_PTR)code_mem + 1,
2564 "expected Rip = %lx, got %lx\n", (DWORD_PTR)code_mem + 1, ExceptionInfo->ContextRecord->Rip);
2565 ok(rec->NumberParameters == 1,
2566 "ExceptionParameters is %d instead of 1\n", rec->NumberParameters);
2567 ok(rec->ExceptionInformation[0] == 0,
2568 "got ExceptionInformation[0] = %lx\n", rec->ExceptionInformation[0]);
2569 ExceptionInfo->ContextRecord->Rip = (DWORD_PTR)code_mem + 2;
2570#endif
2571
2572 breakpoint_exceptions++;
2574}
2575
2576static const BYTE breakpoint_code[] = {
2577 0xcd, 0x03, /* int $0x3 */
2578 0xc3, /* ret */
2579};
2580
2581static void test_breakpoint(DWORD numexc)
2582{
2584 void *vectored_handler;
2585
2586 memcpy(code_mem, breakpoint_code, sizeof(breakpoint_code));
2587
2588 vectored_handler = pRtlAddVectoredExceptionHandler(TRUE, &breakpoint_handler);
2589 ok(vectored_handler != 0, "RtlAddVectoredExceptionHandler failed\n");
2590
2591 breakpoint_exceptions = 0;
2592 func();
2593 ok(breakpoint_exceptions == numexc, "int $0x3 generated %u exceptions, expected %u\n",
2594 breakpoint_exceptions, numexc);
2595
2596 pRtlRemoveVectoredExceptionHandler(vectored_handler);
2597}
2598
2599static DWORD invalid_handle_exceptions;
2600
2601static LONG CALLBACK invalid_handle_vectored_handler(EXCEPTION_POINTERS *ExceptionInfo)
2602{
2603 PEXCEPTION_RECORD rec = ExceptionInfo->ExceptionRecord;
2604 trace("vect. handler %08x addr:%p\n", rec->ExceptionCode, rec->ExceptionAddress);
2605
2606 ok(rec->ExceptionCode == EXCEPTION_INVALID_HANDLE, "ExceptionCode is %08x instead of %08x\n",
2608 ok(rec->NumberParameters == 0, "ExceptionParameters is %d instead of 0\n", rec->NumberParameters);
2609
2610 invalid_handle_exceptions++;
2612}
2613
2614static void test_closehandle(DWORD numexc)
2615{
2616 PVOID vectored_handler;
2618 DWORD res;
2619
2620 if (!pRtlAddVectoredExceptionHandler || !pRtlRemoveVectoredExceptionHandler || !pRtlRaiseException)
2621 {
2622 skip("RtlAddVectoredExceptionHandler or RtlRemoveVectoredExceptionHandler or RtlRaiseException not found\n");
2623 return;
2624 }
2625
2626 vectored_handler = pRtlAddVectoredExceptionHandler(TRUE, &invalid_handle_vectored_handler);
2627 ok(vectored_handler != 0, "RtlAddVectoredExceptionHandler failed\n");
2628
2629 invalid_handle_exceptions = 0;
2630 res = CloseHandle((HANDLE)0xdeadbeef);
2631 ok(!res, "CloseHandle(0xdeadbeef) unexpectedly succeeded\n");
2632 ok(GetLastError() == ERROR_INVALID_HANDLE, "wrong error code %d instead of %d\n",
2634 ok(invalid_handle_exceptions == numexc, "CloseHandle generated %d exceptions, expected %d\n",
2635 invalid_handle_exceptions, numexc);
2636
2637 invalid_handle_exceptions = 0;
2638 status = pNtClose((HANDLE)0xdeadbeef);
2639 ok(status == STATUS_INVALID_HANDLE, "NtClose(0xdeadbeef) returned status %08x\n", status);
2640 ok(invalid_handle_exceptions == numexc, "NtClose generated %d exceptions, expected %d\n",
2641 invalid_handle_exceptions, numexc);
2642
2643 pRtlRemoveVectoredExceptionHandler(vectored_handler);
2644}
2645
2646static void test_vectored_continue_handler(void)
2647{
2648 PVOID handler1, handler2;
2649 ULONG ret;
2650
2651 if (!pRtlAddVectoredContinueHandler || !pRtlRemoveVectoredContinueHandler)
2652 {
2653 skip("RtlAddVectoredContinueHandler or RtlRemoveVectoredContinueHandler not found\n");
2654 return;
2655 }
2656
2657 handler1 = pRtlAddVectoredContinueHandler(TRUE, (void *)0xdeadbeef);
2658 ok(handler1 != 0, "RtlAddVectoredContinueHandler failed\n");
2659
2660 handler2 = pRtlAddVectoredContinueHandler(TRUE, (void *)0xdeadbeef);
2661 ok(handler2 != 0, "RtlAddVectoredContinueHandler failed\n");
2662 ok(handler1 != handler2, "RtlAddVectoredContinueHandler returned same handler\n");
2663
2664 if (pRtlRemoveVectoredExceptionHandler)
2665 {
2666 ret = pRtlRemoveVectoredExceptionHandler(handler1);
2667 ok(!ret, "RtlRemoveVectoredExceptionHandler succeeded\n");
2668 }
2669
2670 ret = pRtlRemoveVectoredContinueHandler(handler1);
2671 ok(ret, "RtlRemoveVectoredContinueHandler failed\n");
2672
2673 ret = pRtlRemoveVectoredContinueHandler(handler2);
2674 ok(ret, "RtlRemoveVectoredContinueHandler failed\n");
2675
2676 ret = pRtlRemoveVectoredContinueHandler(handler1);
2677 ok(!ret, "RtlRemoveVectoredContinueHandler succeeded\n");
2678
2679 ret = pRtlRemoveVectoredContinueHandler((void *)0x11223344);
2680 ok(!ret, "RtlRemoveVectoredContinueHandler succeeded\n");
2681}
2682#endif /* defined(__i386__) || defined(__x86_64__) */
2683
2685{
2686 HMODULE hntdll = GetModuleHandleA("ntdll.dll");
2687#if defined(__x86_64__)
2688 HMODULE hmsvcrt = LoadLibraryA("msvcrt.dll");
2689#endif
2690
2691#if defined(__REACTOS__) && !defined(_M_AMD64)
2692 if (!winetest_interactive &&
2693 !strcmp(winetest_platform, "windows"))
2694 {
2695 skip("ROSTESTS-240: Skipping ntdll_winetest:exception because it hangs on WHS-Testbot. Set winetest_interactive to run it anyway.\n");
2696 return;
2697 }
2698#endif
2700 if(!code_mem) {
2701 trace("VirtualAlloc failed\n");
2702 return;
2703 }
2704
2705 pNtGetContextThread = (void *)GetProcAddress( hntdll, "NtGetContextThread" );
2706 pNtSetContextThread = (void *)GetProcAddress( hntdll, "NtSetContextThread" );
2707 pNtReadVirtualMemory = (void *)GetProcAddress( hntdll, "NtReadVirtualMemory" );
2708 pNtClose = (void *)GetProcAddress( hntdll, "NtClose" );
2709 pRtlUnwind = (void *)GetProcAddress( hntdll, "RtlUnwind" );
2710 pRtlRaiseException = (void *)GetProcAddress( hntdll, "RtlRaiseException" );
2711 pRtlCaptureContext = (void *)GetProcAddress( hntdll, "RtlCaptureContext" );
2712 pNtTerminateProcess = (void *)GetProcAddress( hntdll, "NtTerminateProcess" );
2713 pRtlAddVectoredExceptionHandler = (void *)GetProcAddress( hntdll,
2714 "RtlAddVectoredExceptionHandler" );
2715 pRtlRemoveVectoredExceptionHandler = (void *)GetProcAddress( hntdll,
2716 "RtlRemoveVectoredExceptionHandler" );
2717 pRtlAddVectoredContinueHandler = (void *)GetProcAddress( hntdll,
2718 "RtlAddVectoredContinueHandler" );
2719 pRtlRemoveVectoredContinueHandler = (void *)GetProcAddress( hntdll,
2720 "RtlRemoveVectoredContinueHandler" );
2721 pNtQueryInformationProcess = (void*)GetProcAddress( hntdll,
2722 "NtQueryInformationProcess" );
2723 pNtSetInformationProcess = (void*)GetProcAddress( hntdll,
2724 "NtSetInformationProcess" );
2725 pIsWow64Process = (void *)GetProcAddress(GetModuleHandleA("kernel32.dll"), "IsWow64Process");
2726
2727#ifdef __i386__
2728 if (!pIsWow64Process || !pIsWow64Process( GetCurrentProcess(), &is_wow64 )) is_wow64 = FALSE;
2729
2730 if (pRtlAddVectoredExceptionHandler && pRtlRemoveVectoredExceptionHandler)
2731 have_vectored_api = TRUE;
2732 else
2733 skip("RtlAddVectoredExceptionHandler or RtlRemoveVectoredExceptionHandler not found\n");
2734
2735 my_argc = winetest_get_mainargs( &my_argv );
2736 if (my_argc >= 4)
2737 {
2738 void *addr;
2739 sscanf( my_argv[3], "%p", &addr );
2740
2741 if (addr != &test_stage)
2742 {
2743 skip( "child process not mapped at same address (%p/%p)\n", &test_stage, addr);
2744 return;
2745 }
2746
2747 /* child must be run under a debugger */
2749 {
2750 ok(FALSE, "child process not being debugged?\n");
2751 return;
2752 }
2753
2754 if (pRtlRaiseException)
2755 {
2756 test_stage = 1;
2757 run_rtlraiseexception_test(0x12345);
2758 run_rtlraiseexception_test(EXCEPTION_BREAKPOINT);
2759 run_rtlraiseexception_test(EXCEPTION_INVALID_HANDLE);
2760 test_stage = 2;
2761 run_rtlraiseexception_test(0x12345);
2762 run_rtlraiseexception_test(EXCEPTION_BREAKPOINT);
2763 run_rtlraiseexception_test(EXCEPTION_INVALID_HANDLE);
2764 test_stage = 3;
2765 test_outputdebugstring(0);
2766 test_stage = 4;
2767 test_outputdebugstring(2);
2768 test_stage = 5;
2769 test_ripevent(0);
2770 test_stage = 6;
2771 test_ripevent(1);
2772 test_stage = 7;
2773 test_debug_service(0);
2774 test_stage = 8;
2775 test_debug_service(1);
2776 test_stage = 9;
2777 test_breakpoint(0);
2778 test_stage = 10;
2779 test_breakpoint(1);
2780 test_stage = 11;
2781 test_closehandle(0);
2782 test_stage = 12;
2783 test_closehandle(1);
2784 }
2785 else
2786 skip( "RtlRaiseException not found\n" );
2787
2788 /* rest of tests only run in parent */
2789 return;
2790 }
2791
2792 test_unwind();
2793 test_exceptions();
2794 test_rtlraiseexception();
2795 test_debug_registers();
2796 test_outputdebugstring(1);
2797 test_ripevent(1);
2798 test_debug_service(1);
2799 test_breakpoint(1);
2800 test_closehandle(0);
2801 test_vectored_continue_handler();
2802 test_debugger();
2803 test_simd_exceptions();
2804 test_fpu_exceptions();
2805 test_dpe_exceptions();
2806 test_prot_fault();
2807 test_thread_context();
2808
2809#elif defined(__x86_64__)
2810 pRtlAddFunctionTable = (void *)GetProcAddress( hntdll,
2811 "RtlAddFunctionTable" );
2812 pRtlDeleteFunctionTable = (void *)GetProcAddress( hntdll,
2813 "RtlDeleteFunctionTable" );
2814 pRtlInstallFunctionTableCallback = (void *)GetProcAddress( hntdll,
2815 "RtlInstallFunctionTableCallback" );
2816 pRtlLookupFunctionEntry = (void *)GetProcAddress( hntdll,
2817 "RtlLookupFunctionEntry" );
2818 p__C_specific_handler = (void *)GetProcAddress( hntdll,
2819 "__C_specific_handler" );
2820 pRtlCaptureContext = (void *)GetProcAddress( hntdll,
2821 "RtlCaptureContext" );
2822 pRtlRestoreContext = (void *)GetProcAddress( hntdll,
2823 "RtlRestoreContext" );
2824 pRtlUnwindEx = (void *)GetProcAddress( hntdll,
2825 "RtlUnwindEx" );
2826 p_setjmp = (void *)GetProcAddress( hmsvcrt,
2827 "_setjmp" );
2828
2829 test_debug_registers();
2830 test_outputdebugstring(1);
2831 test_ripevent(1);
2832 test_debug_service(1);
2833 test_breakpoint(1);
2834 test_closehandle(0);
2835 test_vectored_continue_handler();
2836 test_virtual_unwind();
2837 test___C_specific_handler();
2838 test_restore_context();
2839
2840 if (pRtlAddFunctionTable && pRtlDeleteFunctionTable && pRtlInstallFunctionTableCallback && pRtlLookupFunctionEntry)
2841 test_dynamic_unwind();
2842 else
2843 skip( "Dynamic unwind functions not found\n" );
2844
2845#endif
2846
2848}
static struct _test_info results[8]
Definition: SetCursorPos.c:31
#define expect(EXPECTED, GOT)
Definition: SystemMenu.c:483
#define broken(x)
Definition: _sntprintf.h:21
int strcmp(const char *String1, const char *String2)
Definition: utclib.c:469
char * strstr(char *String1, char *String2)
Definition: utclib.c:653
#define VOID
Definition: acefi.h:82
#define stat
Definition: acwin.h:99
@ R15
Definition: amd64_sup.c:13
@ R13
Definition: amd64_sup.c:13
@ R12
Definition: amd64_sup.c:13
@ R14
Definition: amd64_sup.c:13
#define InterlockedIncrement
Definition: armddk.h:53
#define trace
Definition: atltest.h:70
#define ok(value,...)
Definition: atltest.h:57
#define skip(...)
Definition: atltest.h:64
#define START_TEST(x)
Definition: atltest.h:75
LONG NTSTATUS
Definition: precomp.h:26
void dispatch(HANDLE hStopEvent)
Definition: dispatch.c:70
static HANDLE thread
Definition: service.c:33
#define __int64
Definition: basetyps.h:16
#define except(x)
Definition: btrfs_drv.h:136
INT testnum
Definition: capicon.c:26
#define NULL
Definition: types.h:112
#define TRUE
Definition: types.h:120
#define FALSE
Definition: types.h:117
#define NTSTATUS
Definition: precomp.h:21
#define CDECL
Definition: compat.h:29
#define CloseHandle
Definition: compat.h:739
@ ExceptionContinueSearch
Definition: compat.h:91
@ ExceptionContinueExecution
Definition: compat.h:90
#define GetProcAddress(x, y)
Definition: compat.h:753
#define INVALID_HANDLE_VALUE
Definition: compat.h:731
#define GetCurrentProcess()
Definition: compat.h:759
enum _EXCEPTION_DISPOSITION EXCEPTION_DISPOSITION
#define MAX_PATH
Definition: compat.h:34
#define ERROR_INVALID_HANDLE
Definition: compat.h:98
#define CALLBACK
Definition: compat.h:35
BOOL WINAPI ContinueDebugEvent(IN DWORD dwProcessId, IN DWORD dwThreadId, IN DWORD dwContinueStatus)
Definition: debugger.c:413
BOOL WINAPI WaitForDebugEvent(IN LPDEBUG_EVENT lpDebugEvent, IN DWORD dwMilliseconds)
Definition: debugger.c:590
PPEB Peb
Definition: dllmain.c:27
HMODULE WINAPI DECLSPEC_HOTPATCH GetModuleHandleA(LPCSTR lpModuleName)
Definition: loader.c:812
HINSTANCE WINAPI DECLSPEC_HOTPATCH LoadLibraryA(LPCSTR lpLibFileName)
Definition: loader.c:111
BOOL WINAPI DECLSPEC_HOTPATCH CreateProcessA(LPCSTR lpApplicationName, LPSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes, LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags, LPVOID lpEnvironment, LPCSTR lpCurrentDirectory, LPSTARTUPINFOA lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation)
Definition: proc.c:4741
DWORD WINAPI ResumeThread(IN HANDLE hThread)
Definition: thread.c:567
HANDLE WINAPI DECLSPEC_HOTPATCH CreateThread(IN LPSECURITY_ATTRIBUTES lpThreadAttributes, IN DWORD dwStackSize, IN LPTHREAD_START_ROUTINE lpStartAddress, IN LPVOID lpParameter, IN DWORD dwCreationFlags, OUT LPDWORD lpThreadId)
Definition: thread.c:137
UINT(* handler)(MSIPACKAGE *)
Definition: action.c:7482
BOOL is_wow64
Definition: msi.c:54
unsigned int(__cdecl typeof(jpeg_read_scanlines))(struct jpeg_decompress_struct *
Definition: typeof.h:31
#define INFINITE
Definition: serial.h:102
#define ULONG_PTR
Definition: config.h:101
unsigned int BOOL
Definition: ntddk_ex.h:94
unsigned long DWORD
Definition: ntddk_ex.h:95
unsigned short WORD
Definition: ntddk_ex.h:93
void(* func_ptr)(void)
Definition: gccmain.c:11
GLuint GLuint GLsizei count
Definition: gl.h:1545
GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const GLvoid * data
Definition: gl.h:1950
GLenum func
Definition: glext.h:6028
GLuint res
Definition: glext.h:9613
GLuint buffer
Definition: glext.h:5915
GLuint GLuint GLuint GLuint arg1
Definition: glext.h:9513
GLuint GLuint GLuint GLuint GLuint GLuint GLuint arg2
Definition: glext.h:9514
GLenum const GLfloat * params
Definition: glext.h:5645
GLenum GLuint GLenum GLsizei const GLchar * buf
Definition: glext.h:7751
GLbitfield flags
Definition: glext.h:7161
GLuint GLsizei GLsizei * length
Definition: glext.h:6040
const GLint * first
Definition: glext.h:5794
GLenum const GLvoid * addr
Definition: glext.h:9621
GLuint GLfloat * val
Definition: glext.h:7180
GLuint GLint GLboolean GLint GLenum access
Definition: glext.h:7866
GLenum GLsizei len
Definition: glext.h:6722
GLintptr offset
Definition: glext.h:5920
GLfloat GLfloat GLfloat GLfloat h
Definition: glext.h:7723
GLsizei GLenum const GLvoid GLsizei GLenum GLbyte GLbyte GLbyte GLdouble GLdouble GLdouble GLfloat GLfloat GLfloat GLint GLint GLint GLshort GLshort GLshort GLubyte GLubyte GLubyte GLuint GLuint GLuint GLushort GLushort GLushort GLbyte GLbyte GLbyte GLbyte GLdouble GLdouble GLdouble GLdouble GLfloat GLfloat GLfloat GLfloat GLint GLint GLint GLint GLshort GLshort GLshort GLshort GLubyte GLubyte GLubyte GLubyte GLuint GLuint GLuint GLuint GLushort GLushort GLushort GLushort GLboolean const GLdouble const GLfloat const GLint const GLshort const GLbyte const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLdouble const GLfloat const GLfloat const GLint const GLint const GLshort const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort GLenum GLenum GLenum GLfloat GLenum GLint GLenum GLenum GLenum GLfloat GLenum GLenum GLint GLenum GLfloat GLenum GLint GLint GLushort GLenum GLenum GLfloat GLenum GLenum GLint GLfloat const GLubyte GLenum GLenum GLenum const GLfloat GLenum GLenum const GLint GLenum GLint GLint GLsizei GLsizei GLint GLenum GLenum const GLvoid GLenum GLenum const GLfloat GLenum GLenum const GLint GLenum GLenum const GLdouble GLenum GLenum const GLfloat GLenum GLenum const GLint GLsizei GLuint GLfloat GLuint GLbitfield GLfloat GLint GLuint GLboolean GLenum GLfloat GLenum GLbitfield GLenum GLfloat GLfloat GLint GLint const GLfloat GLenum GLfloat GLfloat GLint GLint GLfloat GLfloat GLint GLint const GLfloat GLint GLfloat GLfloat GLint GLfloat GLfloat GLint GLfloat GLfloat const GLdouble const GLfloat const GLdouble const GLfloat GLint i
Definition: glfuncs.h:248
GLsizei GLenum const GLvoid GLsizei GLenum GLbyte GLbyte GLbyte GLdouble GLdouble GLdouble GLfloat GLfloat GLfloat GLint GLint GLint GLshort GLshort GLshort GLubyte GLubyte GLubyte GLuint GLuint GLuint GLushort GLushort GLushort GLbyte GLbyte GLbyte GLbyte GLdouble GLdouble GLdouble GLdouble GLfloat GLfloat GLfloat GLfloat GLint GLint GLint GLint GLshort GLshort GLshort GLshort GLubyte GLubyte GLubyte GLubyte GLuint GLuint GLuint GLuint GLushort GLushort GLushort GLushort GLboolean const GLdouble const GLfloat const GLint const GLshort const GLbyte const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLdouble const GLfloat const GLfloat const GLint const GLint const GLshort const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort GLenum GLenum GLenum GLfloat GLenum GLint GLenum GLenum GLenum GLfloat GLenum GLenum GLint GLenum GLfloat GLenum GLint GLint GLushort GLenum GLenum GLfloat GLenum GLenum GLint GLfloat const GLubyte GLenum GLenum GLenum const GLfloat GLenum GLenum const GLint GLenum GLint GLint GLsizei GLsizei GLint GLenum GLenum const GLvoid GLenum GLenum const GLfloat GLenum GLenum const GLint GLenum GLenum const GLdouble GLenum GLenum const GLfloat GLenum GLenum const GLint GLsizei GLuint GLfloat GLuint GLbitfield GLfloat GLint GLuint GLboolean GLenum GLfloat GLenum GLbitfield GLenum GLfloat GLfloat GLint GLint const GLfloat GLenum GLfloat GLfloat GLint GLint GLfloat GLfloat GLint GLint const GLfloat GLint GLfloat GLfloat GLint GLfloat GLfloat GLint GLfloat GLfloat const GLdouble const GLfloat const GLdouble const GLfloat GLint GLint GLint j
Definition: glfuncs.h:250
void WINAPI SHIM_OBJ_NAME() OutputDebugStringA(LPCSTR lpOutputString)
Definition: ignoredbgout.c:18
#define EXCEPTION_CONTINUE_SEARCH
Definition: excpt.h:86
#define EXCEPTION_CONTINUE_EXECUTION
Definition: excpt.h:87
_Check_return_ _CRTIMP int __cdecl sscanf(_In_z_ const char *_Src, _In_z_ _Scanf_format_string_ const char *_Format,...)
struct __unwind_info unwind_info
@ ProcessExecuteFlags
Definition: winternl.h:889
#define NtCurrentTeb
uint32_t entry
Definition: isohybrid.c:63
#define memcpy(s1, s2, n)
Definition: mkisofs.h:878
unsigned __int64 ULONG64
Definition: imports.h:198
static struct test_info tests[]
#define sprintf(buf, format,...)
Definition: sprintf.c:55
BOOL expected
Definition: store.c:2063
static HINSTANCE hntdll
Definition: process.c:66
static void * code_mem
Definition: exception.c:39
static const void void SIZE_T *static LONG exit_code
Definition: exception.c:51
static PROCESSINFOCLASS
Definition: exception.c:52
static ULONG
Definition: exception.c:52
static PVECTORED_EXCEPTION_HANDLER func
Definition: exception.c:46
static PBOOL
Definition: exception.c:54
static PEXCEPTION_RECORD
Definition: exception.c:44
static PVOID
Definition: exception.c:44
static const void void SIZE_T
Definition: exception.c:50
static PULONG
Definition: exception.c:52
static refpint_t pi[]
Definition: server.c:96
int k
Definition: mpi.c:3369
unsigned int UINT
Definition: ndis.h:50
struct _DISPATCHER_CONTEXT DISPATCHER_CONTEXT
#define MEM_EXECUTE_OPTION_DISABLE
Definition: mmtypes.h:73
#define MEM_EXECUTE_OPTION_PERMANENT
Definition: mmtypes.h:76
#define MEM_EXECUTE_OPTION_ENABLE
Definition: mmtypes.h:74
NTSYSAPI VOID NTAPI RtlCaptureContext(_Out_ PCONTEXT ContextRecord)
LONG(NTAPI * PVECTORED_EXCEPTION_HANDLER)(PEXCEPTION_POINTERS ExceptionPointers)
Definition: rtltypes.h:505
int Count
Definition: noreturn.cpp:7
#define BOOL
Definition: nt_native.h:43
#define CONTEXT_DEBUG_REGISTERS
Definition: nt_native.h:1373
#define CONTEXT_CONTROL
Definition: nt_native.h:1369
#define PAGE_READWRITE
Definition: nt_native.h:1304
#define CONTEXT_INTEGER
Definition: nt_native.h:1370
#define CONTEXT_SEGMENTS
Definition: nt_native.h:1371
#define MEM_RESERVE
Definition: nt_native.h:1314
#define MEM_RELEASE
Definition: nt_native.h:1316
#define DWORD
Definition: nt_native.h:44
#define CONTEXT_FULL
Definition: nt_native.h:1375
#define MEM_COMMIT
Definition: nt_native.h:1313
#define PAGE_NOACCESS
Definition: nt_native.h:1302
#define PAGE_EXECUTE_READWRITE
Definition: nt_native.h:1308
#define DECLSPEC_ALIGN(x)
Definition: ntbasedef.h:251
_IRQL_requires_same_ _In_ PVOID EstablisherFrame
Definition: ntbasedef.h:653
_IRQL_requires_same_ _In_ PVOID _Inout_ struct _CONTEXT * ContextRecord
Definition: ntbasedef.h:654
#define STATUS_ILLEGAL_INSTRUCTION
Definition: ntstatus.h:266
#define STATUS_INVALID_HANDLE
Definition: ntstatus.h:245
#define STATUS_LONGJUMP
Definition: ntstatus.h:217
#define STATUS_UNWIND
Definition: ntstatus.h:276
#define DBG_RIPEXCEPTION
Definition: ntstatus.h:54
#define STATUS_SINGLE_STEP
Definition: ntstatus.h:185
#define STATUS_UNWIND_CONSOLIDATE
Definition: ntstatus.h:220
#define DBG_CONTINUE
Definition: ntstatus.h:47
#define STATUS_PRIVILEGED_INSTRUCTION
Definition: ntstatus.h:386
#define STATUS_BREAKPOINT
Definition: ntstatus.h:184
#define STATUS_FLOAT_MULTIPLE_TRAPS
Definition: ntstatus.h:808
#define STATUS_ACCESS_VIOLATION
Definition: ntstatus.h:242
#define DBG_PRINTEXCEPTION_C
Definition: ntstatus.h:53
#define DBG_EXCEPTION_NOT_HANDLED
Definition: ntstatus.h:57
#define STATUS_INVALID_INFO_CLASS
Definition: ntstatus.h:240
#define LOWORD(l)
Definition: pedump.c:82
#define BOOLEAN
Definition: pedump.c:73
long LONG
Definition: pedump.c:60
__asm__(".p2align 4, 0x90\n" ".seh_proc __seh2_global_filter_func\n" "__seh2_global_filter_func:\n" "\tsub %rbp, %rax\n" "\tpush %rbp\n" "\t.seh_pushreg %rbp\n" "\tsub $32, %rsp\n" "\t.seh_stackalloc 32\n" "\t.seh_endprologue\n" "\tsub %rax, %rdx\n" "\tmov %rdx, %rbp\n" "\tjmp *%r8\n" "__seh2_global_filter_func_exit:\n" "\t.p2align 4\n" "\tadd $32, %rsp\n" "\tpop %rbp\n" "\tret\n" "\t.seh_endproc")
#define test
Definition: rosglue.h:37
struct @5029 regs[]
struct _RUNTIME_FUNCTION * PRUNTIME_FUNCTION
@ UNW_FLAG_EHANDLER
Definition: rsym64.h:109
struct _RUNTIME_FUNCTION RUNTIME_FUNCTION
const char * winetest_platform
#define ros_skip_flaky
Definition: test.h:180
#define win_skip
Definition: test.h:163
int winetest_interactive
int winetest_get_mainargs(char ***pargv)
void winetest_wait_child_process(HANDLE process)
#define EXCEPTION_EXECUTE_FAULT
#define EXCEPTION_READ_FAULT
#define memset(x, y, z)
Definition: compat.h:39
#define STATUS_SUCCESS
Definition: shellext.h:65
TCHAR * cmdline
Definition: stretchblt.cpp:32
ULONG Eip
Definition: nt_native.h:1476
ULONG Eax
Definition: nt_native.h:1468
OUTPUT_DEBUG_STRING_INFO DebugString
Definition: winbase.h:799
DWORD dwDebugEventCode
Definition: winbase.h:788
RIP_INFO RipInfo
Definition: winbase.h:800
DWORD dwThreadId
Definition: winbase.h:790
DWORD dwProcessId
Definition: winbase.h:789
union _DEBUG_EVENT::@3290 u
EXCEPTION_DEBUG_INFO Exception
Definition: winbase.h:792
CREATE_PROCESS_DEBUG_INFO CreateProcessInfo
Definition: winbase.h:794
EXCEPTION_RECORD ExceptionRecord
Definition: winbase.h:751
PEXCEPTION_RECORD ExceptionRecord
Definition: rtltypes.h:200
PCONTEXT ContextRecord
Definition: rtltypes.h:201
DWORD ExceptionCode
Definition: compat.h:208
DWORD NumberParameters
Definition: compat.h:212
DWORD ExceptionFlags
Definition: compat.h:209
ULONG_PTR ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS]
Definition: compat.h:213
PVOID ExceptionAddress
Definition: compat.h:211
PEXCEPTION_ROUTINE Handler
Definition: compat.h:727
struct _EXCEPTION_REGISTRATION_RECORD * Prev
Definition: exception.h:46
PVOID ImageBaseAddress
Definition: ntddk_ex.h:245
BYTE BeingDebugged
Definition: btrfs_drv.h:1909
DWORD dwType
Definition: winbase.h:784
DWORD dwError
Definition: winbase.h:783
DWORD cb
Definition: winbase.h:831
Definition: inflate.c:139
Definition: http.c:7252
Definition: stat.h:55
Definition: ps.c:97
DWORD WINAPI WaitForSingleObject(IN HANDLE hHandle, IN DWORD dwMilliseconds)
Definition: synch.c:82
#define DWORD_PTR
Definition: treelist.c:76
const uint16_t * PCWSTR
Definition: typedefs.h:57
uint32_t DWORD_PTR
Definition: typedefs.h:65
uint64_t DWORD64
Definition: typedefs.h:67
void * PVOID
Definition: typedefs.h:50
PVOID HANDLE
Definition: typedefs.h:73
ULONG_PTR SIZE_T
Definition: typedefs.h:80
uint32_t ULONG_PTR
Definition: typedefs.h:65
uint32_t ULONG
Definition: typedefs.h:59
pass
Definition: typegen.h:25
#define STATUS_ACCESS_DENIED
Definition: udferr_usr.h:145
#define STATUS_INFO_LENGTH_MISMATCH
Definition: udferr_usr.h:133
PEXCEPTION_ROUTINE NTAPI RtlVirtualUnwind(_In_ ULONG HandlerType, _In_ ULONG64 ImageBase, _In_ ULONG64 ControlPc, _In_ PRUNTIME_FUNCTION FunctionEntry, _Inout_ PCONTEXT Context, _Outptr_ PVOID *HandlerData, _Out_ PULONG64 EstablisherFrame, _Inout_opt_ PKNONVOLATILE_CONTEXT_POINTERS ContextPointers)
Definition: unwind.c:478
int ret
LPVOID NTAPI VirtualAlloc(IN LPVOID lpAddress, IN SIZE_T dwSize, IN DWORD flAllocationType, IN DWORD flProtect)
Definition: virtmem.c:65
BOOL NTAPI VirtualProtect(IN LPVOID lpAddress, IN SIZE_T dwSize, IN DWORD flNewProtect, OUT PDWORD lpflOldProtect)
Definition: virtmem.c:135
BOOL NTAPI VirtualFree(IN LPVOID lpAddress, IN SIZE_T dwSize, IN DWORD dwFreeType)
Definition: virtmem.c:119
#define EXCEPTION_SINGLE_STEP
Definition: winbase.h:314
#define RIP_EVENT
Definition: winbase.h:110
#define EXCEPTION_FLT_STACK_CHECK
Definition: winbase.h:321
DWORD WINAPI GetLastError(void)
Definition: except.c:1042
#define CREATE_PROCESS_DEBUG_EVENT
Definition: winbase.h:104
HANDLE WINAPI GetCurrentThread(void)
Definition: proc.c:1148
#define EXIT_PROCESS_DEBUG_EVENT
Definition: winbase.h:106
#define EXCEPTION_INVALID_HANDLE
Definition: winbase.h:332
#define OUTPUT_DEBUG_STRING_EVENT
Definition: winbase.h:109
#define CREATE_SUSPENDED
Definition: winbase.h:178
#define EXCEPTION_DEBUG_EVENT
Definition: winbase.h:102
#define EXCEPTION_ILLEGAL_INSTRUCTION
Definition: winbase.h:327
#define EXCEPTION_ACCESS_VIOLATION
Definition: winbase.h:311
#define EXCEPTION_BREAKPOINT
Definition: winbase.h:313
#define DEBUG_PROCESS
Definition: winbase.h:176
#define EXCEPTION_FLT_DIVIDE_BY_ZERO
Definition: winbase.h:317
#define WINAPI
Definition: msvc.h:6
unsigned char BYTE
Definition: xxhash.c:193