341 lines
9.4 KiB
C++
341 lines
9.4 KiB
C++
/***
|
|
*error.cpp - RTC support
|
|
*
|
|
* Copyright (c) 1998-2001, Microsoft Corporation. All rights reserved.
|
|
*
|
|
*
|
|
*Revision History:
|
|
* 07-28-98 JWM Module incorporated into CRTs (from KFrei)
|
|
* 11-03-98 KBF added throw() to eliminate C++ EH code
|
|
* 05-11-99 KBF Error if RTC support define not enabled
|
|
* 05-26-99 KBF Added -RTCu stuff, _RTC_ prefix on all non-statics
|
|
* 11-30-99 PML Compile /Wp64 clean.
|
|
* 03-19-01 KBF Fix buffer overruns (vs7#227306), eliminate all /GS
|
|
* checks (vs7#224261).
|
|
* 03-26-01 PML Use GetVersionExA, not GetVersionEx (vs7#230286)
|
|
* 07-15-01 PML Remove all ALPHA, MIPS, and PPC code
|
|
*
|
|
****/
|
|
|
|
#ifndef _RTC
|
|
#error RunTime Check support not enabled!
|
|
#endif
|
|
|
|
#include "rtcpriv.h"
|
|
|
|
#pragma intrinsic(strcpy)
|
|
#pragma intrinsic(strcat)
|
|
#pragma intrinsic(strlen)
|
|
|
|
static int __cdecl _IsDebuggerPresent();
|
|
int _RTC_ErrorLevels[_RTC_ILLEGAL] = {1,1,1,1};
|
|
static const char *_RTC_ErrorMessages[_RTC_ILLEGAL+1] =
|
|
{
|
|
"The value of ESP was not properly saved across a function "
|
|
"call. This is usually a result of calling a function "
|
|
"declared with one calling convention with a function "
|
|
"pointer declared with a different calling convention.\n\r",
|
|
"A cast to a smaller data type has caused a loss of data. "
|
|
"If this was intentional, you should mask the source of "
|
|
"the cast with the appropriate bitmask. For example: \n\r"
|
|
"\tchar c = (i & 0xFF);\n\r"
|
|
"Changing the code in this way will not affect the quality of the resulting optimized code.\n\r",
|
|
"Stack memory was corrupted\n\r",
|
|
"A local variable was used before it was initialized\n\r",
|
|
#ifdef _RTC_ADVMEM
|
|
"Referencing invalid memory\n\r",
|
|
"Referencing memory across different blocks\n\r",
|
|
#endif
|
|
"Unknown Runtime Check Error\n\r"
|
|
};
|
|
|
|
static const BOOL _RTC_NoFalsePositives[_RTC_ILLEGAL+1] =
|
|
{
|
|
TRUE, // ESP was trashed
|
|
FALSE, // Shortening convert
|
|
TRUE, // Stack corruption
|
|
TRUE, // Uninitialized use
|
|
#ifdef _RTC_ADVMEM
|
|
TRUE, // Invalid memory reference
|
|
FALSE, // Different memory blocks
|
|
#endif
|
|
TRUE // Illegal
|
|
};
|
|
|
|
// returns TRUE if debugger understands, FALSE if not
|
|
static BOOL
|
|
DebuggerProbe( DWORD dwLevelRequired ) throw()
|
|
{
|
|
EXCEPTION_VISUALCPP_DEBUG_INFO info;
|
|
BYTE bDebuggerListening = FALSE;
|
|
|
|
info.dwType = EXCEPTION_DEBUGGER_PROBE;
|
|
info.DebuggerProbe.dwLevelRequired = dwLevelRequired;
|
|
info.DebuggerProbe.pbDebuggerPresent = &bDebuggerListening;
|
|
|
|
__try
|
|
{
|
|
HelloVC( info );
|
|
}
|
|
__except(EXCEPTION_CONTINUE_EXECUTION)
|
|
{
|
|
}
|
|
|
|
return (BOOL)bDebuggerListening;
|
|
}
|
|
|
|
// returns TRUE if debugger reported it (or was ignored), FALSE if runtime needs to report it
|
|
static int
|
|
DebuggerRuntime( DWORD dwErrorNumber, BOOL bRealBug, PVOID pvReturnAddr, LPCWSTR pwMessage ) throw()
|
|
{
|
|
EXCEPTION_VISUALCPP_DEBUG_INFO info;
|
|
BYTE bDebuggerListening = FALSE;
|
|
|
|
info.dwType = EXCEPTION_DEBUGGER_RUNTIMECHECK;
|
|
info.RuntimeError.dwRuntimeNumber = dwErrorNumber;
|
|
info.RuntimeError.bRealBug = bRealBug;
|
|
info.RuntimeError.pvReturnAddress = pvReturnAddr;
|
|
info.RuntimeError.pbDebuggerPresent = &bDebuggerListening;
|
|
info.RuntimeError.pwRuntimeMessage = pwMessage;
|
|
|
|
__try
|
|
{
|
|
HelloVC( info );
|
|
}
|
|
__except(EXCEPTION_CONTINUE_EXECUTION)
|
|
{
|
|
}
|
|
|
|
return (BOOL)bDebuggerListening;
|
|
}
|
|
|
|
static void
|
|
failwithmessage(void *retaddr, int crttype, int errnum, const char *msg)
|
|
{
|
|
_RTC_error_fn fn = _RTC_GetErrorFunc(retaddr);
|
|
bool dobreak;
|
|
if (DebuggerProbe( EXCEPTION_DEBUGGER_RUNTIMECHECK ))
|
|
{
|
|
wchar_t *buf = (wchar_t*)_alloca(sizeof(wchar_t) * (strlen(msg) + 2));
|
|
int i;
|
|
for (i = 0; msg[i]; i++)
|
|
buf[i] = msg[i];
|
|
buf[i] = 0;
|
|
if (DebuggerRuntime(errnum, _RTC_NoFalsePositives[errnum], retaddr, buf))
|
|
return;
|
|
dobreak = false;
|
|
} else
|
|
dobreak = true;
|
|
if (!fn || (dobreak && _IsDebuggerPresent()))
|
|
DebugBreak();
|
|
else
|
|
{
|
|
char *srcName = (char*)_alloca(sizeof(char) * 513);
|
|
int lineNum;
|
|
char *moduleName;
|
|
_RTC_GetSrcLine(((DWORD)(uintptr_t)retaddr)-5, srcName, 512, &lineNum, &moduleName);
|
|
// We're just running - report it like the user setup (or the default way)
|
|
// If we don't recognize this type, it defaults to an error
|
|
if (fn(crttype, srcName, lineNum, moduleName,
|
|
"Run-Time Check Failure #%d - %s", errnum, msg) == 1)
|
|
DebugBreak();
|
|
}
|
|
}
|
|
|
|
void __cdecl
|
|
_RTC_Failure(void *retaddr, int errnum)
|
|
{
|
|
int crttype;
|
|
const char *msg;
|
|
|
|
if (errnum < _RTC_ILLEGAL && errnum >= 0) {
|
|
crttype = _RTC_ErrorLevels[errnum];
|
|
msg = _RTC_ErrorMessages[errnum];
|
|
} else {
|
|
crttype = 1;
|
|
msg = _RTC_ErrorMessages[_RTC_ILLEGAL];
|
|
errnum = _RTC_ILLEGAL;
|
|
}
|
|
|
|
// If we're running inside a debugger, raise an exception
|
|
|
|
if (crttype != _RTC_ERRTYPE_IGNORE)
|
|
{
|
|
failwithmessage(retaddr, crttype, errnum, msg);
|
|
}
|
|
}
|
|
|
|
static
|
|
char *IntToString(int i)
|
|
{
|
|
static char buf[15];
|
|
bool neg = i < 0;
|
|
int pos = 14;
|
|
buf[14] = 0;
|
|
do {
|
|
buf[--pos] = i % 10 + '0';
|
|
i /= 10;
|
|
} while (i);
|
|
if (neg)
|
|
buf[--pos] = '-';
|
|
return &buf[pos];
|
|
}
|
|
|
|
void __cdecl
|
|
_RTC_MemFailure(void *retaddr, int errnum, const void *assign)
|
|
{
|
|
char *srcName = (char*)_alloca(sizeof(char) * 513);
|
|
int lineNum;
|
|
char *moduleName;
|
|
int crttype = _RTC_ErrorLevels[errnum];
|
|
if (crttype == _RTC_ERRTYPE_IGNORE)
|
|
return;
|
|
_RTC_GetSrcLine(((DWORD)(uintptr_t)assign)-5, srcName, 512, &lineNum, &moduleName);
|
|
if (!lineNum)
|
|
_RTC_Failure(retaddr, errnum);
|
|
else
|
|
{
|
|
char *msg = (char*)_alloca(strlen(_RTC_ErrorMessages[errnum]) +
|
|
strlen(srcName) + strlen(moduleName) +
|
|
150);
|
|
strcpy(msg, _RTC_ErrorMessages[errnum]);
|
|
strcat(msg, "Invalid pointer was assigned at\n\rFile:\t");
|
|
strcat(msg, srcName);
|
|
strcat(msg, "\n\rLine:\t");
|
|
strcat(msg, IntToString(lineNum));
|
|
strcat(msg, "\n\rModule:\t");
|
|
strcat(msg, moduleName);
|
|
failwithmessage(retaddr, crttype, errnum, msg);
|
|
}
|
|
}
|
|
|
|
void __cdecl
|
|
_RTC_StackFailure(void *retaddr, const char *varname)
|
|
{
|
|
int crttype = _RTC_ErrorLevels[_RTC_CORRUPT_STACK];
|
|
if (crttype != _RTC_ERRTYPE_IGNORE)
|
|
{
|
|
char *msg = (char*)_alloca(strlen(varname) + 80);
|
|
strcpy(msg, "Stack around the variable '");
|
|
strcat(msg, varname);
|
|
strcat(msg, "' was corrupted.");
|
|
failwithmessage(retaddr, crttype, _RTC_CORRUPT_STACK, msg);
|
|
}
|
|
}
|
|
|
|
void __cdecl
|
|
_RTC_UninitUse(const char *varname)
|
|
{
|
|
int crttype = _RTC_ErrorLevels[_RTC_UNINIT_LOCAL_USE];
|
|
if (crttype != _RTC_ERRTYPE_IGNORE)
|
|
{
|
|
char *msg = (char*)_alloca(strlen(varname) + 80);
|
|
if (varname)
|
|
{
|
|
strcpy(msg, "The variable '");
|
|
strcat(msg, varname);
|
|
strcat(msg, "' is being used without being defined.");
|
|
} else
|
|
{
|
|
strcpy(msg, "A variable is being used without being defined.");
|
|
}
|
|
failwithmessage(_ReturnAddress(), crttype, _RTC_UNINIT_LOCAL_USE, msg);
|
|
}
|
|
}
|
|
|
|
/* The rest of this file just implements "IsDebuggerPresent" functionality */
|
|
|
|
#pragma pack (push, 1)
|
|
|
|
typedef struct _TIB {
|
|
PVOID ExceptionList;
|
|
PVOID StackLimit;
|
|
PVOID StackBase;
|
|
PVOID SubSystemTib;
|
|
PVOID Something1;
|
|
PVOID ArbitraryUserPointer;
|
|
struct _TIB* Self;
|
|
WORD Flags;
|
|
WORD Win16MutextCount;
|
|
PVOID DebugContext;
|
|
DWORD CurrentPriority;
|
|
DWORD MessageQueueSelector;
|
|
PVOID* TlsSlots; // most likely an array
|
|
} TIB;
|
|
|
|
#pragma pack (pop)
|
|
|
|
//
|
|
// Define function to return the current Thread Environment Block
|
|
// Information Block
|
|
|
|
#pragma warning (disable:4035)
|
|
#define OffsetTib 0x18
|
|
static _inline TIB* GetCurrentTib() { __asm mov eax, fs:[OffsetTib] }
|
|
#pragma warning (default:4035)
|
|
|
|
|
|
#define DLL_NOT_FOUND_EXCEPTION (0xc0000135L)
|
|
|
|
typedef BOOL (WINAPI* NT_IS_DEBUGGER_PRESENT) ();
|
|
|
|
static NT_IS_DEBUGGER_PRESENT FnIsDebuggerPresent = NULL;
|
|
|
|
static PVOID
|
|
WinGetDebugContext()
|
|
{
|
|
return GetCurrentTib()->DebugContext;
|
|
}
|
|
|
|
// here's the Win95 version of IsDebuggerPresent
|
|
static BOOL WINAPI
|
|
Win95IsDebuggerPresent()
|
|
{
|
|
if (WinGetDebugContext ()) {
|
|
return TRUE;
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
static BOOL
|
|
Initialize()
|
|
{
|
|
HINSTANCE hInst = NULL;
|
|
|
|
hInst = LoadLibrary ("Kernel32.dll");
|
|
|
|
FnIsDebuggerPresent =
|
|
(NT_IS_DEBUGGER_PRESENT) GetProcAddress (hInst, "IsDebuggerPresent");
|
|
|
|
if (!FnIsDebuggerPresent) {
|
|
OSVERSIONINFOA *VersionInfo = (OSVERSIONINFOA*)_alloca(sizeof(OSVERSIONINFOA));
|
|
|
|
VersionInfo->dwOSVersionInfoSize = sizeof (OSVERSIONINFOA);
|
|
|
|
if (GetVersionExA (VersionInfo) &&
|
|
VersionInfo->dwPlatformId == VER_PLATFORM_WIN32_WINDOWS &&
|
|
VersionInfo->dwMajorVersion == 4)
|
|
FnIsDebuggerPresent = Win95IsDebuggerPresent;
|
|
}
|
|
|
|
return !!(FnIsDebuggerPresent);
|
|
}
|
|
|
|
|
|
// This is a version of IsDebuggerPresent () that works for all Win32 platforms.
|
|
static int __cdecl
|
|
_IsDebuggerPresent()
|
|
{
|
|
static BOOL fInited = FALSE;
|
|
|
|
if (!fInited) {
|
|
if (!Initialize())
|
|
RaiseException (DLL_NOT_FOUND_EXCEPTION, 0, 0, NULL);
|
|
fInited = TRUE;
|
|
}
|
|
|
|
return FnIsDebuggerPresent();
|
|
}
|