ReactOS 0.4.16-dev-2528-g7139e57
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];
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
1145 ok(ret, "error %u\n", GetLastError());
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{
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;
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;
1698 handler = RtlVirtualUnwind( UNW_FLAG_EHANDLER, (ULONG64)code_mem, orig_rip,
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#ifdef __REACTOS__
1902 int i;
1903 LONG pass;
1904#else
1905 int i, pass;
1906#endif
1907
1908 if (!pRtlUnwindEx || !pRtlRestoreContext || !pRtlCaptureContext || !p_setjmp)
1909 {
1910 skip("RtlUnwindEx/RtlCaptureContext/RtlRestoreContext/_setjmp not found\n");
1911 return;
1912 }
1913
1914 /* RtlRestoreContext(NULL, NULL); crashes on Windows */
1915
1916 /* test simple case of capture and restore context */
1917 pass = 0;
1918 InterlockedIncrement(&pass); /* interlocked to prevent compiler from moving after capture */
1919 pRtlCaptureContext(&ctx);
1920 if (InterlockedIncrement(&pass) == 2) /* interlocked to prevent compiler from moving before capture */
1921 {
1922 pRtlRestoreContext(&ctx, NULL);
1923 ok(0, "shouldn't be reached\n");
1924 }
1925 else
1926 ok(pass < 4, "unexpected pass %d\n", pass);
1927
1928 /* test with jmp using RltRestoreContext */
1929 pass = 0;
1932 InterlockedIncrement(&pass); /* only called once */
1933 p_setjmp(&buf);
1935 if (pass == 3)
1936 {
1938 rec.NumberParameters = 1;
1939 rec.ExceptionInformation[0] = (DWORD64)&buf;
1940
1941 /* uses buf.Rip instead of ctx.Rip */
1942 pRtlRestoreContext(&ctx, &rec);
1943 ok(0, "shouldn't be reached\n");
1944 }
1945 else if (pass == 4)
1946 {
1947 ok(buf.Rbx == ctx.Rbx, "longjmp failed for Rbx, expected: %lx, got: %lx\n", buf.Rbx, ctx.Rbx);
1948 ok(buf.Rsp == ctx.Rsp, "longjmp failed for Rsp, expected: %lx, got: %lx\n", buf.Rsp, ctx.Rsp);
1949 ok(buf.Rbp == ctx.Rbp, "longjmp failed for Rbp, expected: %lx, got: %lx\n", buf.Rbp, ctx.Rbp);
1950 ok(buf.Rsi == ctx.Rsi, "longjmp failed for Rsi, expected: %lx, got: %lx\n", buf.Rsi, ctx.Rsi);
1951 ok(buf.Rdi == ctx.Rdi, "longjmp failed for Rdi, expected: %lx, got: %lx\n", buf.Rdi, ctx.Rdi);
1952 ok(buf.R12 == ctx.R12, "longjmp failed for R12, expected: %lx, got: %lx\n", buf.R12, ctx.R12);
1953 ok(buf.R13 == ctx.R13, "longjmp failed for R13, expected: %lx, got: %lx\n", buf.R13, ctx.R13);
1954 ok(buf.R14 == ctx.R14, "longjmp failed for R14, expected: %lx, got: %lx\n", buf.R14, ctx.R14);
1955 ok(buf.R15 == ctx.R15, "longjmp failed for R15, expected: %lx, got: %lx\n", buf.R15, ctx.R15);
1956
1957 fltsave = &buf.Xmm6;
1958 for (i = 0; i < 10; i++)
1959 {
1960 ok(fltsave[i].Part[0] == ctx.u.FltSave.XmmRegisters[i + 6].Low,
1961 "longjmp failed for Xmm%d, expected %lx, got %lx\n", i + 6,
1962 fltsave[i].Part[0], ctx.u.FltSave.XmmRegisters[i + 6].Low);
1963
1964 ok(fltsave[i].Part[1] == ctx.u.FltSave.XmmRegisters[i + 6].High,
1965 "longjmp failed for Xmm%d, expected %lx, got %lx\n", i + 6,
1966 fltsave[i].Part[1], ctx.u.FltSave.XmmRegisters[i + 6].High);
1967 }
1968 }
1969 else
1970 ok(0, "unexpected pass %d\n", pass);
1971
1972 /* test with jmp through RtlUnwindEx */
1973 pass = 0;
1975 pRtlCaptureContext(&ctx);
1976 InterlockedIncrement(&pass); /* only called once */
1977 p_setjmp(&buf);
1979 if (pass == 3)
1980 {
1982 rec.NumberParameters = 1;
1983 rec.ExceptionInformation[0] = (DWORD64)&buf;
1984
1985 /* uses buf.Rip instead of bogus 0xdeadbeef */
1986 pRtlUnwindEx((void*)buf.Rsp, (void*)0xdeadbeef, &rec, NULL, &ctx, NULL);
1987 ok(0, "shouldn't be reached\n");
1988 }
1989 else
1990 ok(pass == 4, "unexpected pass %d\n", pass);
1991
1992
1993 /* test with consolidate */
1994 pass = 0;
1998 if (pass == 2)
1999 {
2001 rec.NumberParameters = 3;
2002 rec.ExceptionInformation[0] = (DWORD64)test_consolidate_dummy;
2003 rec.ExceptionInformation[1] = (DWORD64)&ctx;
2004 rec.ExceptionInformation[2] = ctx.Rip;
2005 ctx.Rip = 0xdeadbeef;
2006
2007 pRtlRestoreContext(&ctx, &rec);
2008 ok(0, "shouldn't be reached\n");
2009 }
2010 else if (pass == 3)
2011 ok(consolidate_dummy_called, "test_consolidate_dummy not called\n");
2012 else
2013 ok(0, "unexpected pass %d\n", pass);
2014}
2015
2016static RUNTIME_FUNCTION* CALLBACK dynamic_unwind_callback( DWORD64 pc, PVOID context )
2017{
2018 static const int code_offset = 1024;
2019 static RUNTIME_FUNCTION runtime_func;
2020 (*(DWORD *)context)++;
2021
2022 runtime_func.BeginAddress = code_offset + 16;
2023 runtime_func.EndAddress = code_offset + 32;
2024 runtime_func.UnwindData = 0;
2025 return &runtime_func;
2026}
2027
2028static void test_dynamic_unwind(void)
2029{
2030 static const int code_offset = 1024;
2031 char buf[sizeof(RUNTIME_FUNCTION) + 4];
2032 RUNTIME_FUNCTION *runtime_func, *func;
2034 DWORD count;
2035
2036 /* Test RtlAddFunctionTable with aligned RUNTIME_FUNCTION pointer */
2037 runtime_func = (RUNTIME_FUNCTION *)buf;
2038 runtime_func->BeginAddress = code_offset;
2039 runtime_func->EndAddress = code_offset + 16;
2040 runtime_func->UnwindData = 0;
2041 ok( pRtlAddFunctionTable( runtime_func, 1, (ULONG_PTR)code_mem ),
2042 "RtlAddFunctionTable failed for runtime_func = %p (aligned)\n", runtime_func );
2043
2044 /* Lookup function outside of any function table */
2045 base = 0xdeadbeef;
2046 func = pRtlLookupFunctionEntry( (ULONG_PTR)code_mem + code_offset + 16, &base, NULL );
2047 ok( func == NULL,
2048 "RtlLookupFunctionEntry returned unexpected function, expected: NULL, got: %p\n", func );
2049 ok( !base || broken(base == 0xdeadbeef),
2050 "RtlLookupFunctionEntry modified base address, expected: 0, got: %lx\n", base );
2051
2052 /* Test with pointer inside of our function */
2053 base = 0xdeadbeef;
2054 func = pRtlLookupFunctionEntry( (ULONG_PTR)code_mem + code_offset + 8, &base, NULL );
2055 ok( func == runtime_func,
2056 "RtlLookupFunctionEntry didn't return expected function, expected: %p, got: %p\n", runtime_func, func );
2058 "RtlLookupFunctionEntry returned invalid base, expected: %lx, got: %lx\n", (ULONG_PTR)code_mem, base );
2059
2060 /* Test RtlDeleteFunctionTable */
2061 ok( pRtlDeleteFunctionTable( runtime_func ),
2062 "RtlDeleteFunctionTable failed for runtime_func = %p (aligned)\n", runtime_func );
2063 ok( !pRtlDeleteFunctionTable( runtime_func ),
2064 "RtlDeleteFunctionTable returned success for nonexistent table runtime_func = %p\n", runtime_func );
2065
2066 /* Unaligned RUNTIME_FUNCTION pointer */
2067 runtime_func = (RUNTIME_FUNCTION *)((ULONG_PTR)buf | 0x3);
2068 runtime_func->BeginAddress = code_offset;
2069 runtime_func->EndAddress = code_offset + 16;
2070 runtime_func->UnwindData = 0;
2071 ok( pRtlAddFunctionTable( runtime_func, 1, (ULONG_PTR)code_mem ),
2072 "RtlAddFunctionTable failed for runtime_func = %p (unaligned)\n", runtime_func );
2073 ok( pRtlDeleteFunctionTable( runtime_func ),
2074 "RtlDeleteFunctionTable failed for runtime_func = %p (unaligned)\n", runtime_func );
2075
2076 /* Attempt to insert the same entry twice */
2077 runtime_func = (RUNTIME_FUNCTION *)buf;
2078 runtime_func->BeginAddress = code_offset;
2079 runtime_func->EndAddress = code_offset + 16;
2080 runtime_func->UnwindData = 0;
2081 ok( pRtlAddFunctionTable( runtime_func, 1, (ULONG_PTR)code_mem ),
2082 "RtlAddFunctionTable failed for runtime_func = %p (first attempt)\n", runtime_func );
2083 ok( pRtlAddFunctionTable( runtime_func, 1, (ULONG_PTR)code_mem ),
2084 "RtlAddFunctionTable failed for runtime_func = %p (second attempt)\n", runtime_func );
2085 ok( pRtlDeleteFunctionTable( runtime_func ),
2086 "RtlDeleteFunctionTable failed for runtime_func = %p (first attempt)\n", runtime_func );
2087 ok( pRtlDeleteFunctionTable( runtime_func ),
2088 "RtlDeleteFunctionTable failed for runtime_func = %p (second attempt)\n", runtime_func );
2089 ok( !pRtlDeleteFunctionTable( runtime_func ),
2090 "RtlDeleteFunctionTable returned success for nonexistent table runtime_func = %p\n", runtime_func );
2091
2092 /* Test RtlInstallFunctionTableCallback with both low bits unset */
2094 ok( !pRtlInstallFunctionTableCallback( table, (ULONG_PTR)code_mem, code_offset + 32, &dynamic_unwind_callback, (PVOID*)&count, NULL ),
2095 "RtlInstallFunctionTableCallback returned success for table = %lx\n", table );
2096
2097 /* Test RtlInstallFunctionTableCallback with both low bits set */
2098 table = (ULONG_PTR)code_mem | 0x3;
2099 ok( pRtlInstallFunctionTableCallback( table, (ULONG_PTR)code_mem, code_offset + 32, &dynamic_unwind_callback, (PVOID*)&count, NULL ),
2100 "RtlInstallFunctionTableCallback failed for table = %lx\n", table );
2101
2102 /* Lookup function outside of any function table */
2103 count = 0;
2104 base = 0xdeadbeef;
2105 func = pRtlLookupFunctionEntry( (ULONG_PTR)code_mem + code_offset + 32, &base, NULL );
2106 ok( func == NULL,
2107 "RtlLookupFunctionEntry returned unexpected function, expected: NULL, got: %p\n", func );
2108 ok( !base || broken(base == 0xdeadbeef),
2109 "RtlLookupFunctionEntry modified base address, expected: 0, got: %lx\n", base );
2110 ok( !count,
2111 "RtlLookupFunctionEntry issued %d unexpected calls to dynamic_unwind_callback\n", count );
2112
2113 /* Test with pointer inside of our function */
2114 count = 0;
2115 base = 0xdeadbeef;
2116 func = pRtlLookupFunctionEntry( (ULONG_PTR)code_mem + code_offset + 24, &base, NULL );
2117 ok( func != NULL && func->BeginAddress == code_offset + 16 && func->EndAddress == code_offset + 32,
2118 "RtlLookupFunctionEntry didn't return expected function, got: %p\n", func );
2120 "RtlLookupFunctionEntry returned invalid base, expected: %lx, got: %lx\n", (ULONG_PTR)code_mem, base );
2121 ok( count == 1,
2122 "RtlLookupFunctionEntry issued %d calls to dynamic_unwind_callback, expected: 1\n", count );
2123
2124 /* Clean up again */
2125 ok( pRtlDeleteFunctionTable( (PRUNTIME_FUNCTION)table ),
2126 "RtlDeleteFunctionTable failed for table = %p\n", (PVOID)table );
2127 ok( !pRtlDeleteFunctionTable( (PRUNTIME_FUNCTION)table ),
2128 "RtlDeleteFunctionTable returned success for nonexistent table = %p\n", (PVOID)table );
2129
2130}
2131
2132static int termination_handler_called;
2133static void WINAPI termination_handler(ULONG flags, ULONG64 frame)
2134{
2135 termination_handler_called++;
2136
2137 ok(flags == 1 || broken(flags == 0x401), "flags = %x\n", flags);
2138 ok(frame == 0x1234, "frame = %p\n", (void*)frame);
2139}
2140
2141static void test___C_specific_handler(void)
2142{
2144 EXCEPTION_RECORD rec;
2146 ULONG64 frame;
2148 SCOPE_TABLE scope_table;
2149
2150 if (!p__C_specific_handler)
2151 {
2152 win_skip("__C_specific_handler not available\n");
2153 return;
2154 }
2155
2156 memset(&rec, 0, sizeof(rec));
2157 rec.ExceptionFlags = 2; /* EH_UNWINDING */
2158 frame = 0x1234;
2159 memset(&dispatch, 0, sizeof(dispatch));
2161 dispatch.ControlPc = dispatch.ImageBase + 0x200;
2162 dispatch.HandlerData = &scope_table;
2163 dispatch.ContextRecord = &context;
2164 scope_table.Count = 1;
2165 scope_table.ScopeRecord[0].BeginAddress = 0x200;
2166 scope_table.ScopeRecord[0].EndAddress = 0x400;
2167#ifndef __REACTOS__
2168 scope_table.ScopeRecord[0].HandlerAddress = (ULONG_PTR)termination_handler-dispatch.ImageBase;
2169#else
2170 scope_table.ScopeRecord[0].HandlerAddress = ((ULONG_PTR)termination_handler - (ULONG_PTR)dispatch.ImageBase);
2171#endif
2172 scope_table.ScopeRecord[0].JumpTarget = 0;
2173 memset(&context, 0, sizeof(context));
2174
2175 termination_handler_called = 0;
2176 ret = p__C_specific_handler(&rec, frame, &context, &dispatch);
2177 ok(ret == ExceptionContinueSearch, "__C_specific_handler returned %x\n", ret);
2178 ok(termination_handler_called == 1, "termination_handler_called = %d\n",
2179 termination_handler_called);
2180 ok(dispatch.ScopeIndex == 1, "dispatch.ScopeIndex = %d\n", dispatch.ScopeIndex);
2181
2182 ret = p__C_specific_handler(&rec, frame, &context, &dispatch);
2183 ok(ret == ExceptionContinueSearch, "__C_specific_handler returned %x\n", ret);
2184 ok(termination_handler_called == 1, "termination_handler_called = %d\n",
2185 termination_handler_called);
2186 ok(dispatch.ScopeIndex == 1, "dispatch.ScopeIndex = %d\n", dispatch.ScopeIndex);
2187}
2188
2189#endif /* __x86_64__ */
2190
2191#if defined(__i386__) || defined(__x86_64__)
2192
2193static DWORD WINAPI register_check_thread(void *arg)
2194{
2196 CONTEXT ctx;
2197
2198 memset(&ctx, 0, sizeof(ctx));
2199 ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
2200
2201 status = pNtGetContextThread(GetCurrentThread(), &ctx);
2202 ok(status == STATUS_SUCCESS, "NtGetContextThread failed with %x\n", status);
2203 ok(!ctx.Dr0, "expected 0, got %lx\n", (DWORD_PTR)ctx.Dr0);
2204 ok(!ctx.Dr1, "expected 0, got %lx\n", (DWORD_PTR)ctx.Dr1);
2205 ok(!ctx.Dr2, "expected 0, got %lx\n", (DWORD_PTR)ctx.Dr2);
2206 ok(!ctx.Dr3, "expected 0, got %lx\n", (DWORD_PTR)ctx.Dr3);
2207 ok(!ctx.Dr6, "expected 0, got %lx\n", (DWORD_PTR)ctx.Dr6);
2208 ok(!ctx.Dr7, "expected 0, got %lx\n", (DWORD_PTR)ctx.Dr7);
2209
2210 return 0;
2211}
2212
2213static void test_debug_registers(void)
2214{
2215 static const struct
2216 {
2217 ULONG_PTR dr0, dr1, dr2, dr3, dr6, dr7;
2218 }
2219 tests[] =
2220 {
2221 { 0x42424240, 0, 0x126bb070, 0x0badbad0, 0, 0xffff0115 },
2222 { 0x42424242, 0, 0x100f0fe7, 0x0abebabe, 0, 0x115 },
2223 };
2225 CONTEXT ctx;
2226 HANDLE thread;
2227 int i;
2228
2229 for (i = 0; i < sizeof(tests)/sizeof(tests[0]); i++)
2230 {
2231 memset(&ctx, 0, sizeof(ctx));
2232 ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
2233 ctx.Dr0 = tests[i].dr0;
2234 ctx.Dr1 = tests[i].dr1;
2235 ctx.Dr2 = tests[i].dr2;
2236 ctx.Dr3 = tests[i].dr3;
2237 ctx.Dr6 = tests[i].dr6;
2238 ctx.Dr7 = tests[i].dr7;
2239
2240 status = pNtSetContextThread(GetCurrentThread(), &ctx);
2241 ok(status == STATUS_SUCCESS, "NtGetContextThread failed with %08x\n", status);
2242
2243 memset(&ctx, 0, sizeof(ctx));
2244 ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
2245
2246 status = pNtGetContextThread(GetCurrentThread(), &ctx);
2247 ok(status == STATUS_SUCCESS, "NtGetContextThread failed with %08x\n", status);
2248 ok(ctx.Dr0 == tests[i].dr0, "test %d: expected %lx, got %lx\n", i, tests[i].dr0, (DWORD_PTR)ctx.Dr0);
2249 ok(ctx.Dr1 == tests[i].dr1, "test %d: expected %lx, got %lx\n", i, tests[i].dr1, (DWORD_PTR)ctx.Dr1);
2250 ok(ctx.Dr2 == tests[i].dr2, "test %d: expected %lx, got %lx\n", i, tests[i].dr2, (DWORD_PTR)ctx.Dr2);
2251 ok(ctx.Dr3 == tests[i].dr3, "test %d: expected %lx, got %lx\n", i, tests[i].dr3, (DWORD_PTR)ctx.Dr3);
2252 ok((ctx.Dr6 & 0xf00f) == tests[i].dr6, "test %d: expected %lx, got %lx\n", i, tests[i].dr6, (DWORD_PTR)ctx.Dr6);
2253 ok((ctx.Dr7 & ~0xdc00) == tests[i].dr7, "test %d: expected %lx, got %lx\n", i, tests[i].dr7, (DWORD_PTR)ctx.Dr7);
2254 }
2255
2256 memset(&ctx, 0, sizeof(ctx));
2257 ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
2258 ctx.Dr0 = 0xffffffff;
2259 ctx.Dr1 = 0xffffffff;
2260 ctx.Dr2 = 0xffffffff;
2261 ctx.Dr3 = 0xffffffff;
2262 ctx.Dr6 = 0xffffffff;
2263 ctx.Dr7 = 0x00000400;
2264 status = pNtSetContextThread(GetCurrentThread(), &ctx);
2265 ok(status == STATUS_SUCCESS, "NtSetContextThread failed with %x\n", status);
2266
2267 thread = CreateThread(NULL, 0, register_check_thread, NULL, CREATE_SUSPENDED, NULL);
2268 ok(thread != INVALID_HANDLE_VALUE, "CreateThread failed with %d\n", GetLastError());
2269
2270 ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
2271 status = pNtGetContextThread(thread, &ctx);
2272 ok(status == STATUS_SUCCESS, "NtGetContextThread failed with %x\n", status);
2273 ok(!ctx.Dr0, "expected 0, got %lx\n", (DWORD_PTR)ctx.Dr0);
2274 ok(!ctx.Dr1, "expected 0, got %lx\n", (DWORD_PTR)ctx.Dr1);
2275 ok(!ctx.Dr2, "expected 0, got %lx\n", (DWORD_PTR)ctx.Dr2);
2276 ok(!ctx.Dr3, "expected 0, got %lx\n", (DWORD_PTR)ctx.Dr3);
2277 ok(!ctx.Dr6, "expected 0, got %lx\n", (DWORD_PTR)ctx.Dr6);
2278 ok(!ctx.Dr7, "expected 0, got %lx\n", (DWORD_PTR)ctx.Dr7);
2279
2283}
2284
2285static DWORD outputdebugstring_exceptions;
2286
2287static LONG CALLBACK outputdebugstring_vectored_handler(EXCEPTION_POINTERS *ExceptionInfo)
2288{
2289 PEXCEPTION_RECORD rec = ExceptionInfo->ExceptionRecord;
2290 trace("vect. handler %08x addr:%p\n", rec->ExceptionCode, rec->ExceptionAddress);
2291
2292 ok(rec->ExceptionCode == DBG_PRINTEXCEPTION_C, "ExceptionCode is %08x instead of %08x\n",
2294 ok(rec->NumberParameters == 2, "ExceptionParameters is %d instead of 2\n", rec->NumberParameters);
2295 ok(rec->ExceptionInformation[0] == 12, "ExceptionInformation[0] = %d instead of 12\n", (DWORD)rec->ExceptionInformation[0]);
2296 ok(!strcmp((char *)rec->ExceptionInformation[1], "Hello World"),
2297 "ExceptionInformation[1] = '%s' instead of 'Hello World'\n", (char *)rec->ExceptionInformation[1]);
2298
2299 outputdebugstring_exceptions++;
2301}
2302
2303static void test_outputdebugstring(DWORD numexc)
2304{
2305 PVOID vectored_handler;
2306
2307 if (!pRtlAddVectoredExceptionHandler || !pRtlRemoveVectoredExceptionHandler)
2308 {
2309 skip("RtlAddVectoredExceptionHandler or RtlRemoveVectoredExceptionHandler not found\n");
2310 return;
2311 }
2312
2313 vectored_handler = pRtlAddVectoredExceptionHandler(TRUE, &outputdebugstring_vectored_handler);
2314 ok(vectored_handler != 0, "RtlAddVectoredExceptionHandler failed\n");
2315
2316 outputdebugstring_exceptions = 0;
2317 OutputDebugStringA("Hello World");
2318
2319 ok(outputdebugstring_exceptions == numexc, "OutputDebugStringA generated %d exceptions, expected %d\n",
2320 outputdebugstring_exceptions, numexc);
2321
2322 pRtlRemoveVectoredExceptionHandler(vectored_handler);
2323}
2324
2325static DWORD ripevent_exceptions;
2326
2327static LONG CALLBACK ripevent_vectored_handler(EXCEPTION_POINTERS *ExceptionInfo)
2328{
2329 PEXCEPTION_RECORD rec = ExceptionInfo->ExceptionRecord;
2330 trace("vect. handler %08x addr:%p\n", rec->ExceptionCode, rec->ExceptionAddress);
2331
2332 ok(rec->ExceptionCode == DBG_RIPEXCEPTION, "ExceptionCode is %08x instead of %08x\n",
2334 ok(rec->NumberParameters == 2, "ExceptionParameters is %d instead of 2\n", rec->NumberParameters);
2335 ok(rec->ExceptionInformation[0] == 0x11223344, "ExceptionInformation[0] = %08x instead of %08x\n",
2336 (NTSTATUS)rec->ExceptionInformation[0], 0x11223344);
2337 ok(rec->ExceptionInformation[1] == 0x55667788, "ExceptionInformation[1] = %08x instead of %08x\n",
2338 (NTSTATUS)rec->ExceptionInformation[1], 0x55667788);
2339
2340 ripevent_exceptions++;
2342}
2343
2344static void test_ripevent(DWORD numexc)
2345{
2347 PVOID vectored_handler;
2348
2349 if (!pRtlAddVectoredExceptionHandler || !pRtlRemoveVectoredExceptionHandler || !pRtlRaiseException)
2350 {
2351 skip("RtlAddVectoredExceptionHandler or RtlRemoveVectoredExceptionHandler or RtlRaiseException not found\n");
2352 return;
2353 }
2354
2355 vectored_handler = pRtlAddVectoredExceptionHandler(TRUE, &ripevent_vectored_handler);
2356 ok(vectored_handler != 0, "RtlAddVectoredExceptionHandler failed\n");
2357
2358 record.ExceptionCode = DBG_RIPEXCEPTION;
2359 record.ExceptionFlags = 0;
2360 record.ExceptionRecord = NULL;
2361 record.ExceptionAddress = NULL;
2362 record.NumberParameters = 2;
2363 record.ExceptionInformation[0] = 0x11223344;
2364 record.ExceptionInformation[1] = 0x55667788;
2365
2366 ripevent_exceptions = 0;
2367 pRtlRaiseException(&record);
2368 ok(ripevent_exceptions == numexc, "RtlRaiseException generated %d exceptions, expected %d\n",
2369 ripevent_exceptions, numexc);
2370
2371 pRtlRemoveVectoredExceptionHandler(vectored_handler);
2372}
2373
2374static DWORD debug_service_exceptions;
2375
2376static LONG CALLBACK debug_service_handler(EXCEPTION_POINTERS *ExceptionInfo)
2377{
2378 EXCEPTION_RECORD *rec = ExceptionInfo->ExceptionRecord;
2379 trace("vect. handler %08x addr:%p\n", rec->ExceptionCode, rec->ExceptionAddress);
2380
2381 ok(rec->ExceptionCode == EXCEPTION_BREAKPOINT, "ExceptionCode is %08x instead of %08x\n",
2383
2384#ifdef __i386__
2385 ok(ExceptionInfo->ContextRecord->Eip == (DWORD)code_mem + 0x1c,
2386 "expected Eip = %x, got %x\n", (DWORD)code_mem + 0x1c, ExceptionInfo->ContextRecord->Eip);
2387 ok(rec->NumberParameters == (is_wow64 ? 1 : 3),
2388 "ExceptionParameters is %d instead of %d\n", rec->NumberParameters, is_wow64 ? 1 : 3);
2389 ok(rec->ExceptionInformation[0] == ExceptionInfo->ContextRecord->Eax,
2390 "expected ExceptionInformation[0] = %x, got %lx\n",
2391 ExceptionInfo->ContextRecord->Eax, rec->ExceptionInformation[0]);
2392 if (!is_wow64)
2393 {
2394 ok(rec->ExceptionInformation[1] == 0x11111111,
2395 "got ExceptionInformation[1] = %lx\n", rec->ExceptionInformation[1]);
2396 ok(rec->ExceptionInformation[2] == 0x22222222,
2397 "got ExceptionInformation[2] = %lx\n", rec->ExceptionInformation[2]);
2398 }
2399#else
2400 ok(ExceptionInfo->ContextRecord->Rip == (DWORD_PTR)code_mem + 0x2f,
2401 "expected Rip = %lx, got %lx\n", (DWORD_PTR)code_mem + 0x2f, ExceptionInfo->ContextRecord->Rip);
2402 ok(rec->NumberParameters == 1,
2403 "ExceptionParameters is %d instead of 1\n", rec->NumberParameters);
2404 ok(rec->ExceptionInformation[0] == ExceptionInfo->ContextRecord->Rax,
2405 "expected ExceptionInformation[0] = %lx, got %lx\n",
2406 ExceptionInfo->ContextRecord->Rax, rec->ExceptionInformation[0]);
2407#endif
2408
2409 debug_service_exceptions++;
2411}
2412
2413#ifdef __i386__
2414
2415static const BYTE call_debug_service_code[] = {
2416 0x53, /* pushl %ebx */
2417 0x57, /* pushl %edi */
2418 0x8b, 0x44, 0x24, 0x0c, /* movl 12(%esp),%eax */
2419 0xb9, 0x11, 0x11, 0x11, 0x11, /* movl $0x11111111,%ecx */
2420 0xba, 0x22, 0x22, 0x22, 0x22, /* movl $0x22222222,%edx */
2421 0xbb, 0x33, 0x33, 0x33, 0x33, /* movl $0x33333333,%ebx */
2422 0xbf, 0x44, 0x44, 0x44, 0x44, /* movl $0x44444444,%edi */
2423 0xcd, 0x2d, /* int $0x2d */
2424 0xeb, /* jmp $+17 */
2425 0x0f, 0x1f, 0x00, /* nop */
2426 0x31, 0xc0, /* xorl %eax,%eax */
2427 0xeb, 0x0c, /* jmp $+14 */
2428 0x90, 0x90, 0x90, 0x90, /* nop */
2429 0x90, 0x90, 0x90, 0x90,
2430 0x90,
2431 0x31, 0xc0, /* xorl %eax,%eax */
2432 0x40, /* incl %eax */
2433 0x5f, /* popl %edi */
2434 0x5b, /* popl %ebx */
2435 0xc3, /* ret */
2436};
2437
2438#else
2439
2440static const BYTE call_debug_service_code[] = {
2441 0x53, /* push %rbx */
2442 0x57, /* push %rdi */
2443 0x48, 0x89, 0xc8, /* movl %rcx,%rax */
2444 0x48, 0xb9, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, /* movabs $0x1111111111111111,%rcx */
2445 0x48, 0xba, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, /* movabs $0x2222222222222222,%rdx */
2446 0x48, 0xbb, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, /* movabs $0x3333333333333333,%rbx */
2447 0x48, 0xbf, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, /* movabs $0x4444444444444444,%rdi */
2448 0xcd, 0x2d, /* int $0x2d */
2449 0xeb, /* jmp $+17 */
2450 0x0f, 0x1f, 0x00, /* nop */
2451 0x48, 0x31, 0xc0, /* xor %rax,%rax */
2452 0xeb, 0x0e, /* jmp $+16 */
2453 0x90, 0x90, 0x90, 0x90, /* nop */
2454 0x90, 0x90, 0x90, 0x90,
2455 0x48, 0x31, 0xc0, /* xor %rax,%rax */
2456 0x48, 0xff, 0xc0, /* inc %rax */
2457 0x5f, /* pop %rdi */
2458 0x5b, /* pop %rbx */
2459 0xc3, /* ret */
2460};
2461
2462#endif
2463
2464static void test_debug_service(DWORD numexc)
2465{
2467 DWORD expected_exc, expected_ret;
2468 void *vectored_handler;
2469 DWORD ret;
2470
2471 /* code will return 0 if execution resumes immediately after "int $0x2d", otherwise 1 */
2473
2474 vectored_handler = pRtlAddVectoredExceptionHandler(TRUE, &debug_service_handler);
2475 ok(vectored_handler != 0, "RtlAddVectoredExceptionHandler failed\n");
2476
2477 expected_exc = numexc;
2478 expected_ret = (numexc != 0);
2479
2480 /* BREAKPOINT_BREAK */
2481 debug_service_exceptions = 0;
2482 ret = func(0);
2483 ok(debug_service_exceptions == expected_exc,
2484 "BREAKPOINT_BREAK generated %u exceptions, expected %u\n",
2485 debug_service_exceptions, expected_exc);
2486 ok(ret == expected_ret,
2487 "BREAKPOINT_BREAK returned %u, expected %u\n", ret, expected_ret);
2488
2489 /* BREAKPOINT_PROMPT */
2490 debug_service_exceptions = 0;
2491 ret = func(2);
2492 ok(debug_service_exceptions == expected_exc,
2493 "BREAKPOINT_PROMPT generated %u exceptions, expected %u\n",
2494 debug_service_exceptions, expected_exc);
2495 ok(ret == expected_ret,
2496 "BREAKPOINT_PROMPT returned %u, expected %u\n", ret, expected_ret);
2497
2498 /* invalid debug service */
2499 debug_service_exceptions = 0;
2500 ret = func(6);
2501 ok(debug_service_exceptions == expected_exc,
2502 "invalid debug service generated %u exceptions, expected %u\n",
2503 debug_service_exceptions, expected_exc);
2504 ok(ret == expected_ret,
2505 "invalid debug service returned %u, expected %u\n", ret, expected_ret);
2506
2507 expected_exc = (is_wow64 ? numexc : 0);
2508 expected_ret = (is_wow64 && numexc);
2509
2510 /* BREAKPOINT_PRINT */
2511 debug_service_exceptions = 0;
2512 ret = func(1);
2513 ok(debug_service_exceptions == expected_exc,
2514 "BREAKPOINT_PRINT generated %u exceptions, expected %u\n",
2515 debug_service_exceptions, expected_exc);
2516 ok(ret == expected_ret,
2517 "BREAKPOINT_PRINT returned %u, expected %u\n", ret, expected_ret);
2518
2519 /* BREAKPOINT_LOAD_SYMBOLS */
2520 debug_service_exceptions = 0;
2521 ret = func(3);
2522 ok(debug_service_exceptions == expected_exc,
2523 "BREAKPOINT_LOAD_SYMBOLS generated %u exceptions, expected %u\n",
2524 debug_service_exceptions, expected_exc);
2525 ok(ret == expected_ret,
2526 "BREAKPOINT_LOAD_SYMBOLS returned %u, expected %u\n", ret, expected_ret);
2527
2528 /* BREAKPOINT_UNLOAD_SYMBOLS */
2529 debug_service_exceptions = 0;
2530 ret = func(4);
2531 ok(debug_service_exceptions == expected_exc,
2532 "BREAKPOINT_UNLOAD_SYMBOLS generated %u exceptions, expected %u\n",
2533 debug_service_exceptions, expected_exc);
2534 ok(ret == expected_ret,
2535 "BREAKPOINT_UNLOAD_SYMBOLS returned %u, expected %u\n", ret, expected_ret);
2536
2537 /* BREAKPOINT_COMMAND_STRING */
2538 debug_service_exceptions = 0;
2539 ret = func(5);
2540 ok(debug_service_exceptions == expected_exc || broken(debug_service_exceptions == numexc),
2541 "BREAKPOINT_COMMAND_STRING generated %u exceptions, expected %u\n",
2542 debug_service_exceptions, expected_exc);
2543 ok(ret == expected_ret || broken(ret == (numexc != 0)),
2544 "BREAKPOINT_COMMAND_STRING returned %u, expected %u\n", ret, expected_ret);
2545
2546 pRtlRemoveVectoredExceptionHandler(vectored_handler);
2547}
2548
2549static DWORD breakpoint_exceptions;
2550
2551static LONG CALLBACK breakpoint_handler(EXCEPTION_POINTERS *ExceptionInfo)
2552{
2553 EXCEPTION_RECORD *rec = ExceptionInfo->ExceptionRecord;
2554 trace("vect. handler %08x addr:%p\n", rec->ExceptionCode, rec->ExceptionAddress);
2555
2556 ok(rec->ExceptionCode == EXCEPTION_BREAKPOINT, "ExceptionCode is %08x instead of %08x\n",
2558
2559#ifdef __i386__
2560 ok(ExceptionInfo->ContextRecord->Eip == (DWORD)code_mem + 1,
2561 "expected Eip = %x, got %x\n", (DWORD)code_mem + 1, ExceptionInfo->ContextRecord->Eip);
2562 ok(rec->NumberParameters == (is_wow64 ? 1 : 3),
2563 "ExceptionParameters is %d instead of %d\n", rec->NumberParameters, is_wow64 ? 1 : 3);
2564 ok(rec->ExceptionInformation[0] == 0,
2565 "got ExceptionInformation[0] = %lx\n", rec->ExceptionInformation[0]);
2566 ExceptionInfo->ContextRecord->Eip = (DWORD)code_mem + 2;
2567#else
2568 ok(ExceptionInfo->ContextRecord->Rip == (DWORD_PTR)code_mem + 1,
2569 "expected Rip = %lx, got %lx\n", (DWORD_PTR)code_mem + 1, ExceptionInfo->ContextRecord->Rip);
2570 ok(rec->NumberParameters == 1,
2571 "ExceptionParameters is %d instead of 1\n", rec->NumberParameters);
2572 ok(rec->ExceptionInformation[0] == 0,
2573 "got ExceptionInformation[0] = %lx\n", rec->ExceptionInformation[0]);
2574 ExceptionInfo->ContextRecord->Rip = (DWORD_PTR)code_mem + 2;
2575#endif
2576
2577 breakpoint_exceptions++;
2579}
2580
2581static const BYTE breakpoint_code[] = {
2582 0xcd, 0x03, /* int $0x3 */
2583 0xc3, /* ret */
2584};
2585
2586static void test_breakpoint(DWORD numexc)
2587{
2589 void *vectored_handler;
2590
2591 memcpy(code_mem, breakpoint_code, sizeof(breakpoint_code));
2592
2593 vectored_handler = pRtlAddVectoredExceptionHandler(TRUE, &breakpoint_handler);
2594 ok(vectored_handler != 0, "RtlAddVectoredExceptionHandler failed\n");
2595
2596 breakpoint_exceptions = 0;
2597 func();
2598 ok(breakpoint_exceptions == numexc, "int $0x3 generated %u exceptions, expected %u\n",
2599 breakpoint_exceptions, numexc);
2600
2601 pRtlRemoveVectoredExceptionHandler(vectored_handler);
2602}
2603
2604static DWORD invalid_handle_exceptions;
2605
2606static LONG CALLBACK invalid_handle_vectored_handler(EXCEPTION_POINTERS *ExceptionInfo)
2607{
2608 PEXCEPTION_RECORD rec = ExceptionInfo->ExceptionRecord;
2609 trace("vect. handler %08x addr:%p\n", rec->ExceptionCode, rec->ExceptionAddress);
2610
2611 ok(rec->ExceptionCode == EXCEPTION_INVALID_HANDLE, "ExceptionCode is %08x instead of %08x\n",
2613 ok(rec->NumberParameters == 0, "ExceptionParameters is %d instead of 0\n", rec->NumberParameters);
2614
2615 invalid_handle_exceptions++;
2617}
2618
2619static void test_closehandle(DWORD numexc)
2620{
2621 PVOID vectored_handler;
2623 DWORD res;
2624
2625 if (!pRtlAddVectoredExceptionHandler || !pRtlRemoveVectoredExceptionHandler || !pRtlRaiseException)
2626 {
2627 skip("RtlAddVectoredExceptionHandler or RtlRemoveVectoredExceptionHandler or RtlRaiseException not found\n");
2628 return;
2629 }
2630
2631 vectored_handler = pRtlAddVectoredExceptionHandler(TRUE, &invalid_handle_vectored_handler);
2632 ok(vectored_handler != 0, "RtlAddVectoredExceptionHandler failed\n");
2633
2634 invalid_handle_exceptions = 0;
2635 res = CloseHandle((HANDLE)0xdeadbeef);
2636 ok(!res, "CloseHandle(0xdeadbeef) unexpectedly succeeded\n");
2637 ok(GetLastError() == ERROR_INVALID_HANDLE, "wrong error code %d instead of %d\n",
2639 ok(invalid_handle_exceptions == numexc, "CloseHandle generated %d exceptions, expected %d\n",
2640 invalid_handle_exceptions, numexc);
2641
2642 invalid_handle_exceptions = 0;
2643 status = pNtClose((HANDLE)0xdeadbeef);
2644 ok(status == STATUS_INVALID_HANDLE, "NtClose(0xdeadbeef) returned status %08x\n", status);
2645 ok(invalid_handle_exceptions == numexc, "NtClose generated %d exceptions, expected %d\n",
2646 invalid_handle_exceptions, numexc);
2647
2648 pRtlRemoveVectoredExceptionHandler(vectored_handler);
2649}
2650
2651static void test_vectored_continue_handler(void)
2652{
2653 PVOID handler1, handler2;
2654 ULONG ret;
2655
2656 if (!pRtlAddVectoredContinueHandler || !pRtlRemoveVectoredContinueHandler)
2657 {
2658 skip("RtlAddVectoredContinueHandler or RtlRemoveVectoredContinueHandler not found\n");
2659 return;
2660 }
2661
2662 handler1 = pRtlAddVectoredContinueHandler(TRUE, (void *)0xdeadbeef);
2663 ok(handler1 != 0, "RtlAddVectoredContinueHandler failed\n");
2664
2665 handler2 = pRtlAddVectoredContinueHandler(TRUE, (void *)0xdeadbeef);
2666 ok(handler2 != 0, "RtlAddVectoredContinueHandler failed\n");
2667 ok(handler1 != handler2, "RtlAddVectoredContinueHandler returned same handler\n");
2668
2669 if (pRtlRemoveVectoredExceptionHandler)
2670 {
2671 ret = pRtlRemoveVectoredExceptionHandler(handler1);
2672 ok(!ret, "RtlRemoveVectoredExceptionHandler succeeded\n");
2673 }
2674
2675 ret = pRtlRemoveVectoredContinueHandler(handler1);
2676 ok(ret, "RtlRemoveVectoredContinueHandler failed\n");
2677
2678 ret = pRtlRemoveVectoredContinueHandler(handler2);
2679 ok(ret, "RtlRemoveVectoredContinueHandler failed\n");
2680
2681 ret = pRtlRemoveVectoredContinueHandler(handler1);
2682 ok(!ret, "RtlRemoveVectoredContinueHandler succeeded\n");
2683
2684 ret = pRtlRemoveVectoredContinueHandler((void *)0x11223344);
2685 ok(!ret, "RtlRemoveVectoredContinueHandler succeeded\n");
2686}
2687#endif /* defined(__i386__) || defined(__x86_64__) */
2688
2690{
2691 HMODULE hntdll = GetModuleHandleA("ntdll.dll");
2692#if defined(__x86_64__)
2693 HMODULE hmsvcrt = LoadLibraryA("msvcrt.dll");
2694#endif
2695
2696#if defined(__REACTOS__) && !defined(_M_AMD64)
2697 if (!winetest_interactive &&
2698 !strcmp(winetest_platform, "windows"))
2699 {
2700 skip("ROSTESTS-240: Skipping ntdll_winetest:exception because it hangs on WHS-Testbot. Set winetest_interactive to run it anyway.\n");
2701 return;
2702 }
2703#endif
2705 if(!code_mem) {
2706 trace("VirtualAlloc failed\n");
2707 return;
2708 }
2709
2710 pNtGetContextThread = (void *)GetProcAddress( hntdll, "NtGetContextThread" );
2711 pNtSetContextThread = (void *)GetProcAddress( hntdll, "NtSetContextThread" );
2712 pNtReadVirtualMemory = (void *)GetProcAddress( hntdll, "NtReadVirtualMemory" );
2713 pNtClose = (void *)GetProcAddress( hntdll, "NtClose" );
2714 pRtlUnwind = (void *)GetProcAddress( hntdll, "RtlUnwind" );
2715 pRtlRaiseException = (void *)GetProcAddress( hntdll, "RtlRaiseException" );
2716 pRtlCaptureContext = (void *)GetProcAddress( hntdll, "RtlCaptureContext" );
2717 pNtTerminateProcess = (void *)GetProcAddress( hntdll, "NtTerminateProcess" );
2718 pRtlAddVectoredExceptionHandler = (void *)GetProcAddress( hntdll,
2719 "RtlAddVectoredExceptionHandler" );
2720 pRtlRemoveVectoredExceptionHandler = (void *)GetProcAddress( hntdll,
2721 "RtlRemoveVectoredExceptionHandler" );
2722 pRtlAddVectoredContinueHandler = (void *)GetProcAddress( hntdll,
2723 "RtlAddVectoredContinueHandler" );
2724 pRtlRemoveVectoredContinueHandler = (void *)GetProcAddress( hntdll,
2725 "RtlRemoveVectoredContinueHandler" );
2726 pNtQueryInformationProcess = (void*)GetProcAddress( hntdll,
2727 "NtQueryInformationProcess" );
2728 pNtSetInformationProcess = (void*)GetProcAddress( hntdll,
2729 "NtSetInformationProcess" );
2730 pIsWow64Process = (void *)GetProcAddress(GetModuleHandleA("kernel32.dll"), "IsWow64Process");
2731
2732#ifdef __i386__
2733 if (!pIsWow64Process || !pIsWow64Process( GetCurrentProcess(), &is_wow64 )) is_wow64 = FALSE;
2734
2735 if (pRtlAddVectoredExceptionHandler && pRtlRemoveVectoredExceptionHandler)
2736 have_vectored_api = TRUE;
2737 else
2738 skip("RtlAddVectoredExceptionHandler or RtlRemoveVectoredExceptionHandler not found\n");
2739
2740 my_argc = winetest_get_mainargs( &my_argv );
2741 if (my_argc >= 4)
2742 {
2743 void *addr;
2744 sscanf( my_argv[3], "%p", &addr );
2745
2746 if (addr != &test_stage)
2747 {
2748 skip( "child process not mapped at same address (%p/%p)\n", &test_stage, addr);
2749 return;
2750 }
2751
2752 /* child must be run under a debugger */
2754 {
2755 ok(FALSE, "child process not being debugged?\n");
2756 return;
2757 }
2758
2759 if (pRtlRaiseException)
2760 {
2761 test_stage = 1;
2762 run_rtlraiseexception_test(0x12345);
2763 run_rtlraiseexception_test(EXCEPTION_BREAKPOINT);
2764 run_rtlraiseexception_test(EXCEPTION_INVALID_HANDLE);
2765 test_stage = 2;
2766 run_rtlraiseexception_test(0x12345);
2767 run_rtlraiseexception_test(EXCEPTION_BREAKPOINT);
2768 run_rtlraiseexception_test(EXCEPTION_INVALID_HANDLE);
2769 test_stage = 3;
2770 test_outputdebugstring(0);
2771 test_stage = 4;
2772 test_outputdebugstring(2);
2773 test_stage = 5;
2774 test_ripevent(0);
2775 test_stage = 6;
2776 test_ripevent(1);
2777 test_stage = 7;
2778 test_debug_service(0);
2779 test_stage = 8;
2780 test_debug_service(1);
2781 test_stage = 9;
2782 test_breakpoint(0);
2783 test_stage = 10;
2784 test_breakpoint(1);
2785 test_stage = 11;
2786 test_closehandle(0);
2787 test_stage = 12;
2788 test_closehandle(1);
2789 }
2790 else
2791 skip( "RtlRaiseException not found\n" );
2792
2793 /* rest of tests only run in parent */
2794 return;
2795 }
2796
2797 test_unwind();
2798 test_exceptions();
2799 test_rtlraiseexception();
2800 test_debug_registers();
2801 test_outputdebugstring(1);
2802 test_ripevent(1);
2803 test_debug_service(1);
2804 test_breakpoint(1);
2805 test_closehandle(0);
2806 test_vectored_continue_handler();
2807 test_debugger();
2808 test_simd_exceptions();
2809 test_fpu_exceptions();
2810 test_dpe_exceptions();
2811 test_prot_fault();
2812 test_thread_context();
2813
2814#elif defined(__x86_64__)
2815 pRtlAddFunctionTable = (void *)GetProcAddress( hntdll,
2816 "RtlAddFunctionTable" );
2817 pRtlDeleteFunctionTable = (void *)GetProcAddress( hntdll,
2818 "RtlDeleteFunctionTable" );
2819 pRtlInstallFunctionTableCallback = (void *)GetProcAddress( hntdll,
2820 "RtlInstallFunctionTableCallback" );
2821 pRtlLookupFunctionEntry = (void *)GetProcAddress( hntdll,
2822 "RtlLookupFunctionEntry" );
2823 p__C_specific_handler = (void *)GetProcAddress( hntdll,
2824 "__C_specific_handler" );
2825 pRtlCaptureContext = (void *)GetProcAddress( hntdll,
2826 "RtlCaptureContext" );
2827 pRtlRestoreContext = (void *)GetProcAddress( hntdll,
2828 "RtlRestoreContext" );
2829 pRtlUnwindEx = (void *)GetProcAddress( hntdll,
2830 "RtlUnwindEx" );
2831 p_setjmp = (void *)GetProcAddress( hmsvcrt,
2832 "_setjmp" );
2833
2834 test_debug_registers();
2835 test_outputdebugstring(1);
2836 test_ripevent(1);
2837 test_debug_service(1);
2838 test_breakpoint(1);
2839 test_closehandle(0);
2840 test_vectored_continue_handler();
2841 test_virtual_unwind();
2842 test___C_specific_handler();
2843 test_restore_context();
2844
2845 if (pRtlAddFunctionTable && pRtlDeleteFunctionTable && pRtlInstallFunctionTableCallback && pRtlLookupFunctionEntry)
2846 test_dynamic_unwind();
2847 else
2848 skip( "Dynamic unwind functions not found\n" );
2849
2850#endif
2851
2853}
static struct _test_info results[8]
Definition: SetCursorPos.c:31
#define expect(EXPECTED, GOT)
Definition: SystemMenu.c:483
#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 broken(x)
Definition: atltest.h:178
#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 except(x)
Definition: btrfs_drv.h:136
INT testnum
Definition: capicon.c:26
#define STATUS_ILLEGAL_INSTRUCTION
Definition: d3dkmdt.h:41
#define STATUS_INVALID_HANDLE
Definition: d3dkmdt.h:40
#define NULL
Definition: types.h:112
#define TRUE
Definition: types.h:120
#define FALSE
Definition: types.h:117
#define NTSTATUS
Definition: precomp.h:19
#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
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
BOOL WINAPI DECLSPEC_HOTPATCH CreateProcessA(const char *app_name, char *cmd_line, SECURITY_ATTRIBUTES *process_attr, SECURITY_ATTRIBUTES *thread_attr, BOOL inherit, DWORD flags, void *env, const char *cur_dir, STARTUPINFOA *startup_info, PROCESS_INFORMATION *info)
Definition: process.c:686
UINT(* handler)(MSIPACKAGE *)
Definition: action.c:7512
#define DECLSPEC_ALIGN(x)
Definition: corecrt.h:141
#define __int64
Definition: corecrt.h:72
_ACRTIMP int __cdecl _ACRTIMP int __cdecl _ACRTIMP int __cdecl _ACRTIMP int __cdecl _ACRTIMP int __cdecl _ACRTIMP int __cdecl _ACRTIMP int __cdecl sscanf(const char *, const char *,...) __WINE_CRT_SCANF_ATTR(2
_ACRTIMP char *__cdecl strstr(const char *, const char *)
Definition: string.c:3415
_ACRTIMP int __cdecl strcmp(const char *, const char *)
Definition: string.c:3319
unsigned int(__cdecl typeof(jpeg_read_scanlines))(struct jpeg_decompress_struct *
Definition: typeof.h:31
return ret
Definition: mutex.c:146
#define INFINITE
Definition: serial.h:102
#define ULONG_PTR
Definition: config.h:101
unsigned short WORD
Definition: ntddk_ex.h:93
unsigned int BOOL
Definition: ntddk_ex.h:94
unsigned long DWORD
Definition: ntddk_ex.h:95
#define STATUS_ACCESS_VIOLATION
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
GLintptr offset
Definition: glext.h:5920
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
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_code
Definition: excpt.h:84
#define EXCEPTION_CONTINUE_SEARCH
Definition: excpt.h:91
#define EXCEPTION_CONTINUE_EXECUTION
Definition: excpt.h:92
#define exception_info
Definition: excpt.h:86
#define NtCurrentTeb
uint32_t entry
Definition: isohybrid.c:63
BOOL is_wow64
Definition: main.c:38
const char * winetest_platform
#define ros_skip_flaky
Definition: minitest.h:84
#define win_skip
Definition: minitest.h:67
int winetest_interactive
#define EXCEPTION_SINGLE_STEP
Definition: minwinbase.h:47
#define RIP_EVENT
Definition: minwinbase.h:41
#define EXCEPTION_FLT_STACK_CHECK
Definition: minwinbase.h:54
#define CREATE_PROCESS_DEBUG_EVENT
Definition: minwinbase.h:35
#define EXIT_PROCESS_DEBUG_EVENT
Definition: minwinbase.h:37
#define EXCEPTION_INVALID_HANDLE
Definition: minwinbase.h:65
#define OUTPUT_DEBUG_STRING_EVENT
Definition: minwinbase.h:40
#define EXCEPTION_DEBUG_EVENT
Definition: minwinbase.h:33
#define EXCEPTION_ILLEGAL_INSTRUCTION
Definition: minwinbase.h:60
#define EXCEPTION_ACCESS_VIOLATION
Definition: minwinbase.h:44
#define EXCEPTION_BREAKPOINT
Definition: minwinbase.h:46
#define EXCEPTION_FLT_DIVIDE_BY_ZERO
Definition: minwinbase.h:50
#define memcpy(s1, s2, n)
Definition: mkisofs.h:878
unsigned __int64 ULONG64
Definition: imports.h:198
static struct test_info tests[]
#define sprintf
Definition: sprintf.c:45
BOOL expected
Definition: store.c:2000
static const BYTE call_debug_service_code[]
Definition: debugger.c:1594
static void test_debugger(const char *argv0)
Definition: debugger.c:1645
static PROCESS_INFORMATION pi
Definition: debugger.c:2303
static HINSTANCE hntdll
Definition: process.c:68
static SYSTEM_INFO si
Definition: virtual.c:39
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
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:499
int Count
Definition: noreturn.cpp:7
#define CONTEXT_DEBUG_REGISTERS
Definition: nt_native.h:1376
#define CONTEXT_CONTROL
Definition: nt_native.h:1372
#define PAGE_READWRITE
Definition: nt_native.h:1307
#define CONTEXT_INTEGER
Definition: nt_native.h:1373
#define CONTEXT_SEGMENTS
Definition: nt_native.h:1374
#define BOOL
Definition: nt_native.h:43
#define MEM_RESERVE
Definition: nt_native.h:1317
#define MEM_RELEASE
Definition: nt_native.h:1319
#define DWORD
Definition: nt_native.h:44
#define CONTEXT_FULL
Definition: nt_native.h:1378
#define MEM_COMMIT
Definition: nt_native.h:1316
#define PAGE_NOACCESS
Definition: nt_native.h:1305
#define PAGE_EXECUTE_READWRITE
Definition: nt_native.h:1311
_IRQL_requires_same_ _In_ PVOID EstablisherFrame
Definition: ntbasedef.h:665
_IRQL_requires_same_ _In_ PVOID _Inout_ struct _CONTEXT * ContextRecord
Definition: ntbasedef.h:666
#define STATUS_LONGJUMP
Definition: ntstatus.h:297
#define STATUS_UNWIND
Definition: ntstatus.h:369
#define DBG_RIPEXCEPTION
Definition: ntstatus.h:105
#define STATUS_SINGLE_STEP
Definition: ntstatus.h:265
#define STATUS_UNWIND_CONSOLIDATE
Definition: ntstatus.h:300
#define DBG_CONTINUE
Definition: ntstatus.h:98
#define STATUS_PRIVILEGED_INSTRUCTION
Definition: ntstatus.h:480
#define STATUS_BREAKPOINT
Definition: ntstatus.h:264
#define STATUS_FLOAT_MULTIPLE_TRAPS
Definition: ntstatus.h:933
#define DBG_PRINTEXCEPTION_C
Definition: ntstatus.h:104
#define DBG_EXCEPTION_NOT_HANDLED
Definition: ntstatus.h:109
#define STATUS_INVALID_INFO_CLASS
Definition: ntstatus.h:333
#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" "\tpush %rbx\n" "\t.seh_pushreg %rbx\n" "\tpush %rdi\n" "\t.seh_pushreg %rdi\n" "\tpush %rsi\n" "\t.seh_pushreg %rsi\n" "\tpush %r12\n" "\t.seh_pushreg %r12\n" "\tpush %r13\n" "\t.seh_pushreg %r13\n" "\tpush %r14\n" "\t.seh_pushreg %r14\n" "\tpush %r15\n" "\t.seh_pushreg %r15\n" "\tsub $40, %rsp\n" "\t.seh_stackalloc 40\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 $40, %rsp\n" "\tpop %r15\n" "\tpop %r14\n" "\tpop %r13\n" "\tpop %r12\n" "\tpop %rsi\n" "\tpop %rdi\n" "\tpop %rbx\n" "\tpop %rbp\n" "\tret\n" "\t.seh_endproc")
#define test
Definition: rosglue.h:37
NTSYSAPI PEXCEPTION_ROUTINE WINAPI RtlVirtualUnwind(ULONG, ULONG_PTR, ULONG_PTR, RUNTIME_FUNCTION *, CONTEXT *, void **, ULONG_PTR *, KNONVOLATILE_CONTEXT_POINTERS *)
PRUNTIME_FUNCTION(CALLBACK * PGET_RUNTIME_FUNCTION_CALLBACK)(DWORD_PTR, PVOID)
Definition: rtlsupportapi.h:56
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:1479
ULONG Eax
Definition: nt_native.h:1471
OUTPUT_DEBUG_STRING_INFO DebugString
Definition: minwinbase.h:208
DWORD dwDebugEventCode
Definition: minwinbase.h:196
RIP_INFO RipInfo
Definition: minwinbase.h:209
DWORD dwThreadId
Definition: minwinbase.h:198
DWORD dwProcessId
Definition: minwinbase.h:197
EXCEPTION_DEBUG_INFO Exception
Definition: minwinbase.h:201
union _DEBUG_EVENT::@3268 u
CREATE_PROCESS_DEBUG_INFO CreateProcessInfo
Definition: minwinbase.h:203
EXCEPTION_RECORD ExceptionRecord
Definition: minwinbase.h:152
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:45
PVOID ImageBaseAddress
Definition: ntddk_ex.h:245
BYTE BeingDebugged
Definition: btrfs_drv.h:1909
DWORD dwType
Definition: minwinbase.h:191
DWORD dwError
Definition: minwinbase.h:190
Definition: inflate.c:139
Definition: http.c:7252
Definition: stat.h:66
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
int retval
Definition: wcstombs.cpp:91
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
DWORD WINAPI GetLastError(void)
Definition: except.c:1042
HANDLE WINAPI GetCurrentThread(void)
Definition: proc.c:1146
#define CREATE_SUSPENDED
Definition: winbase.h:182
#define DEBUG_PROCESS
Definition: winbase.h:180
#define WINAPI
Definition: msvc.h:6
@ ProcessExecuteFlags
Definition: winternl.h:1916
unsigned char BYTE
Definition: xxhash.c:193