ntdll/tests: Run exception tests under the debugger on all platforms.
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
parent
4f3534fa6f
commit
13abe9e2bd
|
@ -160,6 +160,8 @@ static int (CDECL *p_setjmp)(_JUMP_BUFFER*);
|
|||
static int my_argc;
|
||||
static char** my_argv;
|
||||
static BOOL is_wow64;
|
||||
static BOOL have_vectored_api;
|
||||
static int test_stage;
|
||||
|
||||
#ifdef __i386__
|
||||
|
||||
|
@ -171,8 +173,6 @@ static BOOL is_wow64;
|
|||
#define MEM_EXECUTE_OPTION_PERMANENT 0x08
|
||||
#endif
|
||||
|
||||
static int test_stage;
|
||||
|
||||
/* Test various instruction combinations that cause a protection fault on the i386,
|
||||
* and check what the resulting exception looks like.
|
||||
*/
|
||||
|
@ -325,7 +325,6 @@ static const struct exception
|
|||
};
|
||||
|
||||
static int got_exception;
|
||||
static BOOL have_vectored_api;
|
||||
|
||||
static void run_exception_test(void *handler, const void* context,
|
||||
const void *code, unsigned int code_size,
|
||||
|
@ -1045,11 +1044,10 @@ static void test_debugger(void)
|
|||
{
|
||||
ok((char *)ctx.Eip == (char *)code_mem_address + 0xb, "Eip at %x instead of %p\n",
|
||||
ctx.Eip, (char *)code_mem_address + 0xb);
|
||||
/* setting the context from debugger does not affect the context, the exception handlers gets */
|
||||
/* uncomment once wine is fixed */
|
||||
/* ctx.Eip = 0x12345; */
|
||||
/* setting the context from debugger does not affect the context that the
|
||||
* exception handler gets */
|
||||
ctx.Eip = 0x12345;
|
||||
ctx.Eax = 0xf00f00f1;
|
||||
|
||||
/* let the debuggee handle the exception */
|
||||
continuestatus = DBG_EXCEPTION_NOT_HANDLED;
|
||||
}
|
||||
|
@ -1059,16 +1057,11 @@ static void test_debugger(void)
|
|||
{
|
||||
/* debugger gets first chance exception with unmodified ctx.Eip */
|
||||
ok((char *)ctx.Eip == (char *)code_mem_address + 0xb, "Eip at 0x%x instead of %p\n",
|
||||
ctx.Eip, (char *)code_mem_address + 0xb);
|
||||
|
||||
/* setting the context from debugger does not affect the context, the exception handlers gets */
|
||||
/* uncomment once wine is fixed */
|
||||
/* ctx.Eip = 0x12345; */
|
||||
ctx.Eip, (char *)code_mem_address + 0xb);
|
||||
ctx.Eip = 0x12345;
|
||||
ctx.Eax = 0xf00f00f1;
|
||||
|
||||
/* pass exception to debuggee
|
||||
* exception will not be handled and
|
||||
* a second chance exception will be raised */
|
||||
* exception will not be handled and a second chance exception will be raised */
|
||||
continuestatus = DBG_EXCEPTION_NOT_HANDLED;
|
||||
}
|
||||
else
|
||||
|
@ -2855,6 +2848,218 @@ static void test_dpe_exceptions(void)
|
|||
pRtlRemoveVectoredExceptionHandler(handler);
|
||||
}
|
||||
|
||||
static void test_debugger(void)
|
||||
{
|
||||
char cmdline[MAX_PATH];
|
||||
PROCESS_INFORMATION pi;
|
||||
STARTUPINFOA si = { 0 };
|
||||
DEBUG_EVENT de;
|
||||
DWORD continuestatus;
|
||||
PVOID code_mem_address = NULL;
|
||||
NTSTATUS status;
|
||||
SIZE_T size_read;
|
||||
BOOL ret;
|
||||
int counter = 0;
|
||||
si.cb = sizeof(si);
|
||||
|
||||
if(!pNtGetContextThread || !pNtSetContextThread || !pNtReadVirtualMemory || !pNtTerminateProcess)
|
||||
{
|
||||
skip("NtGetContextThread, NtSetContextThread, NtReadVirtualMemory or NtTerminateProcess not found\n");
|
||||
return;
|
||||
}
|
||||
|
||||
sprintf(cmdline, "%s %s %s %p", my_argv[0], my_argv[1], "debuggee", &test_stage);
|
||||
ret = CreateProcessA(NULL, cmdline, NULL, NULL, FALSE, DEBUG_PROCESS, NULL, NULL, &si, &pi);
|
||||
ok(ret, "could not create child process error: %u\n", GetLastError());
|
||||
if (!ret)
|
||||
return;
|
||||
|
||||
do
|
||||
{
|
||||
continuestatus = DBG_CONTINUE;
|
||||
ok(WaitForDebugEvent(&de, INFINITE), "reading debug event\n");
|
||||
|
||||
ret = ContinueDebugEvent(de.dwProcessId, de.dwThreadId, 0xdeadbeef);
|
||||
ok(!ret, "ContinueDebugEvent unexpectedly succeeded\n");
|
||||
ok(GetLastError() == ERROR_INVALID_PARAMETER, "Unexpected last error: %u\n", GetLastError());
|
||||
|
||||
if (de.dwThreadId != pi.dwThreadId)
|
||||
{
|
||||
trace("event %d not coming from main thread, ignoring\n", de.dwDebugEventCode);
|
||||
ContinueDebugEvent(de.dwProcessId, de.dwThreadId, DBG_CONTINUE);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (de.dwDebugEventCode == CREATE_PROCESS_DEBUG_EVENT)
|
||||
{
|
||||
if(de.u.CreateProcessInfo.lpBaseOfImage != NtCurrentTeb()->Peb->ImageBaseAddress)
|
||||
{
|
||||
skip("child process loaded at different address, terminating it\n");
|
||||
pNtTerminateProcess(pi.hProcess, 0);
|
||||
}
|
||||
}
|
||||
else if (de.dwDebugEventCode == EXCEPTION_DEBUG_EVENT)
|
||||
{
|
||||
CONTEXT ctx;
|
||||
int stage;
|
||||
|
||||
counter++;
|
||||
status = pNtReadVirtualMemory(pi.hProcess, &code_mem, &code_mem_address,
|
||||
sizeof(code_mem_address), &size_read);
|
||||
ok(!status,"NtReadVirtualMemory failed with 0x%x\n", status);
|
||||
status = pNtReadVirtualMemory(pi.hProcess, &test_stage, &stage,
|
||||
sizeof(stage), &size_read);
|
||||
ok(!status,"NtReadVirtualMemory failed with 0x%x\n", status);
|
||||
|
||||
ctx.ContextFlags = CONTEXT_FULL;
|
||||
status = pNtGetContextThread(pi.hThread, &ctx);
|
||||
ok(!status, "NtGetContextThread failed with 0x%x\n", status);
|
||||
|
||||
trace("exception 0x%x at %p firstchance=%d Eip=%p, Eax=%p\n",
|
||||
de.u.Exception.ExceptionRecord.ExceptionCode,
|
||||
de.u.Exception.ExceptionRecord.ExceptionAddress,
|
||||
de.u.Exception.dwFirstChance, (char *)ctx.Rip, (char *)ctx.Rax);
|
||||
|
||||
if (counter > 100)
|
||||
{
|
||||
ok(FALSE, "got way too many exceptions, probably caught in an infinite loop, terminating child\n");
|
||||
pNtTerminateProcess(pi.hProcess, 1);
|
||||
}
|
||||
else if (counter >= 2) /* skip startup breakpoint */
|
||||
{
|
||||
#if 0 /* RtlRaiseException test disabled for now */
|
||||
if (stage == 1)
|
||||
{
|
||||
ok((char *)ctx.Rip == (char *)code_mem_address + 0xb, "Rip at %p instead of %p\n",
|
||||
(char *)ctx.Rip, (char *)code_mem_address + 0xb);
|
||||
/* setting the context from debugger does not affect the context that the
|
||||
* exception handler gets */
|
||||
ctx.Rip = 0x12345;
|
||||
ctx.Rax = 0xf00f00f1;
|
||||
/* let the debuggee handle the exception */
|
||||
continuestatus = DBG_EXCEPTION_NOT_HANDLED;
|
||||
}
|
||||
else if (stage == 2)
|
||||
{
|
||||
if (de.u.Exception.dwFirstChance)
|
||||
{
|
||||
ok((char *)ctx.Rip == (char *)code_mem_address + 0xb, "Rip at %p instead of %p\n",
|
||||
(char *)ctx.Rip, (char *)code_mem_address + 0xb);
|
||||
/* setting the context from debugger does not affect the context that the
|
||||
* exception handler gets */
|
||||
ctx.Rip = 0x12345;
|
||||
ctx.Rax = 0xf00f00f1;
|
||||
/* pass exception to debuggee
|
||||
* exception will not be handled and a second chance exception will be raised */
|
||||
continuestatus = DBG_EXCEPTION_NOT_HANDLED;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* debugger gets context after exception handler has played with it */
|
||||
if (de.u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_BREAKPOINT)
|
||||
{
|
||||
ok((char *)ctx.Rip == (char *)code_mem_address + 0xa,
|
||||
"Rip at %p instead of %p\n",
|
||||
(char *)ctx.Rip, (char *)code_mem_address + 0xa);
|
||||
ctx.Rip += 1;
|
||||
}
|
||||
else ok((char *)ctx.Rip == (char *)code_mem_address + 0xb, "Rip at 0x%x instead of %p\n",
|
||||
ctx.Rip, (char *)code_mem_address + 0xb);
|
||||
/* here we handle exception */
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
if (stage == 7 || stage == 8)
|
||||
{
|
||||
ok(de.u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_BREAKPOINT,
|
||||
"expected EXCEPTION_BREAKPOINT, got %08x\n", de.u.Exception.ExceptionRecord.ExceptionCode);
|
||||
ok((char *)ctx.Rip == (char *)code_mem_address + 0x30,
|
||||
"expected Rip = %p, got %p\n", (char *)code_mem_address + 0x30, (char *)ctx.Rip);
|
||||
if (stage == 8) continuestatus = DBG_EXCEPTION_NOT_HANDLED;
|
||||
}
|
||||
else if (stage == 9 || stage == 10)
|
||||
{
|
||||
ok(de.u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_BREAKPOINT,
|
||||
"expected EXCEPTION_BREAKPOINT, got %08x\n", de.u.Exception.ExceptionRecord.ExceptionCode);
|
||||
ok((char *)ctx.Rip == (char *)code_mem_address + 2,
|
||||
"expected Rip = %p, got %p\n", (char *)code_mem_address + 2, (char *)ctx.Rip);
|
||||
if (stage == 10) continuestatus = DBG_EXCEPTION_NOT_HANDLED;
|
||||
}
|
||||
else if (stage == 11 || stage == 12 || stage == 13)
|
||||
{
|
||||
ok(de.u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_INVALID_HANDLE,
|
||||
"unexpected exception code %08x, expected %08x\n", de.u.Exception.ExceptionRecord.ExceptionCode,
|
||||
EXCEPTION_INVALID_HANDLE);
|
||||
ok(de.u.Exception.ExceptionRecord.NumberParameters == 0,
|
||||
"unexpected number of parameters %d, expected 0\n", de.u.Exception.ExceptionRecord.NumberParameters);
|
||||
|
||||
if (stage == 12|| stage == 13) continuestatus = DBG_EXCEPTION_NOT_HANDLED;
|
||||
}
|
||||
else
|
||||
ok(FALSE, "unexpected stage %x\n", stage);
|
||||
|
||||
status = pNtSetContextThread(pi.hThread, &ctx);
|
||||
ok(!status, "NtSetContextThread failed with 0x%x\n", status);
|
||||
}
|
||||
}
|
||||
else if (de.dwDebugEventCode == OUTPUT_DEBUG_STRING_EVENT)
|
||||
{
|
||||
int stage;
|
||||
char buffer[64];
|
||||
|
||||
status = pNtReadVirtualMemory(pi.hProcess, &test_stage, &stage,
|
||||
sizeof(stage), &size_read);
|
||||
ok(!status,"NtReadVirtualMemory failed with 0x%x\n", status);
|
||||
|
||||
ok(!de.u.DebugString.fUnicode, "unexpected unicode debug string event\n");
|
||||
ok(de.u.DebugString.nDebugStringLength < sizeof(buffer) - 1, "buffer not large enough to hold %d bytes\n",
|
||||
de.u.DebugString.nDebugStringLength);
|
||||
|
||||
memset(buffer, 0, sizeof(buffer));
|
||||
status = pNtReadVirtualMemory(pi.hProcess, de.u.DebugString.lpDebugStringData, buffer,
|
||||
de.u.DebugString.nDebugStringLength, &size_read);
|
||||
ok(!status,"NtReadVirtualMemory failed with 0x%x\n", status);
|
||||
|
||||
if (stage == 3 || stage == 4)
|
||||
ok(!strcmp(buffer, "Hello World"), "got unexpected debug string '%s'\n", buffer);
|
||||
else /* ignore unrelated debug strings like 'SHIMVIEW: ShimInfo(Complete)' */
|
||||
ok(strstr(buffer, "SHIMVIEW") != NULL, "unexpected stage %x, got debug string event '%s'\n", stage, buffer);
|
||||
|
||||
if (stage == 4) continuestatus = DBG_EXCEPTION_NOT_HANDLED;
|
||||
}
|
||||
else if (de.dwDebugEventCode == RIP_EVENT)
|
||||
{
|
||||
int stage;
|
||||
|
||||
status = pNtReadVirtualMemory(pi.hProcess, &test_stage, &stage,
|
||||
sizeof(stage), &size_read);
|
||||
ok(!status,"NtReadVirtualMemory failed with 0x%x\n", status);
|
||||
|
||||
if (stage == 5 || stage == 6)
|
||||
{
|
||||
ok(de.u.RipInfo.dwError == 0x11223344, "got unexpected rip error code %08x, expected %08x\n",
|
||||
de.u.RipInfo.dwError, 0x11223344);
|
||||
ok(de.u.RipInfo.dwType == 0x55667788, "got unexpected rip type %08x, expected %08x\n",
|
||||
de.u.RipInfo.dwType, 0x55667788);
|
||||
}
|
||||
else
|
||||
ok(FALSE, "unexpected stage %x\n", stage);
|
||||
|
||||
if (stage == 6) continuestatus = DBG_EXCEPTION_NOT_HANDLED;
|
||||
}
|
||||
|
||||
ContinueDebugEvent(de.dwProcessId, de.dwThreadId, continuestatus);
|
||||
|
||||
} while (de.dwDebugEventCode != EXIT_PROCESS_DEBUG_EVENT);
|
||||
|
||||
wait_child_process( pi.hProcess );
|
||||
ret = CloseHandle(pi.hThread);
|
||||
ok(ret, "error %u\n", GetLastError());
|
||||
ret = CloseHandle(pi.hProcess);
|
||||
ok(ret, "error %u\n", GetLastError());
|
||||
}
|
||||
|
||||
static void test_thread_context(void)
|
||||
{
|
||||
CONTEXT context;
|
||||
|
@ -3442,6 +3647,223 @@ static void test_thread_context(void)
|
|||
#undef COMPARE
|
||||
}
|
||||
|
||||
static void test_debugger(void)
|
||||
{
|
||||
char cmdline[MAX_PATH];
|
||||
PROCESS_INFORMATION pi;
|
||||
STARTUPINFOA si = { 0 };
|
||||
DEBUG_EVENT de;
|
||||
DWORD continuestatus;
|
||||
PVOID code_mem_address = NULL;
|
||||
NTSTATUS status;
|
||||
SIZE_T size_read;
|
||||
BOOL ret;
|
||||
int counter = 0;
|
||||
si.cb = sizeof(si);
|
||||
|
||||
if(!pNtGetContextThread || !pNtSetContextThread || !pNtReadVirtualMemory || !pNtTerminateProcess)
|
||||
{
|
||||
skip("NtGetContextThread, NtSetContextThread, NtReadVirtualMemory or NtTerminateProcess not found\n");
|
||||
return;
|
||||
}
|
||||
|
||||
sprintf(cmdline, "%s %s %s %p", my_argv[0], my_argv[1], "debuggee", &test_stage);
|
||||
ret = CreateProcessA(NULL, cmdline, NULL, NULL, FALSE, DEBUG_PROCESS, NULL, NULL, &si, &pi);
|
||||
ok(ret, "could not create child process error: %u\n", GetLastError());
|
||||
if (!ret)
|
||||
return;
|
||||
|
||||
do
|
||||
{
|
||||
continuestatus = DBG_CONTINUE;
|
||||
ok(WaitForDebugEvent(&de, INFINITE), "reading debug event\n");
|
||||
|
||||
ret = ContinueDebugEvent(de.dwProcessId, de.dwThreadId, 0xdeadbeef);
|
||||
ok(!ret, "ContinueDebugEvent unexpectedly succeeded\n");
|
||||
ok(GetLastError() == ERROR_INVALID_PARAMETER, "Unexpected last error: %u\n", GetLastError());
|
||||
|
||||
if (de.dwThreadId != pi.dwThreadId)
|
||||
{
|
||||
trace("event %d not coming from main thread, ignoring\n", de.dwDebugEventCode);
|
||||
ContinueDebugEvent(de.dwProcessId, de.dwThreadId, DBG_CONTINUE);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (de.dwDebugEventCode == CREATE_PROCESS_DEBUG_EVENT)
|
||||
{
|
||||
if(de.u.CreateProcessInfo.lpBaseOfImage != NtCurrentTeb()->Peb->ImageBaseAddress)
|
||||
{
|
||||
skip("child process loaded at different address, terminating it\n");
|
||||
pNtTerminateProcess(pi.hProcess, 0);
|
||||
}
|
||||
}
|
||||
else if (de.dwDebugEventCode == EXCEPTION_DEBUG_EVENT)
|
||||
{
|
||||
CONTEXT ctx;
|
||||
int stage;
|
||||
|
||||
counter++;
|
||||
status = pNtReadVirtualMemory(pi.hProcess, &code_mem, &code_mem_address,
|
||||
sizeof(code_mem_address), &size_read);
|
||||
ok(!status,"NtReadVirtualMemory failed with 0x%x\n", status);
|
||||
status = pNtReadVirtualMemory(pi.hProcess, &test_stage, &stage,
|
||||
sizeof(stage), &size_read);
|
||||
ok(!status,"NtReadVirtualMemory failed with 0x%x\n", status);
|
||||
|
||||
ctx.ContextFlags = CONTEXT_FULL;
|
||||
status = pNtGetContextThread(pi.hThread, &ctx);
|
||||
ok(!status, "NtGetContextThread failed with 0x%x\n", status);
|
||||
|
||||
trace("exception 0x%x at %p firstchance=%d pc=%08x, r0=%08x\n",
|
||||
de.u.Exception.ExceptionRecord.ExceptionCode,
|
||||
de.u.Exception.ExceptionRecord.ExceptionAddress,
|
||||
de.u.Exception.dwFirstChance, ctx.Pc, ctx.R0);
|
||||
|
||||
if (counter > 100)
|
||||
{
|
||||
ok(FALSE, "got way too many exceptions, probably caught in an infinite loop, terminating child\n");
|
||||
pNtTerminateProcess(pi.hProcess, 1);
|
||||
}
|
||||
else if (counter >= 2) /* skip startup breakpoint */
|
||||
{
|
||||
#if 0 /* RtlRaiseException test disabled for now */
|
||||
if (stage == 1)
|
||||
{
|
||||
ok((char *)ctx.Pc == (char *)code_mem_address + 0xb, "Pc at %x instead of %p\n",
|
||||
ctx.Pc, (char *)code_mem_address + 0xb);
|
||||
/* setting the context from debugger does not affect the context that the
|
||||
* exception handler gets */
|
||||
ctx.Pc = 0x12345;
|
||||
ctx.R0 = 0xf00f00f1;
|
||||
/* let the debuggee handle the exception */
|
||||
continuestatus = DBG_EXCEPTION_NOT_HANDLED;
|
||||
}
|
||||
else if (stage == 2)
|
||||
{
|
||||
if (de.u.Exception.dwFirstChance)
|
||||
{
|
||||
/* debugger gets first chance exception with unmodified ctx.Pc */
|
||||
ok((char *)ctx.Pc == (char *)code_mem_address + 0xb, "Pc at 0x%x instead of %p\n",
|
||||
ctx.Pc, (char *)code_mem_address + 0xb);
|
||||
ctx.Pc = 0x12345;
|
||||
ctx.R0 = 0xf00f00f1;
|
||||
/* pass exception to debuggee
|
||||
* exception will not be handled and a second chance exception will be raised */
|
||||
continuestatus = DBG_EXCEPTION_NOT_HANDLED;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* debugger gets context after exception handler has played with it */
|
||||
/* ctx.Pc is the same value the exception handler got */
|
||||
if (de.u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_BREAKPOINT)
|
||||
{
|
||||
ok((char *)ctx.Pc == (char *)code_mem_address + 0xa,
|
||||
"Pc at 0x%x instead of %p\n", ctx.Pc, (char *)code_mem_address + 0xa);
|
||||
/* need to fixup Pc for debuggee */
|
||||
/*ctx.Pc += 2; */
|
||||
}
|
||||
else ok((char *)ctx.Pc == (char *)code_mem_address + 0xb,
|
||||
"Pc at 0x%x instead of %p\n", ctx.Pc, (char *)code_mem_address + 0xb);
|
||||
/* here we handle exception */
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
if (stage == 7 || stage == 8)
|
||||
{
|
||||
ok(de.u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_BREAKPOINT,
|
||||
"expected EXCEPTION_BREAKPOINT, got %08x\n", de.u.Exception.ExceptionRecord.ExceptionCode);
|
||||
ok((char *)ctx.Pc == (char *)code_mem_address + 0x1d,
|
||||
"expected Pc = %p, got 0x%x\n", (char *)code_mem_address + 0x1d, ctx.Pc);
|
||||
if (stage == 8) continuestatus = DBG_EXCEPTION_NOT_HANDLED;
|
||||
}
|
||||
else if (stage == 9 || stage == 10)
|
||||
{
|
||||
ok(de.u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_BREAKPOINT,
|
||||
"expected EXCEPTION_BREAKPOINT, got %08x\n", de.u.Exception.ExceptionRecord.ExceptionCode);
|
||||
ok((char *)ctx.Pc == (char *)code_mem_address + 4,
|
||||
"expected Pc = %p, got 0x%x\n", (char *)code_mem_address + 4, ctx.Pc);
|
||||
if (stage == 10) continuestatus = DBG_EXCEPTION_NOT_HANDLED;
|
||||
}
|
||||
else if (stage == 11 || stage == 12 || stage == 13)
|
||||
{
|
||||
ok(de.u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_INVALID_HANDLE,
|
||||
"unexpected exception code %08x, expected %08x\n", de.u.Exception.ExceptionRecord.ExceptionCode,
|
||||
EXCEPTION_INVALID_HANDLE);
|
||||
ok(de.u.Exception.ExceptionRecord.NumberParameters == 0,
|
||||
"unexpected number of parameters %d, expected 0\n", de.u.Exception.ExceptionRecord.NumberParameters);
|
||||
|
||||
if (stage == 12|| stage == 13) continuestatus = DBG_EXCEPTION_NOT_HANDLED;
|
||||
}
|
||||
else
|
||||
ok(FALSE, "unexpected stage %x\n", stage);
|
||||
|
||||
status = pNtSetContextThread(pi.hThread, &ctx);
|
||||
ok(!status, "NtSetContextThread failed with 0x%x\n", status);
|
||||
}
|
||||
}
|
||||
else if (de.dwDebugEventCode == OUTPUT_DEBUG_STRING_EVENT)
|
||||
{
|
||||
int stage;
|
||||
char buffer[64];
|
||||
|
||||
status = pNtReadVirtualMemory(pi.hProcess, &test_stage, &stage,
|
||||
sizeof(stage), &size_read);
|
||||
ok(!status,"NtReadVirtualMemory failed with 0x%x\n", status);
|
||||
|
||||
ok(!de.u.DebugString.fUnicode, "unexpected unicode debug string event\n");
|
||||
ok(de.u.DebugString.nDebugStringLength < sizeof(buffer) - 1, "buffer not large enough to hold %d bytes\n",
|
||||
de.u.DebugString.nDebugStringLength);
|
||||
|
||||
memset(buffer, 0, sizeof(buffer));
|
||||
status = pNtReadVirtualMemory(pi.hProcess, de.u.DebugString.lpDebugStringData, buffer,
|
||||
de.u.DebugString.nDebugStringLength, &size_read);
|
||||
ok(!status,"NtReadVirtualMemory failed with 0x%x\n", status);
|
||||
|
||||
if (stage == 3 || stage == 4)
|
||||
ok(!strcmp(buffer, "Hello World"), "got unexpected debug string '%s'\n", buffer);
|
||||
else /* ignore unrelated debug strings like 'SHIMVIEW: ShimInfo(Complete)' */
|
||||
ok(strstr(buffer, "SHIMVIEW") != NULL, "unexpected stage %x, got debug string event '%s'\n", stage, buffer);
|
||||
|
||||
if (stage == 4) continuestatus = DBG_EXCEPTION_NOT_HANDLED;
|
||||
}
|
||||
else if (de.dwDebugEventCode == RIP_EVENT)
|
||||
{
|
||||
int stage;
|
||||
|
||||
status = pNtReadVirtualMemory(pi.hProcess, &test_stage, &stage,
|
||||
sizeof(stage), &size_read);
|
||||
ok(!status,"NtReadVirtualMemory failed with 0x%x\n", status);
|
||||
|
||||
if (stage == 5 || stage == 6)
|
||||
{
|
||||
ok(de.u.RipInfo.dwError == 0x11223344, "got unexpected rip error code %08x, expected %08x\n",
|
||||
de.u.RipInfo.dwError, 0x11223344);
|
||||
ok(de.u.RipInfo.dwType == 0x55667788, "got unexpected rip type %08x, expected %08x\n",
|
||||
de.u.RipInfo.dwType, 0x55667788);
|
||||
}
|
||||
else
|
||||
ok(FALSE, "unexpected stage %x\n", stage);
|
||||
|
||||
if (stage == 6) continuestatus = DBG_EXCEPTION_NOT_HANDLED;
|
||||
}
|
||||
|
||||
ContinueDebugEvent(de.dwProcessId, de.dwThreadId, continuestatus);
|
||||
|
||||
} while (de.dwDebugEventCode != EXIT_PROCESS_DEBUG_EVENT);
|
||||
|
||||
wait_child_process( pi.hProcess );
|
||||
ret = CloseHandle(pi.hThread);
|
||||
ok(ret, "error %u\n", GetLastError());
|
||||
ret = CloseHandle(pi.hProcess);
|
||||
ok(ret, "error %u\n", GetLastError());
|
||||
}
|
||||
|
||||
static void test_debug_service(DWORD numexc)
|
||||
{
|
||||
/* not supported */
|
||||
}
|
||||
|
||||
#elif defined(__aarch64__)
|
||||
|
||||
static void test_thread_context(void)
|
||||
|
@ -3603,6 +4025,223 @@ static void test_thread_context(void)
|
|||
#undef COMPARE
|
||||
}
|
||||
|
||||
static void test_debugger(void)
|
||||
{
|
||||
char cmdline[MAX_PATH];
|
||||
PROCESS_INFORMATION pi;
|
||||
STARTUPINFOA si = { 0 };
|
||||
DEBUG_EVENT de;
|
||||
DWORD continuestatus;
|
||||
PVOID code_mem_address = NULL;
|
||||
NTSTATUS status;
|
||||
SIZE_T size_read;
|
||||
BOOL ret;
|
||||
int counter = 0;
|
||||
si.cb = sizeof(si);
|
||||
|
||||
if(!pNtGetContextThread || !pNtSetContextThread || !pNtReadVirtualMemory || !pNtTerminateProcess)
|
||||
{
|
||||
skip("NtGetContextThread, NtSetContextThread, NtReadVirtualMemory or NtTerminateProcess not found\n");
|
||||
return;
|
||||
}
|
||||
|
||||
sprintf(cmdline, "%s %s %s %p", my_argv[0], my_argv[1], "debuggee", &test_stage);
|
||||
ret = CreateProcessA(NULL, cmdline, NULL, NULL, FALSE, DEBUG_PROCESS, NULL, NULL, &si, &pi);
|
||||
ok(ret, "could not create child process error: %u\n", GetLastError());
|
||||
if (!ret)
|
||||
return;
|
||||
|
||||
do
|
||||
{
|
||||
continuestatus = DBG_CONTINUE;
|
||||
ok(WaitForDebugEvent(&de, INFINITE), "reading debug event\n");
|
||||
|
||||
ret = ContinueDebugEvent(de.dwProcessId, de.dwThreadId, 0xdeadbeef);
|
||||
ok(!ret, "ContinueDebugEvent unexpectedly succeeded\n");
|
||||
ok(GetLastError() == ERROR_INVALID_PARAMETER, "Unexpected last error: %u\n", GetLastError());
|
||||
|
||||
if (de.dwThreadId != pi.dwThreadId)
|
||||
{
|
||||
trace("event %d not coming from main thread, ignoring\n", de.dwDebugEventCode);
|
||||
ContinueDebugEvent(de.dwProcessId, de.dwThreadId, DBG_CONTINUE);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (de.dwDebugEventCode == CREATE_PROCESS_DEBUG_EVENT)
|
||||
{
|
||||
if(de.u.CreateProcessInfo.lpBaseOfImage != NtCurrentTeb()->Peb->ImageBaseAddress)
|
||||
{
|
||||
skip("child process loaded at different address, terminating it\n");
|
||||
pNtTerminateProcess(pi.hProcess, 0);
|
||||
}
|
||||
}
|
||||
else if (de.dwDebugEventCode == EXCEPTION_DEBUG_EVENT)
|
||||
{
|
||||
CONTEXT ctx;
|
||||
int stage;
|
||||
|
||||
counter++;
|
||||
status = pNtReadVirtualMemory(pi.hProcess, &code_mem, &code_mem_address,
|
||||
sizeof(code_mem_address), &size_read);
|
||||
ok(!status,"NtReadVirtualMemory failed with 0x%x\n", status);
|
||||
status = pNtReadVirtualMemory(pi.hProcess, &test_stage, &stage,
|
||||
sizeof(stage), &size_read);
|
||||
ok(!status,"NtReadVirtualMemory failed with 0x%x\n", status);
|
||||
|
||||
ctx.ContextFlags = CONTEXT_FULL;
|
||||
status = pNtGetContextThread(pi.hThread, &ctx);
|
||||
ok(!status, "NtGetContextThread failed with 0x%x\n", status);
|
||||
|
||||
trace("exception 0x%x at %p firstchance=%d pc=%p, x0=%p\n",
|
||||
de.u.Exception.ExceptionRecord.ExceptionCode,
|
||||
de.u.Exception.ExceptionRecord.ExceptionAddress,
|
||||
de.u.Exception.dwFirstChance, (char *)ctx.Pc, (char *)ctx.X0);
|
||||
|
||||
if (counter > 100)
|
||||
{
|
||||
ok(FALSE, "got way too many exceptions, probably caught in an infinite loop, terminating child\n");
|
||||
pNtTerminateProcess(pi.hProcess, 1);
|
||||
}
|
||||
else if (counter >= 2) /* skip startup breakpoint */
|
||||
{
|
||||
#if 0 /* RtlRaiseException test disabled for now */
|
||||
if (stage == 1)
|
||||
{
|
||||
ok((char *)ctx.Pc == (char *)code_mem_address + 0xb, "Pc at %p instead of %p\n",
|
||||
(char *)ctx.Pc, (char *)code_mem_address + 0xb);
|
||||
/* setting the context from debugger does not affect the context that the
|
||||
* exception handler gets */
|
||||
ctx.Pc = 0x12345;
|
||||
ctx.X0 = 0xf00f00f1;
|
||||
/* let the debuggee handle the exception */
|
||||
continuestatus = DBG_EXCEPTION_NOT_HANDLED;
|
||||
}
|
||||
else if (stage == 2)
|
||||
{
|
||||
if (de.u.Exception.dwFirstChance)
|
||||
{
|
||||
/* debugger gets first chance exception with unmodified ctx.Pc */
|
||||
ok((char *)ctx.Pc == (char *)code_mem_address + 0xb, "Pc at %p instead of %p\n",
|
||||
(char *)ctx.Pc, (char *)code_mem_address + 0xb);
|
||||
ctx.Pc = 0x12345;
|
||||
ctx.X0 = 0xf00f00f1;
|
||||
/* pass exception to debuggee
|
||||
* exception will not be handled and a second chance exception will be raised */
|
||||
continuestatus = DBG_EXCEPTION_NOT_HANDLED;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* debugger gets context after exception handler has played with it */
|
||||
/* ctx.Pc is the same value the exception handler got */
|
||||
if (de.u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_BREAKPOINT)
|
||||
{
|
||||
ok((char *)ctx.Pc == (char *)code_mem_address + 0xa,
|
||||
"Pc at %p instead of %p\n", (char *)ctx.Pc, (char *)code_mem_address + 0xa);
|
||||
/* need to fixup Pc for debuggee */
|
||||
ctx.Pc += 4;
|
||||
}
|
||||
else ok((char *)ctx.Pc == (char *)code_mem_address + 0xb,
|
||||
"Pc at 0x%x instead of %p\n", ctx.Pc, (char *)code_mem_address + 0xb);
|
||||
/* here we handle exception */
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
if (stage == 7 || stage == 8)
|
||||
{
|
||||
ok(de.u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_BREAKPOINT,
|
||||
"expected EXCEPTION_BREAKPOINT, got %08x\n", de.u.Exception.ExceptionRecord.ExceptionCode);
|
||||
ok((char *)ctx.Pc == (char *)code_mem_address + 0x1d,
|
||||
"expected Pc = %p, got %p\n", (char *)code_mem_address + 0x1d, (char *)ctx.Pc);
|
||||
if (stage == 8) continuestatus = DBG_EXCEPTION_NOT_HANDLED;
|
||||
}
|
||||
else if (stage == 9 || stage == 10)
|
||||
{
|
||||
ok(de.u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_BREAKPOINT,
|
||||
"expected EXCEPTION_BREAKPOINT, got %08x\n", de.u.Exception.ExceptionRecord.ExceptionCode);
|
||||
ok((char *)ctx.Pc == (char *)code_mem_address + 4,
|
||||
"expected Pc = %p, got %p\n", (char *)code_mem_address + 4, (char *)ctx.Pc);
|
||||
if (stage == 10) continuestatus = DBG_EXCEPTION_NOT_HANDLED;
|
||||
}
|
||||
else if (stage == 11 || stage == 12 || stage == 13)
|
||||
{
|
||||
ok(de.u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_INVALID_HANDLE,
|
||||
"unexpected exception code %08x, expected %08x\n", de.u.Exception.ExceptionRecord.ExceptionCode,
|
||||
EXCEPTION_INVALID_HANDLE);
|
||||
ok(de.u.Exception.ExceptionRecord.NumberParameters == 0,
|
||||
"unexpected number of parameters %d, expected 0\n", de.u.Exception.ExceptionRecord.NumberParameters);
|
||||
|
||||
if (stage == 12|| stage == 13) continuestatus = DBG_EXCEPTION_NOT_HANDLED;
|
||||
}
|
||||
else
|
||||
ok(FALSE, "unexpected stage %x\n", stage);
|
||||
|
||||
status = pNtSetContextThread(pi.hThread, &ctx);
|
||||
ok(!status, "NtSetContextThread failed with 0x%x\n", status);
|
||||
}
|
||||
}
|
||||
else if (de.dwDebugEventCode == OUTPUT_DEBUG_STRING_EVENT)
|
||||
{
|
||||
int stage;
|
||||
char buffer[64];
|
||||
|
||||
status = pNtReadVirtualMemory(pi.hProcess, &test_stage, &stage,
|
||||
sizeof(stage), &size_read);
|
||||
ok(!status,"NtReadVirtualMemory failed with 0x%x\n", status);
|
||||
|
||||
ok(!de.u.DebugString.fUnicode, "unexpected unicode debug string event\n");
|
||||
ok(de.u.DebugString.nDebugStringLength < sizeof(buffer) - 1, "buffer not large enough to hold %d bytes\n",
|
||||
de.u.DebugString.nDebugStringLength);
|
||||
|
||||
memset(buffer, 0, sizeof(buffer));
|
||||
status = pNtReadVirtualMemory(pi.hProcess, de.u.DebugString.lpDebugStringData, buffer,
|
||||
de.u.DebugString.nDebugStringLength, &size_read);
|
||||
ok(!status,"NtReadVirtualMemory failed with 0x%x\n", status);
|
||||
|
||||
if (stage == 3 || stage == 4)
|
||||
ok(!strcmp(buffer, "Hello World"), "got unexpected debug string '%s'\n", buffer);
|
||||
else /* ignore unrelated debug strings like 'SHIMVIEW: ShimInfo(Complete)' */
|
||||
ok(strstr(buffer, "SHIMVIEW") != NULL, "unexpected stage %x, got debug string event '%s'\n", stage, buffer);
|
||||
|
||||
if (stage == 4) continuestatus = DBG_EXCEPTION_NOT_HANDLED;
|
||||
}
|
||||
else if (de.dwDebugEventCode == RIP_EVENT)
|
||||
{
|
||||
int stage;
|
||||
|
||||
status = pNtReadVirtualMemory(pi.hProcess, &test_stage, &stage,
|
||||
sizeof(stage), &size_read);
|
||||
ok(!status,"NtReadVirtualMemory failed with 0x%x\n", status);
|
||||
|
||||
if (stage == 5 || stage == 6)
|
||||
{
|
||||
ok(de.u.RipInfo.dwError == 0x11223344, "got unexpected rip error code %08x, expected %08x\n",
|
||||
de.u.RipInfo.dwError, 0x11223344);
|
||||
ok(de.u.RipInfo.dwType == 0x55667788, "got unexpected rip type %08x, expected %08x\n",
|
||||
de.u.RipInfo.dwType, 0x55667788);
|
||||
}
|
||||
else
|
||||
ok(FALSE, "unexpected stage %x\n", stage);
|
||||
|
||||
if (stage == 6) continuestatus = DBG_EXCEPTION_NOT_HANDLED;
|
||||
}
|
||||
|
||||
ContinueDebugEvent(de.dwProcessId, de.dwThreadId, continuestatus);
|
||||
|
||||
} while (de.dwDebugEventCode != EXIT_PROCESS_DEBUG_EVENT);
|
||||
|
||||
wait_child_process( pi.hProcess );
|
||||
ret = CloseHandle(pi.hThread);
|
||||
ok(ret, "error %u\n", GetLastError());
|
||||
ret = CloseHandle(pi.hProcess);
|
||||
ok(ret, "error %u\n", GetLastError());
|
||||
}
|
||||
|
||||
static void test_debug_service(DWORD numexc)
|
||||
{
|
||||
/* not supported */
|
||||
}
|
||||
|
||||
#endif /* __aarch64__ */
|
||||
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
|
@ -3992,21 +4631,19 @@ static LONG CALLBACK breakpoint_handler(EXCEPTION_POINTERS *ExceptionInfo)
|
|||
"got ExceptionInformation[0] = %lx\n", rec->ExceptionInformation[0]);
|
||||
ExceptionInfo->ContextRecord->Rip = (DWORD_PTR)code_mem + 2;
|
||||
#elif defined(__arm__)
|
||||
ok(ExceptionInfo->ContextRecord->Pc == (DWORD)code_mem,
|
||||
"expected pc = %lx, got %lx\n", (DWORD)code_mem, ExceptionInfo->ContextRecord->Pc);
|
||||
ok(ExceptionInfo->ContextRecord->Pc == (DWORD)code_mem + 4,
|
||||
"expected pc = %lx, got %lx\n", (DWORD)code_mem + 4, ExceptionInfo->ContextRecord->Pc);
|
||||
ok(rec->NumberParameters == 1,
|
||||
"ExceptionParameters is %d instead of 1\n", rec->NumberParameters);
|
||||
ok(rec->ExceptionInformation[0] == 0,
|
||||
"got ExceptionInformation[0] = %lx\n", rec->ExceptionInformation[0]);
|
||||
ExceptionInfo->ContextRecord->Pc = (DWORD)code_mem + 4;
|
||||
#elif defined(__aarch64__)
|
||||
ok(ExceptionInfo->ContextRecord->Pc == (DWORD_PTR)code_mem,
|
||||
"expected pc = %lx, got %lx\n", (DWORD_PTR)code_mem, ExceptionInfo->ContextRecord->Pc);
|
||||
ok(ExceptionInfo->ContextRecord->Pc == (DWORD_PTR)code_mem + 4,
|
||||
"expected pc = %lx, got %lx\n", (DWORD_PTR)code_mem + 4, ExceptionInfo->ContextRecord->Pc);
|
||||
ok(rec->NumberParameters == 1,
|
||||
"ExceptionParameters is %d instead of 1\n", rec->NumberParameters);
|
||||
ok(rec->ExceptionInformation[0] == 0,
|
||||
"got ExceptionInformation[0] = %lx\n", rec->ExceptionInformation[0]);
|
||||
ExceptionInfo->ContextRecord->Pc = (DWORD_PTR)code_mem + 4;
|
||||
#endif
|
||||
|
||||
breakpoint_exceptions++;
|
||||
|
@ -4470,8 +5107,6 @@ START_TEST(exception)
|
|||
#undef X
|
||||
|
||||
pIsWow64Process = (void *)GetProcAddress(GetModuleHandleA("kernel32.dll"), "IsWow64Process");
|
||||
|
||||
#ifdef __i386__
|
||||
if (!pIsWow64Process || !pIsWow64Process( GetCurrentProcess(), &is_wow64 )) is_wow64 = FALSE;
|
||||
|
||||
if (pRtlAddVectoredExceptionHandler && pRtlRemoveVectoredExceptionHandler)
|
||||
|
@ -4498,6 +5133,7 @@ START_TEST(exception)
|
|||
return;
|
||||
}
|
||||
|
||||
#ifdef __i386__
|
||||
if (pRtlRaiseException)
|
||||
{
|
||||
test_stage = 1;
|
||||
|
@ -4508,42 +5144,43 @@ START_TEST(exception)
|
|||
run_rtlraiseexception_test(0x12345);
|
||||
run_rtlraiseexception_test(EXCEPTION_BREAKPOINT);
|
||||
run_rtlraiseexception_test(EXCEPTION_INVALID_HANDLE);
|
||||
test_stage = 3;
|
||||
test_outputdebugstring(0, FALSE);
|
||||
test_stage = 4;
|
||||
test_outputdebugstring(2, TRUE); /* is this a Windows bug? */
|
||||
test_stage = 5;
|
||||
test_ripevent(0);
|
||||
test_stage = 6;
|
||||
test_ripevent(1);
|
||||
test_stage = 7;
|
||||
test_debug_service(0);
|
||||
test_stage = 8;
|
||||
test_debug_service(1);
|
||||
test_stage = 9;
|
||||
test_breakpoint(0);
|
||||
test_stage = 10;
|
||||
test_breakpoint(1);
|
||||
test_stage = 11;
|
||||
test_closehandle(0, (HANDLE)0xdeadbeef);
|
||||
test_stage = 12;
|
||||
test_closehandle(1, (HANDLE)0xdeadbeef);
|
||||
test_stage = 13;
|
||||
test_closehandle(0, 0); /* Special case. */
|
||||
}
|
||||
else
|
||||
skip( "RtlRaiseException not found\n" );
|
||||
else skip( "RtlRaiseException not found\n" );
|
||||
#endif
|
||||
test_stage = 3;
|
||||
test_outputdebugstring(0, FALSE);
|
||||
test_stage = 4;
|
||||
test_outputdebugstring(2, TRUE); /* is this a Windows bug? */
|
||||
test_stage = 5;
|
||||
test_ripevent(0);
|
||||
test_stage = 6;
|
||||
test_ripevent(1);
|
||||
test_stage = 7;
|
||||
test_debug_service(0);
|
||||
test_stage = 8;
|
||||
test_debug_service(1);
|
||||
test_stage = 9;
|
||||
test_breakpoint(0);
|
||||
test_stage = 10;
|
||||
test_breakpoint(1);
|
||||
test_stage = 11;
|
||||
test_closehandle(0, (HANDLE)0xdeadbeef);
|
||||
test_stage = 12;
|
||||
test_closehandle(1, (HANDLE)0xdeadbeef);
|
||||
test_stage = 13;
|
||||
test_closehandle(0, 0); /* Special case. */
|
||||
|
||||
/* rest of tests only run in parent */
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef __i386__
|
||||
|
||||
test_unwind();
|
||||
test_exceptions();
|
||||
test_rtlraiseexception();
|
||||
test_debug_registers();
|
||||
test_debug_service(1);
|
||||
test_debugger();
|
||||
test_simd_exceptions();
|
||||
test_fpu_exceptions();
|
||||
test_dpe_exceptions();
|
||||
|
@ -4587,6 +5224,7 @@ START_TEST(exception)
|
|||
skip( "Dynamic unwind functions not found\n" );
|
||||
#endif
|
||||
|
||||
test_debugger();
|
||||
test_thread_context();
|
||||
test_outputdebugstring(1, FALSE);
|
||||
test_ripevent(1);
|
||||
|
|
|
@ -552,6 +552,7 @@ static void setup_exception( ucontext_t *sigcontext, EXCEPTION_RECORD *rec )
|
|||
|
||||
rec->ExceptionAddress = (void *)PC_sig(sigcontext);
|
||||
save_context( &context, sigcontext );
|
||||
if (rec->ExceptionCode == EXCEPTION_BREAKPOINT) context.Pc += 4;
|
||||
|
||||
status = send_debug_event( rec, &context, TRUE );
|
||||
if (status == DBG_CONTINUE || status == DBG_EXCEPTION_HANDLED)
|
||||
|
|
|
@ -554,6 +554,7 @@ static void setup_exception( ucontext_t *sigcontext, EXCEPTION_RECORD *rec )
|
|||
rec->ExceptionAddress = (void *)PC_sig(sigcontext);
|
||||
save_context( &context, sigcontext );
|
||||
save_fpu( &context, sigcontext );
|
||||
if (rec->ExceptionCode == EXCEPTION_BREAKPOINT) context.Pc += 4;
|
||||
|
||||
status = send_debug_event( rec, &context, TRUE );
|
||||
if (status == DBG_CONTINUE || status == DBG_EXCEPTION_HANDLED)
|
||||
|
@ -562,9 +563,6 @@ static void setup_exception( ucontext_t *sigcontext, EXCEPTION_RECORD *rec )
|
|||
return;
|
||||
}
|
||||
|
||||
/* fix up instruction pointer in context for EXCEPTION_BREAKPOINT */
|
||||
if (rec->ExceptionCode == EXCEPTION_BREAKPOINT) context.Pc += 4;
|
||||
|
||||
stack = virtual_setup_exception( stack_ptr, (sizeof(*stack) + 15) & ~15, rec );
|
||||
stack->rec = *rec;
|
||||
stack->context = context;
|
||||
|
|
Loading…
Reference in New Issue