diff --git a/reactos/dll/ntdll/dispatch/arm/stubs_asm.s b/reactos/dll/ntdll/dispatch/arm/stubs_asm.s index f11c598b951..d78a4749820 100644 --- a/reactos/dll/ntdll/dispatch/arm/stubs_asm.s +++ b/reactos/dll/ntdll/dispatch/arm/stubs_asm.s @@ -6,7 +6,6 @@ GENERATE_ARM_STUB LdrInitializeThunk GENERATE_ARM_STUB RtlGetCallersAddress GENERATE_ARM_STUB RtlUnwind -GENERATE_ARM_STUB RtlpGetExceptionAddress GENERATE_ARM_STUB RtlDispatchException GENERATE_ARM_STUB RtlpGetStackLimits GENERATE_ARM_STUB DbgUserBreakPoint @@ -15,4 +14,3 @@ GENERATE_ARM_STUB KiFastSystemCallRet GENERATE_ARM_STUB KiIntSystemCall GENERATE_ARM_STUB KiUserApcDispatcher GENERATE_ARM_STUB RtlInitializeContext - diff --git a/reactos/dll/ntdll/rtl/libsupp.c b/reactos/dll/ntdll/rtl/libsupp.c index 1ef8c26c3ae..3c4e01bba64 100644 --- a/reactos/dll/ntdll/rtl/libsupp.c +++ b/reactos/dll/ntdll/rtl/libsupp.c @@ -33,9 +33,10 @@ RtlWalkFrameChain(OUT PVOID *Callers, BOOLEAN NTAPI -RtlpCheckForActiveDebugger(BOOLEAN Type) +RtlpCheckForActiveDebugger(VOID) { - return (NtCurrentPeb()->BeingDebugged); + /* Return the flag in the PEB */ + return NtCurrentPeb()->BeingDebugged; } BOOLEAN diff --git a/reactos/include/crt/mingw32/intrin_arm.h b/reactos/include/crt/mingw32/intrin_arm.h index 3e788926cbb..066c641254f 100644 --- a/reactos/include/crt/mingw32/intrin_arm.h +++ b/reactos/include/crt/mingw32/intrin_arm.h @@ -32,6 +32,7 @@ #error Unsupported compiler #endif +#define _ReturnAddress() (__builtin_return_address(0)) #define _ReadWriteBarrier() __sync_synchronize() __INTRIN_INLINE char _InterlockedCompareExchange8(volatile char * const Destination, const char Exchange, const char Comperand) diff --git a/reactos/include/ddk/winddk.h b/reactos/include/ddk/winddk.h index 928d4fa60d0..491d2ba8fea 100644 --- a/reactos/include/ddk/winddk.h +++ b/reactos/include/ddk/winddk.h @@ -5796,6 +5796,25 @@ KeAcquireSpinLockRaiseToDpc( #define ROUND_TO_PAGES(Size) \ ((ULONG_PTR) (((ULONG_PTR) Size + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1))) + + +#if defined(_X86_) || defined(_AMD64_) + +// +// x86 and x64 performs a 0x2C interrupt +// +#define DbgRaiseAssertionFailure __int2c + +#elif defined(_ARM_) + +// +// TODO +// + +#else +#error Unsupported Architecture +#endif + #if DBG #define ASSERT(exp) \ @@ -5807,7 +5826,7 @@ KeAcquireSpinLockRaiseToDpc( RtlAssert( #exp, __FILE__, __LINE__, msg ), FALSE : TRUE) #define RTL_SOFT_ASSERT(exp) \ - (VOID)((!(_exp)) ? \ + (VOID)((!(exp)) ? \ DbgPrint("%s(%d): Soft assertion failed\n Expression: %s\n", __FILE__, __LINE__, #exp), FALSE : TRUE) #define RTL_SOFT_ASSERTMSG(msg, exp) \ @@ -5820,6 +5839,36 @@ KeAcquireSpinLockRaiseToDpc( #define RTL_SOFT_VERIFY(exp) RTL_SOFT_ASSERT(exp) #define RTL_SOFT_VERIFYMSG(msg, exp) RTL_SOFT_ASSERTMSG(msg, exp) +#if defined(_MSC_VER) + +#define NT_ASSERT(exp) \ + ((!(exp)) ? \ + (__annotation(L"Debug", L"AssertFail", L#exp), \ + DbgRaiseAssertionFailure(), FALSE) : TRUE) + +#define NT_ASSERTMSG(msg, exp) \ + ((!(exp)) ? \ + (__annotation(L"Debug", L"AssertFail", L##msg), \ + DbgRaiseAssertionFailure(), FALSE) : TRUE) + +#define NT_ASSERTMSGW(msg, exp) \ + ((!(exp)) ? \ + (__annotation(L"Debug", L"AssertFail", msg), \ + DbgRaiseAssertionFailure(), FALSE) : TRUE) + +#else + +// +// GCC doesn't support __annotation (nor PDB) +// +#define NT_ASSERT(exp) \ + (VOID)((!(exp)) ? (DbgRaiseAssertionFailure(), FALSE) : TRUE) + +#define NT_ASSERTMSG NT_ASSERT +#define NT_ASSERTMSGW NT_ASSERT + +#endif + #else /* !DBG */ #define ASSERT(exp) ((VOID) 0) @@ -5834,6 +5883,10 @@ KeAcquireSpinLockRaiseToDpc( #define RTL_SOFT_VERIFY(exp) ((exp) ? TRUE : FALSE) #define RTL_SOFT_VERIFYMSG(msg, exp) ((exp) ? TRUE : FALSE) +#define NT_ASSERT(exp) ((VOID)0) +#define NT_ASSERTMSG(exp) ((VOID)0) +#define NT_ASSERTMSGW(exp) ((VOID)0) + #endif /* DBG */ /* HACK HACK HACK - GCC (or perhaps LD) is messing this up */ diff --git a/reactos/include/ndk/i386/asm.h b/reactos/include/ndk/i386/asm.h index 597010889f6..18908cca4fe 100644 --- a/reactos/include/ndk/i386/asm.h +++ b/reactos/include/ndk/i386/asm.h @@ -376,6 +376,19 @@ Author: #define CONTEXT_FLOAT_SAVE_STATUS_WORD CONTEXT_FLOAT_SAVE + FP_STATUS_WORD #define CONTEXT_FLOAT_SAVE_TAG_WORD CONTEXT_FLOAT_SAVE + FP_TAG_WORD #define CONTEXT_ALIGNED_SIZE 0x2CC +#define CONTEXT_FRAME_LENGTH 0x2D0 + +// +// CONTEXT Flags +// +#ifdef __ASM__ +#define CONTEXT_CONTROL 0x10001 +#define CONTEXT_INTEGER 0x10002 +#define CONTEXT_SEGMENTS 0x10004 +#define CONTEXT_FLOATING_POINT 0x10008 +#define CONTEXT_DEBUG_REGISTERS 0x10010 +#define CONTEXT_FULL 0x10007 +#endif // // EXCEPTION_RECORD Offsets @@ -527,7 +540,6 @@ Author: // NTSTATUS, Bugcheck Codes and Debug Codes // #ifdef __ASM__ -#define STATUS_SUCCESS 0x00000000 #define STATUS_ACCESS_VIOLATION 0xC0000005 #define STATUS_IN_PAGE_ERROR 0xC0000006 #define STATUS_GUARD_PAGE_VIOLATION 0x80000001 @@ -553,6 +565,7 @@ Author: #define STATUS_FLOAT_UNDERFLOW 0xC0000093 #define STATUS_FLOAT_MULTIPLE_FAULTS 0xC00002B4 #define STATUS_FLOAT_MULTIPLE_TRAPS 0xC00002B5 +#define STATUS_ASSERTION_FAILURE 0xC0000420 #define APC_INDEX_MISMATCH 0x01 #define IRQL_NOT_GREATER_OR_EQUAL 0x09 #define IRQL_NOT_LESS_OR_EQUAL 0x0A @@ -564,6 +577,11 @@ Author: #define HARDWARE_INTERRUPT_STORM 0xF2 #define DBG_STATUS_CONTROL_C 0x01 +// +// DebugService Control Types +// +#define BREAKPOINT_BREAK 0x0 + // // IRQL Levels // diff --git a/reactos/lib/rtl/assert.c b/reactos/lib/rtl/assert.c new file mode 100644 index 00000000000..faa65ba1724 --- /dev/null +++ b/reactos/lib/rtl/assert.c @@ -0,0 +1,119 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS Run-Time Library + * PURPOSE: Implements RtlAssert used by the ASSERT + * and ASSERTMSG debugging macros + * FILE: lib/rtl/assert.c + * PROGRAMERS: Stefan Ginsberg (stefan.ginsberg@reactos.org) + */ + +/* INCLUDES *****************************************************************/ + +#include +#define NDEBUG +#include + +/* PUBLIC FUNCTIONS **********************************************************/ + +/* + * @implemented + */ +VOID +NTAPI +RtlAssert(IN PVOID FailedAssertion, + IN PVOID FileName, + IN ULONG LineNumber, + IN PCHAR Message OPTIONAL) +{ +#if 0 // Disabled until sysreg can handle debug prompts + CHAR Action[2]; + CONTEXT Context; + + /* Capture caller's context for the debugger */ + RtlCaptureContext(&Context); + + /* Enter prompt loop */ + for (;;) + { + /* Print the assertion */ + DbgPrint("\n*** Assertion failed: %s%s\n" + "*** Source File: %s, line %ld\n\n", + Message != NULL ? Message : "", + (PSTR)FailedAssertion, + (PSTR)FileName, + LineNumber); + + /* Prompt for action */ + DbgPrompt("Break repeatedly, break Once, Ignore," + " terminate Process or terminate Thread (boipt)? ", + Action, + sizeof(Action)); + switch (Action[0]) + { + /* Break repeatedly */ + case 'B': case 'b': + + /* Do a breakpoint, then prompt again */ + DbgPrint("Execute '.cxr %p' to dump context\n", &Context); + DbgBreakPoint(); + break; + + /* Ignore */ + case 'I': case 'i': + + /* Return to caller */ + return; + + /* Break once */ + case 'O': case 'o': + + /* Do a breakpoint and return */ + DbgPrint("Execute '.cxr %p' to dump context\n", &Context); + DbgBreakPoint(); + return; + + /* Terminate process*/ + case 'P': case 'p': + + /* Terminate us */ + ZwTerminateProcess(ZwCurrentProcess(), STATUS_UNSUCCESSFUL); + break; + + /* Terminate thread */ + case 'T': case 't': + + /* Terminate us */ + ZwTerminateThread(ZwCurrentThread(), STATUS_UNSUCCESSFUL); + break; + + /* Unrecognized */ + default: + + /* Prompt again */ + break; + } + } + + /* Shouldn't get here */ + DbgBreakPoint(); + ZwTerminateProcess(ZwCurrentProcess(), STATUS_UNSUCCESSFUL); +#else + if (NULL != Message) + { + DbgPrint("Assertion \'%s\' failed at %s line %d: %s\n", + (PCHAR)FailedAssertion, + (PCHAR)FileName, + LineNumber, + Message); + } + else + { + DbgPrint("Assertion \'%s\' failed at %s line %d\n", + (PCHAR)FailedAssertion, + (PCHAR)FileName, + LineNumber); + } + + DbgBreakPoint(); +#endif +} diff --git a/reactos/lib/rtl/debug.c b/reactos/lib/rtl/debug.c index 7c42ebca8f4..1ba40c70af9 100644 --- a/reactos/lib/rtl/debug.c +++ b/reactos/lib/rtl/debug.c @@ -22,7 +22,7 @@ DebugPrint(IN PANSI_STRING DebugString, IN ULONG ComponentId, IN ULONG Level) { - /* Call the INT2D Service */ + /* Call the Debug Service */ return DebugService(BREAKPOINT_PRINT, DebugString->Buffer, DebugString->Length, @@ -35,7 +35,7 @@ NTAPI DebugPrompt(IN PCSTRING Output, IN PSTRING Input) { - /* Call the INT2D Service */ + /* Call the Debug Service */ return DebugService(BREAKPOINT_PROMPT, Output->Buffer, Output->Length, @@ -115,7 +115,7 @@ vDbgPrintExWithPrefixInternal(IN LPCSTR Prefix, DebugString.Buffer = Buffer; /* First, let the debugger know as well */ - if (RtlpCheckForActiveDebugger(FALSE)) + if (RtlpCheckForActiveDebugger()) { /* Fill out an exception record */ ExceptionRecord.ExceptionCode = DBG_PRINTEXCEPTION_C; diff --git a/reactos/lib/rtl/error.c b/reactos/lib/rtl/error.c index f81254e3af3..ce35919d6be 100644 --- a/reactos/lib/rtl/error.c +++ b/reactos/lib/rtl/error.c @@ -809,35 +809,6 @@ static const ERROR_TABLE ErrorTable[] = /* FUNCTIONS ***************************************************************/ -/* - * @implemented - */ -VOID -NTAPI -RtlAssert(PVOID FailedAssertion, - PVOID FileName, - ULONG LineNumber, - PCHAR Message) -{ - if (NULL != Message) - { - DbgPrint("Assertion \'%s\' failed at %s line %d: %s\n", - (PCHAR)FailedAssertion, - (PCHAR)FileName, - LineNumber, - Message); - } - else - { - DbgPrint("Assertion \'%s\' failed at %s line %d\n", - (PCHAR)FailedAssertion, - (PCHAR)FileName, - LineNumber); - } - - DbgBreakPoint(); -} - /* * @unimplemented */ diff --git a/reactos/lib/rtl/exception.c b/reactos/lib/rtl/exception.c index 15c1fe8d306..d4462e32111 100644 --- a/reactos/lib/rtl/exception.c +++ b/reactos/lib/rtl/exception.c @@ -17,12 +17,14 @@ /* FUNCTIONS ***************************************************************/ +#if !defined(_M_IX86) + /* * @implemented */ VOID NTAPI -RtlRaiseException(PEXCEPTION_RECORD ExceptionRecord) +RtlRaiseException(IN PEXCEPTION_RECORD ExceptionRecord) { CONTEXT Context; NTSTATUS Status; @@ -30,19 +32,14 @@ RtlRaiseException(PEXCEPTION_RECORD ExceptionRecord) /* Capture the context */ RtlCaptureContext(&Context); -#ifdef _M_IX86 - /* Fixup ESP */ - Context.Esp += sizeof(ULONG); -#endif - /* Save the exception address */ - ExceptionRecord->ExceptionAddress = RtlpGetExceptionAddress(); + ExceptionRecord->ExceptionAddress = _ReturnAddress(); /* Write the context flag */ Context.ContextFlags = CONTEXT_FULL; /* Check if user mode debugger is active */ - if (RtlpCheckForActiveDebugger(FALSE)) + if (RtlpCheckForActiveDebugger()) { /* Raise an exception immediately */ Status = ZwRaiseException(ExceptionRecord, &Context, TRUE); @@ -68,7 +65,7 @@ RtlRaiseException(PEXCEPTION_RECORD ExceptionRecord) #ifdef _MSC_VER #pragma warning(push) -#pragma warning(disable:4717) +#pragma warning(disable:4717) // RtlRaiseStatus is recursive by design #endif /* @@ -76,7 +73,7 @@ RtlRaiseException(PEXCEPTION_RECORD ExceptionRecord) */ VOID NTAPI -RtlRaiseStatus(NTSTATUS Status) +RtlRaiseStatus(IN NTSTATUS Status) { EXCEPTION_RECORD ExceptionRecord; CONTEXT Context; @@ -84,13 +81,8 @@ RtlRaiseStatus(NTSTATUS Status) /* Capture the context */ RtlCaptureContext(&Context); -#ifdef _M_IX86 - /* Add one argument to ESP */ - Context.Esp += sizeof(PVOID); -#endif - /* Create an exception record */ - ExceptionRecord.ExceptionAddress = RtlpGetExceptionAddress(); + ExceptionRecord.ExceptionAddress = _ReturnAddress(); ExceptionRecord.ExceptionCode = Status; ExceptionRecord.ExceptionRecord = NULL; ExceptionRecord.NumberParameters = 0; @@ -100,7 +92,7 @@ RtlRaiseStatus(NTSTATUS Status) Context.ContextFlags = CONTEXT_FULL; /* Check if user mode debugger is active */ - if (RtlpCheckForActiveDebugger(FALSE)) + if (RtlpCheckForActiveDebugger()) { /* Raise an exception immediately */ ZwRaiseException(&ExceptionRecord, &Context, TRUE); @@ -122,6 +114,8 @@ RtlRaiseStatus(NTSTATUS Status) #pragma warning(pop) #endif +#endif + /* * @implemented */ diff --git a/reactos/lib/rtl/i386/except.c b/reactos/lib/rtl/i386/except.c index 0b5d151929e..66a9d810e80 100644 --- a/reactos/lib/rtl/i386/except.c +++ b/reactos/lib/rtl/i386/except.c @@ -235,7 +235,7 @@ RtlUnwind(IN PVOID TargetFrame OPTIONAL, ExceptionRecord3.ExceptionFlags = 0; ExceptionRecord3.ExceptionCode = STATUS_UNWIND; ExceptionRecord3.ExceptionRecord = NULL; - ExceptionRecord3.ExceptionAddress = RtlpGetExceptionAddress(); + ExceptionRecord3.ExceptionAddress = _ReturnAddress(); ExceptionRecord3.NumberParameters = 0; } diff --git a/reactos/lib/rtl/i386/except_asm.s b/reactos/lib/rtl/i386/except_asm.s index ed20db17cc3..9b2f856c724 100644 --- a/reactos/lib/rtl/i386/except_asm.s +++ b/reactos/lib/rtl/i386/except_asm.s @@ -4,6 +4,7 @@ * FILE: lib/rtl/i386/except_asm.S * PURPOSE: User-mode exception support for IA-32 * PROGRAMMERS: Alex Ionescu (alex@relsoft.net) + * Stefan Ginsberg (stefan.ginsberg@reactos.org) */ /* INCLUDES ******************************************************************/ @@ -41,17 +42,6 @@ _RtlpSetExceptionList@4: ret 4 .endfunc -.func RtlpGetExceptionAddress@0 -.globl _RtlpGetExceptionAddress@0 -_RtlpGetExceptionAddress@0: - - /* Return the address from the stack */ - mov eax, [ebp+4] - - /* Return */ - ret -.endfunc - .func RtlCaptureContext@4 .globl _RtlCaptureContext@4 _RtlCaptureContext@4: @@ -262,3 +252,150 @@ _RtlpUnwindProtector: ret 16 .endfunc +.func RtlRaiseException@4 +.globl _RtlRaiseException@4 +_RtlRaiseException@4: + + /* Set up stack frame */ + push ebp + mov ebp, esp + + /* + * Save the context while preserving everything but ESP and EBP. + * This is vital because the caller will be restored with this context + * in case the execution is continued, which means we must not clobber + * the non-volatiles. We preserve the volatiles too because the context + * could get passed to a debugger. + */ + lea esp, [esp-CONTEXT_FRAME_LENGTH] + push esp + call _RtlCaptureContext@4 + + /* Adjust ESP to account for the argument that was passed */ + add dword ptr [esp+CONTEXT_ESP], 4 + + /* Save the exception address */ + mov edx, [ebp+4] + mov eax, [ebp+8] + mov [eax+EXCEPTION_RECORD_EXCEPTION_ADDRESS], edx + + /* Write the context flag */ + mov dword ptr [esp+CONTEXT_FLAGS], CONTEXT_FULL + + /* Check if user mode debugger is active */ + call _RtlpCheckForActiveDebugger@0 + test al, al + jnz DebuggerActive1 + + /* Dispatch the exception */ + push esp + push [ebp+8] + call _RtlDispatchException@8 + test al, al + jz RaiseException + + /* Continue, go back to previous context */ + mov ecx, esp + push 0 + push ecx + call _ZwContinue@8 + jmp RaiseStatus1 + +DebuggerActive1: + + /* Raise an exception immediately */ + mov ecx, esp + push 1 + push ecx + push [ebp+8] + call _ZwRaiseException@12 + jmp RaiseStatus1 + +RaiseException: + + /* Raise the exception */ + mov ecx, esp + push 0 + push ecx + push [ebp+8] + call _ZwRaiseException@12 + +RaiseStatus1: + + /* If we returned, raise a status */ + push eax + call _RtlRaiseStatus@4 +.endfunc + +.func RtlRaiseStatus@4 +.globl _RtlRaiseStatus@4 +_RtlRaiseStatus@4: + + /* Set up stack frame */ + push ebp + mov ebp, esp + + /* + * Save the context while preserving everything but ESP and EBP. + * This is vital because the caller will be restored with this context + * in case the execution is continued, which means we must not clobber + * the non-volatiles. We preserve the volatiles too because the context + * could get passed to a debugger. + */ + lea esp, [esp-CONTEXT_FRAME_LENGTH-EXCEPTION_RECORD_LENGTH] + push esp + call _RtlCaptureContext@4 + + /* Adjust ESP to account for the argument that was passed */ + add dword ptr [esp+CONTEXT_ESP], 4 + + /* Set up the exception record */ + lea ecx, [esp+CONTEXT_FRAME_LENGTH] + mov eax, [ebp+8] + mov dword ptr [ecx+EXCEPTION_RECORD_EXCEPTION_CODE], eax + mov dword ptr [ecx+EXCEPTION_RECORD_EXCEPTION_FLAGS], EXCEPTION_NONCONTINUABLE + and dword ptr [ecx+EXCEPTION_RECORD_EXCEPTION_RECORD], 0 + mov eax, [ebp+4] + mov dword ptr [ecx+EXCEPTION_RECORD_EXCEPTION_ADDRESS], eax + and dword ptr [ecx+EXCEPTION_RECORD_NUMBER_PARAMETERS], 0 + + /* Write the context flag */ + mov dword ptr [esp+CONTEXT_FLAGS], CONTEXT_FULL + + /* Check if user mode debugger is active */ + call _RtlpCheckForActiveDebugger@0 + + /* Restore ECX and jump if debugger is active */ + lea ecx, [esp+CONTEXT_FRAME_LENGTH] + test al, al + jnz DebuggerActive2 + + /* Dispatch the exception */ + push esp + push ecx + call _RtlDispatchException@8 + + /* Raise exception if we got here */ + lea ecx, [esp+CONTEXT_FRAME_LENGTH] + mov edx, esp + push 0 + push edx + push ecx + call _ZwRaiseException@12 + jmp RaiseStatus2 + +DebuggerActive2: + + /* Raise an exception immediately */ + mov edx, esp + push 1 + push edx + push ecx + call _ZwRaiseException@12 + +RaiseStatus2: + + /* If we returned, raise a status */ + push eax + call _RtlRaiseStatus@4 +.endfunc diff --git a/reactos/lib/rtl/powerpc/except.c b/reactos/lib/rtl/powerpc/except.c index 5b6bce3397d..817ba3086f4 100644 --- a/reactos/lib/rtl/powerpc/except.c +++ b/reactos/lib/rtl/powerpc/except.c @@ -34,14 +34,6 @@ RtlDispatchException return TRUE; } -NTSYSAPI -PVOID -RtlpGetExceptionAddress() -{ - // XXX arty fixme - return NULL; -} - VOID NTAPI RtlUnwind(IN PVOID TargetFrame OPTIONAL, diff --git a/reactos/lib/rtl/rtl.rbuild b/reactos/lib/rtl/rtl.rbuild index e4aed9b73f8..64682037507 100644 --- a/reactos/lib/rtl/rtl.rbuild +++ b/reactos/lib/rtl/rtl.rbuild @@ -43,6 +43,7 @@ access.c acl.c actctx.c + assert.c atom.c bitmap.c bootdata.c diff --git a/reactos/lib/rtl/rtlp.h b/reactos/lib/rtl/rtlp.h index ea0114d056a..e0f2436e8b6 100644 --- a/reactos/lib/rtl/rtlp.h +++ b/reactos/lib/rtl/rtlp.h @@ -100,7 +100,7 @@ RtlLeaveHeapLock(PRTL_CRITICAL_SECTION CriticalSection); BOOLEAN NTAPI -RtlpCheckForActiveDebugger(BOOLEAN Type); +RtlpCheckForActiveDebugger(VOID); BOOLEAN NTAPI @@ -141,10 +141,6 @@ RtlpCheckLogException(IN PEXCEPTION_RECORD ExceptionRecord, IN PVOID ContextData, IN ULONG Size); -PVOID -NTAPI -RtlpGetExceptionAddress(VOID); - VOID NTAPI RtlpCaptureContext(OUT PCONTEXT ContextRecord); diff --git a/reactos/ntoskrnl/include/internal/kd.h b/reactos/ntoskrnl/include/internal/kd.h index d42ffb359e4..256d66c7e75 100644 --- a/reactos/ntoskrnl/include/internal/kd.h +++ b/reactos/ntoskrnl/include/internal/kd.h @@ -208,6 +208,15 @@ KdpPrintString( LPSTR String, ULONG Length); +ULONG +NTAPI +KdpPrompt( + IN LPSTR InString, + IN USHORT InStringLength, + OUT LPSTR OutString, + IN USHORT OutStringLength +); + BOOLEAN NTAPI KdpDetectConflicts(PCM_RESOURCE_LIST DriverList); diff --git a/reactos/ntoskrnl/include/internal/kd64.h b/reactos/ntoskrnl/include/internal/kd64.h index 5961bd42d1a..fb193a4b60e 100644 --- a/reactos/ntoskrnl/include/internal/kd64.h +++ b/reactos/ntoskrnl/include/internal/kd64.h @@ -201,13 +201,13 @@ KdpPrint( OUT PBOOLEAN Status ); -BOOLEAN +USHORT NTAPI KdpPrompt( - IN LPSTR InString, - IN USHORT InStringLength, - OUT LPSTR OutString, - IN USHORT OutStringLength, + IN LPSTR PromptString, + IN USHORT PromptLength, + OUT LPSTR ResponseString, + IN USHORT MaximumResponseLength, IN KPROCESSOR_MODE PreviousMode, IN PKTRAP_FRAME TrapFrame, IN PKEXCEPTION_FRAME ExceptionFrame diff --git a/reactos/ntoskrnl/kd/kdmain.c b/reactos/ntoskrnl/kd/kdmain.c index 5c22935d4b7..791be4e8326 100644 --- a/reactos/ntoskrnl/kd/kdmain.c +++ b/reactos/ntoskrnl/kd/kdmain.c @@ -113,7 +113,8 @@ KdpEnterDebuggerException(IN PKTRAP_FRAME TrapFrame, ((ExceptionCommand == BREAKPOINT_LOAD_SYMBOLS) || (ExceptionCommand == BREAKPOINT_UNLOAD_SYMBOLS) || (ExceptionCommand == BREAKPOINT_COMMAND_STRING) || - (ExceptionCommand == BREAKPOINT_PRINT))) + (ExceptionCommand == BREAKPOINT_PRINT) || + (ExceptionCommand == BREAKPOINT_PROMPT))) { /* Check if this is a debug print */ if (ExceptionCommand == BREAKPOINT_PRINT) @@ -126,22 +127,57 @@ KdpEnterDebuggerException(IN PKTRAP_FRAME TrapFrame, /* Return success */ KeSetContextReturnRegister(Context, STATUS_SUCCESS); } +#ifdef KDBG else if (ExceptionCommand == BREAKPOINT_LOAD_SYMBOLS) { -#ifdef KDBG PLDR_DATA_TABLE_ENTRY LdrEntry; /* Load symbols. Currently implemented only for KDBG! */ if(KdbpSymFindModule(((PKD_SYMBOLS_INFO)ExceptionRecord->ExceptionInformation[2])->BaseOfDll, NULL, -1, &LdrEntry)) KdbSymProcessSymbols(LdrEntry); -#endif } + else if (ExceptionCommand == BREAKPOINT_PROMPT) + { + ULONG ReturnValue; + LPSTR OutString; + USHORT OutStringLength; + + /* Get the response string and length */ + OutString = (LPSTR)Context->Ebx; + OutStringLength = (USHORT)Context->Edi; + + /* Call KDBG */ + ReturnValue = KdpPrompt((LPSTR)ExceptionRecord-> + ExceptionInformation[1], + (USHORT)ExceptionRecord-> + ExceptionInformation[2], + OutString, + OutStringLength); + + /* Return the number of characters that we received */ + Context->Eax = ReturnValue; + } +#endif /* This we can handle: simply bump the Program Counter */ KeSetContextPc(Context, KeGetContextPc(Context) + KD_BREAKPOINT_SIZE); return TRUE; } +#ifdef KDBG + /* Check if this is an assertion failure */ + if (ExceptionRecord->ExceptionCode == STATUS_ASSERTION_FAILURE) + { + /* Warn about it */ + DbgPrint("\n!!! Assertion Failure at Address 0x%p !!!\n\n", + (PVOID)Context->Eip); + + /* Bump EIP to the instruction following the int 2C and return */ + Context->Eip += 2; + return TRUE; + } +#endif + /* Get out of here if the Debugger isn't connected */ if (KdDebuggerNotPresent) return FALSE; diff --git a/reactos/ntoskrnl/kd64/i386/kdsup.c b/reactos/ntoskrnl/kd64/i386/kdsup.c index 5777388a43c..b232a023f9a 100644 --- a/reactos/ntoskrnl/kd64/i386/kdsup.c +++ b/reactos/ntoskrnl/kd64/i386/kdsup.c @@ -3,7 +3,8 @@ * LICENSE: GPL - See COPYING in the top level directory * FILE: ntoskrnl/kd64/i386/kdsup.c * PURPOSE: KD support routines for x86 - * PROGRAMMERS: Stefan Ginsberg (stefan.ginsberg@reactos.org) + * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org) + * Stefan Ginsberg (stefan.ginsberg@reactos.org) */ /* INCLUDES *****************************************************************/ diff --git a/reactos/ntoskrnl/kd64/kdapi.c b/reactos/ntoskrnl/kd64/kdapi.c index a68e43b7c91..329fec5de54 100644 --- a/reactos/ntoskrnl/kd64/kdapi.c +++ b/reactos/ntoskrnl/kd64/kdapi.c @@ -4,6 +4,7 @@ * FILE: ntoskrnl/kd64/kdapi.c * PURPOSE: KD64 Public Routines and Internal Support * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org) + * Stefan Ginsberg (stefan.ginsberg@reactos.org) */ /* INCLUDES ******************************************************************/ diff --git a/reactos/ntoskrnl/kd64/kdbreak.c b/reactos/ntoskrnl/kd64/kdbreak.c index 2583623bf18..48c2ccad9aa 100644 --- a/reactos/ntoskrnl/kd64/kdbreak.c +++ b/reactos/ntoskrnl/kd64/kdbreak.c @@ -4,6 +4,7 @@ * FILE: ntoskrnl/kd64/kdbreak.c * PURPOSE: KD64 Breakpoint Support * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org) + * Stefan Ginsberg (stefan.ginsberg@reactos.org) */ /* INCLUDES ******************************************************************/ diff --git a/reactos/ntoskrnl/kd64/kdinit.c b/reactos/ntoskrnl/kd64/kdinit.c index e5c487c8a3b..81395643710 100644 --- a/reactos/ntoskrnl/kd64/kdinit.c +++ b/reactos/ntoskrnl/kd64/kdinit.c @@ -4,6 +4,7 @@ * FILE: ntoskrnl/kd64/kdinit.c * PURPOSE: KD64 Initialization Code * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org) + * Stefan Ginsberg (stefan.ginsberg@reactos.org) */ /* INCLUDES ******************************************************************/ diff --git a/reactos/ntoskrnl/kd64/kdprint.c b/reactos/ntoskrnl/kd64/kdprint.c index 8892358ed5d..3523efdee2f 100644 --- a/reactos/ntoskrnl/kd64/kdprint.c +++ b/reactos/ntoskrnl/kd64/kdprint.c @@ -4,6 +4,7 @@ * FILE: ntoskrnl/kd64/kdprint.c * PURPOSE: KD64 Trap Handler Routines * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org) + * Stefan Ginsberg (stefan.ginsberg@reactos.org) */ /* INCLUDES ******************************************************************/ @@ -51,6 +52,75 @@ KdpPrintString(IN PSTRING Output) return KdpPollBreakInWithPortLock(); } +BOOLEAN +NTAPI +KdpPromptString(IN PSTRING PromptString, + IN PSTRING ResponseString) +{ + STRING Data, Header; + DBGKD_DEBUG_IO DebugIo; + ULONG Length = PromptString->Length; + KDSTATUS Status; + + /* Copy the string to the message buffer */ + RtlCopyMemory(KdpMessageBuffer, + PromptString->Buffer, + PromptString->Length); + + /* Make sure we don't exceed the KD Packet size */ + if ((sizeof(DBGKD_DEBUG_IO) + Length) > PACKET_MAX_SIZE) + { + /* Normalize length */ + Length = PACKET_MAX_SIZE - sizeof(DBGKD_DEBUG_IO); + } + + /* Build the packet header */ + DebugIo.ApiNumber = DbgKdGetStringApi; + DebugIo.ProcessorLevel = KeProcessorLevel; + DebugIo.Processor = KeGetCurrentPrcb()->Number; + DebugIo.u.GetString.LengthOfPromptString = Length; + DebugIo.u.GetString.LengthOfStringRead = ResponseString->MaximumLength; + Header.Length = sizeof(DBGKD_DEBUG_IO); + Header.Buffer = (PCHAR)&DebugIo; + + /* Build the data */ + Data.Length = PromptString->Length; + Data.Buffer = KdpMessageBuffer; + + /* Send the packet */ + KdSendPacket(PACKET_TYPE_KD_DEBUG_IO, &Header, &Data, &KdpContext); + + /* Set the maximum lengths for the receive */ + Header.MaximumLength = sizeof(DBGKD_DEBUG_IO); + Data.MaximumLength = sizeof(KdpMessageBuffer); + + /* Enter receive loop */ + do + { + /* Get our reply */ + Status = KdReceivePacket(PACKET_TYPE_KD_DEBUG_IO, + &Header, + &Data, + &Length, + &KdpContext); + + /* Return TRUE if we need to resend */ + if (Status == KdPacketNeedsResend) return TRUE; + + /* Loop until we succeed */ + } while (Status != KdPacketReceived); + + /* Don't copy back a larger respone than there is room foor */ + Length = min(Length, ResponseString->MaximumLength); + + /* Copy back the string and return the length */ + RtlCopyMemory(ResponseString->Buffer, KdpMessageBuffer, Length); + ResponseString->Length = Length; + + /* Success; we don't need to resend */ + return FALSE; +} + VOID NTAPI KdpCommandString(IN ULONG Length, @@ -108,20 +178,56 @@ KdpSymbol(IN PSTRING DllPath, KdExitDebugger(Entered); } -BOOLEAN +USHORT NTAPI -KdpPrompt(IN LPSTR InString, - IN USHORT InStringLength, - OUT LPSTR OutString, - IN USHORT OutStringLength, +KdpPrompt(IN LPSTR PromptString, + IN USHORT PromptLength, + OUT LPSTR ResponseString, + IN USHORT MaximumResponseLength, IN KPROCESSOR_MODE PreviousMode, IN PKTRAP_FRAME TrapFrame, IN PKEXCEPTION_FRAME ExceptionFrame) { - /* FIXME */ - KdpDprintf("KdpPrompt called\n"); - while (TRUE); - return FALSE; + STRING PromptBuffer, ResponseBuffer; + BOOLEAN Entered, Resend; + + /* Normalize the lengths */ + PromptLength = min(PromptLength, 512); + MaximumResponseLength = min(MaximumResponseLength, 512); + + /* Check if we need to verify the string */ + if (PreviousMode != KernelMode) + { + /* FIXME: Handle user-mode */ + } + + /* Setup the prompt and response buffers */ + PromptBuffer.Buffer = PromptString; + PromptBuffer.Length = PromptLength; + ResponseBuffer.Buffer = ResponseString; + ResponseBuffer.Length = 0; + ResponseBuffer.MaximumLength = MaximumResponseLength; + + /* Log the print */ + //KdLogDbgPrint(&PromptBuffer); + + /* Enter the debugger */ + Entered = KdEnterDebugger(TrapFrame, ExceptionFrame); + + /* Enter prompt loop */ + do + { + /* Send the prompt and receive the response */ + Resend = KdpPromptString(&PromptBuffer, &ResponseBuffer); + + /* Loop while we need to resend */ + } while (Resend); + + /* Exit the debugger */ + KdExitDebugger(Entered); + + /* Return the number of characters received */ + return ResponseBuffer.Length; } NTSTATUS diff --git a/reactos/ntoskrnl/kd64/kdtrap.c b/reactos/ntoskrnl/kd64/kdtrap.c index 409b2e9a5bb..aed14beba55 100644 --- a/reactos/ntoskrnl/kd64/kdtrap.c +++ b/reactos/ntoskrnl/kd64/kdtrap.c @@ -4,6 +4,7 @@ * FILE: ntoskrnl/kd64/kdtrap.c * PURPOSE: KD64 Trap Handlers * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org) + * Stefan Ginsberg (stefan.ginsberg@reactos.org) */ /* INCLUDES ******************************************************************/ @@ -55,24 +56,41 @@ KdpReport(IN PKTRAP_FRAME TrapFrame, PKPRCB Prcb; NTSTATUS ExceptionCode = ExceptionRecord->ExceptionCode; - /* Check if this is single step or a breakpoint, or if we're forced to handle it */ + /* + * Determine whether to pass the exception to the debugger. + * First, check if this is a "debug exception", meaning breakpoint + * (including debug service), single step and assertion failure exceptions. + */ if ((ExceptionCode == STATUS_BREAKPOINT) || (ExceptionCode == STATUS_SINGLE_STEP) || - (ExceptionCode == STATUS_ASSERTION_FAILURE) || - (NtGlobalFlag & FLG_STOP_ON_EXCEPTION)) + (ExceptionCode == STATUS_ASSERTION_FAILURE)) { - /* Check if we can't really handle this */ - if ((SecondChanceException) || - (ExceptionCode == STATUS_PORT_DISCONNECTED) || - (NT_SUCCESS(ExceptionCode))) + /* This is a debug exception; we always pass them to the debugger */ + } + else if (NtGlobalFlag & FLG_STOP_ON_EXCEPTION) + { + /* + * Not a debug exception, but the stop-on-exception flag is set, + * meaning the debugger requests that we pass it first chance + * exceptions. However, some exceptions are always passed to the + * exception handler first, namely exceptions with a code that isn't + * an error or warning code, and also exceptions with the special + * STATUS_PORT_DISCONNECTED code (an error code). + */ + if ((SecondChanceException == FALSE) && + ((ExceptionCode == STATUS_PORT_DISCONNECTED) || + (NT_SUCCESS(ExceptionCode)))) { - /* Return false to have someone else take care of the exception */ + /* Let the exception handler, if any, try to handle it */ return FALSE; } } - else if (SecondChanceException) + else if (SecondChanceException == FALSE) { - /* We won't bother unless this is first chance */ + /* + * This isn't a debug exception and the stop-on-exception flag isn't + * set, so don't bother + */ return FALSE; } @@ -122,7 +140,8 @@ KdpTrap(IN PKTRAP_FRAME TrapFrame, /* * Check if we got a STATUS_BREAKPOINT with a SubID for Print, Prompt or - * Load/Unload symbols. + * Load/Unload symbols. Make sure it isn't a software breakpoints as those + * are handled by KdpReport. */ if ((ExceptionRecord->ExceptionCode == STATUS_BREAKPOINT) && (ExceptionRecord->ExceptionInformation[0] != BREAKPOINT_BREAK)) @@ -296,7 +315,10 @@ KdIsThisAKdTrap(IN PEXCEPTION_RECORD ExceptionRecord, IN PCONTEXT Context, IN KPROCESSOR_MODE PreviousMode) { - /* Check if this is a breakpoint or a valid debug service */ + /* + * Determine if this is a valid debug service call and make sure that + * it isn't a software breakpoint + */ if ((ExceptionRecord->ExceptionCode == STATUS_BREAKPOINT) && (ExceptionRecord->NumberParameters > 0) && (ExceptionRecord->ExceptionInformation[0] != BREAKPOINT_BREAK)) diff --git a/reactos/ntoskrnl/kdbg/kdb_cli.c b/reactos/ntoskrnl/kdbg/kdb_cli.c index a517f96de2a..8c817f889f5 100644 --- a/reactos/ntoskrnl/kdbg/kdb_cli.c +++ b/reactos/ntoskrnl/kdbg/kdb_cli.c @@ -2801,3 +2801,142 @@ KdbpCliInit() ExFreePool(FileBuffer); } + +VOID +NTAPI +KdpSerialDebugPrint( + LPSTR Message, + ULONG Length +); + +STRING KdpPromptString = RTL_CONSTANT_STRING("kdb:> "); +extern KSPIN_LOCK KdpSerialSpinLock; + +ULONG +NTAPI +KdpPrompt(IN LPSTR InString, + IN USHORT InStringLength, + OUT LPSTR OutString, + IN USHORT OutStringLength) +{ + USHORT i; + CHAR Response; + ULONG DummyScanCode; + KIRQL OldIrql; + + /* Acquire the printing spinlock without waiting at raised IRQL */ + while (TRUE) + { + /* Wait when the spinlock becomes available */ + while (!KeTestSpinLock(&KdpSerialSpinLock)); + + /* Spinlock was free, raise IRQL */ + KeRaiseIrql(HIGH_LEVEL, &OldIrql); + + /* Try to get the spinlock */ + if (KeTryToAcquireSpinLockAtDpcLevel(&KdpSerialSpinLock)) + break; + + /* Someone else got the spinlock, lower IRQL back */ + KeLowerIrql(OldIrql); + } + + /* Loop the string to send */ + for (i = 0; i < InStringLength; i++) + { + /* Print it to serial */ + KdPortPutByteEx(&SerialPortInfo, *(PCHAR)(InString + i)); + } + + /* Print a new line for log neatness */ + KdPortPutByteEx(&SerialPortInfo, '\r'); + KdPortPutByteEx(&SerialPortInfo, '\n'); + + /* Print the kdb prompt */ + for (i = 0; i < KdpPromptString.Length; i++) + { + /* Print it to serial */ + KdPortPutByteEx(&SerialPortInfo, + *(KdpPromptString.Buffer + i)); + } + + /* Loop the whole string */ + for (i = 0; i < OutStringLength; i++) + { + /* Check if this is serial debugging mode */ + if (KdbDebugState & KD_DEBUG_KDSERIAL) + { + /* Get the character from serial */ + do + { + Response = KdbpTryGetCharSerial(MAXULONG); + } while (Response == -1); + } + else + { + /* Get the response from the keyboard */ + do + { + Response = KdbpTryGetCharKeyboard(&DummyScanCode, MAXULONG); + } while (Response == -1); + } + + /* Check for return */ + if (Response == '\r') + { + /* + * We might need to discard the next '\n'. + * Wait a bit to make sure we receive it. + */ + KeStallExecutionProcessor(100000); + + /* Check the mode */ + if (KdbDebugState & KD_DEBUG_KDSERIAL) + { + /* Read and discard the next character, if any */ + KdbpTryGetCharSerial(5); + } + else + { + /* Read and discard the next character, if any */ + KdbpTryGetCharKeyboard(&DummyScanCode, 5); + } + + /* + * Null terminate the output string -- documentation states that + * DbgPrompt does not null terminate, but it does + */ + *(PCHAR)(OutString + i) = 0; + + /* Print a new line */ + KdPortPutByteEx(&SerialPortInfo, '\r'); + KdPortPutByteEx(&SerialPortInfo, '\n'); + + /* Release spinlock */ + KiReleaseSpinLock(&KdpSerialSpinLock); + + /* Lower IRQL back */ + KeLowerIrql(OldIrql); + + /* Return the length */ + return OutStringLength + 1; + } + + /* Write it back and print it to the log */ + *(PCHAR)(OutString + i) = Response; + KdPortPutByteEx(&SerialPortInfo, Response); + } + + /* Print a new line */ + KdPortPutByteEx(&SerialPortInfo, '\r'); + KdPortPutByteEx(&SerialPortInfo, '\n'); + + /* Release spinlock */ + KiReleaseSpinLock(&KdpSerialSpinLock); + + /* Lower IRQL back */ + KeLowerIrql(OldIrql); + + /* Return the length */ + return OutStringLength; +} diff --git a/reactos/ntoskrnl/ke/arm/stubs_asm.s b/reactos/ntoskrnl/ke/arm/stubs_asm.s index 214b3976085..11b8f3a9bc4 100644 --- a/reactos/ntoskrnl/ke/arm/stubs_asm.s +++ b/reactos/ntoskrnl/ke/arm/stubs_asm.s @@ -10,7 +10,6 @@ GENERATE_ARM_STUB _global_unwind2 GENERATE_ARM_STUB _local_unwind2 GENERATE_ARM_STUB RtlGetCallersAddress GENERATE_ARM_STUB RtlUnwind -GENERATE_ARM_STUB RtlpGetExceptionAddress GENERATE_ARM_STUB RtlDispatchException GENERATE_ARM_STUB RtlpGetStackLimits GENERATE_ARM_STUB DbgBreakPointWithStatus diff --git a/reactos/ntoskrnl/ke/i386/trap.s b/reactos/ntoskrnl/ke/i386/trap.s index 190855fe6c8..15a146e1012 100644 --- a/reactos/ntoskrnl/ke/i386/trap.s +++ b/reactos/ntoskrnl/ke/i386/trap.s @@ -132,7 +132,6 @@ _KiTrapIoTable: _KiGetTickCount: _KiCallbackReturn: -_KiRaiseAssertion: /* FIXME: TODO */ UNHANDLED_PATH @@ -461,6 +460,29 @@ AbiosExit: /* FIXME: TODO */ UNHANDLED_PATH +.func KiRaiseAssertion +TRAP_FIXUPS kira_a, kira_t, DoFixupV86, DoFixupAbios +_KiRaiseAssertion: + + /* Push error code */ + push 0 + + /* Enter trap */ + TRAP_PROLOG kira_a, kira_t + + /* + * Modify EIP so it points to the faulting instruction and set it as the + * exception address. Note that the 'int 2C' instruction used for this call + * is 2 bytes long as opposed to 1 byte 'int 3'. + */ + sub dword ptr [ebp+KTRAP_FRAME_EIP], 2 + mov ebx, [ebp+KTRAP_FRAME_EIP] + + /* Raise an assertion failure */ + mov eax, STATUS_ASSERTION_FAILURE + jmp _DispatchNoParam +.endfunc + .func KiDebugService TRAP_FIXUPS kids_a, kids_t, DoFixupV86, DoFixupAbios _KiDebugService: @@ -780,8 +802,11 @@ _KiTrap3: /* Enter trap */ TRAP_PROLOG kit3_a, kit3_t - /* Set status code */ - mov eax, STATUS_SUCCESS + /* + * Set the special code to indicate that this is a software breakpoint + * and not a debug service call + */ + mov eax, BREAKPOINT_BREAK /* Check for V86 */ PrepareInt3: diff --git a/reactos/ntoskrnl/rtl/libsupp.c b/reactos/ntoskrnl/rtl/libsupp.c index ced25c1bade..7e4ee49dcf4 100644 --- a/reactos/ntoskrnl/rtl/libsupp.c +++ b/reactos/ntoskrnl/rtl/libsupp.c @@ -44,10 +44,10 @@ RtlInitializeRangeListPackage(VOID) BOOLEAN NTAPI -RtlpCheckForActiveDebugger(BOOLEAN Type) +RtlpCheckForActiveDebugger(VOID) { /* This check is meaningless in kernel-mode */ - return Type; + return FALSE; } BOOLEAN