summaryrefslogtreecommitdiff
path: root/src/vm/util.cpp
diff options
context:
space:
mode:
authordotnet-bot <dotnet-bot@microsoft.com>2015-01-30 14:14:42 -0800
committerdotnet-bot <dotnet-bot@microsoft.com>2015-01-30 14:14:42 -0800
commitef1e2ab328087c61a6878c1e84f4fc5d710aebce (patch)
treedee1bbb89e9d722e16b0d1485e3cdd1b6c8e2cfa /src/vm/util.cpp
downloadcoreclr-ef1e2ab328087c61a6878c1e84f4fc5d710aebce.tar.gz
coreclr-ef1e2ab328087c61a6878c1e84f4fc5d710aebce.tar.bz2
coreclr-ef1e2ab328087c61a6878c1e84f4fc5d710aebce.zip
Initial commit to populate CoreCLR repo
[tfs-changeset: 1407945]
Diffstat (limited to 'src/vm/util.cpp')
-rw-r--r--src/vm/util.cpp4000
1 files changed, 4000 insertions, 0 deletions
diff --git a/src/vm/util.cpp b/src/vm/util.cpp
new file mode 100644
index 0000000000..6fd3cc2b34
--- /dev/null
+++ b/src/vm/util.cpp
@@ -0,0 +1,4000 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+// ===========================================================================
+// File: UTIL.CPP
+//
+
+// ===========================================================================
+
+
+#include "common.h"
+#include "excep.h"
+#include "corhost.h"
+#include "eventtrace.h"
+#include "posterror.h"
+#include "eemessagebox.h"
+#include "newapis.h"
+
+#include <shlobj.h>
+
+#include "dlwrap.h"
+
+#ifndef DACCESS_COMPILE
+
+// Helper function that encapsulates the parsing rules.
+//
+// Called first with *pdstout == NULL to figure out how many args there are
+// and the size of the required destination buffer.
+//
+// Called again with a nonnull *pdstout to fill in the actual buffer.
+//
+// Returns the # of arguments.
+static UINT ParseCommandLine(LPCWSTR psrc, __out LPWSTR *pdstout)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ FORBID_FAULT;
+ }
+ CONTRACTL_END
+
+ UINT argcount = 1; // discovery of arg0 is unconditional, below
+ LPWSTR pdst = *pdstout;
+ BOOL fDoWrite = (pdst != NULL);
+
+ BOOL fInQuotes;
+ int iSlash;
+
+ /* A quoted program name is handled here. The handling is much
+ simpler than for other arguments. Basically, whatever lies
+ between the leading double-quote and next one, or a terminal null
+ character is simply accepted. Fancier handling is not required
+ because the program name must be a legal NTFS/HPFS file name.
+ Note that the double-quote characters are not copied, nor do they
+ contribute to numchars.
+
+ This "simplification" is necessary for compatibility reasons even
+ though it leads to mishandling of certain cases. For example,
+ "c:\tests\"test.exe will result in an arg0 of c:\tests\ and an
+ arg1 of test.exe. In any rational world this is incorrect, but
+ we need to preserve compatibility.
+ */
+
+ LPCWSTR pStart = psrc;
+ BOOL skipQuote = FALSE;
+
+ if (*psrc == W('\"'))
+ {
+ // scan from just past the first double-quote through the next
+ // double-quote, or up to a null, whichever comes first
+ while ((*(++psrc) != W('\"')) && (*psrc != W('\0')))
+ continue;
+
+ skipQuote = TRUE;
+ }
+ else
+ {
+ /* Not a quoted program name */
+
+ while (!ISWWHITE(*psrc) && *psrc != W('\0'))
+ psrc++;
+ }
+
+ // We have now identified arg0 as pStart (or pStart+1 if we have a leading
+ // quote) through psrc-1 inclusive
+ if (skipQuote)
+ pStart++;
+ while (pStart < psrc)
+ {
+ if (fDoWrite)
+ *pdst = *pStart;
+
+ pStart++;
+ pdst++;
+ }
+
+ // And terminate it.
+ if (fDoWrite)
+ *pdst = W('\0');
+
+ pdst++;
+
+ // if we stopped on a double-quote when arg0 is quoted, skip over it
+ if (skipQuote && *psrc == W('\"'))
+ psrc++;
+
+ while ( *psrc != W('\0'))
+ {
+LEADINGWHITE:
+
+ // The outofarg state.
+ while (ISWWHITE(*psrc))
+ psrc++;
+
+ if (*psrc == W('\0'))
+ break;
+ else
+ if (*psrc == W('#'))
+ {
+ while (*psrc != W('\0') && *psrc != W('\n'))
+ psrc++; // skip to end of line
+
+ goto LEADINGWHITE;
+ }
+
+ argcount++;
+ fInQuotes = FALSE;
+
+ while ((!ISWWHITE(*psrc) || fInQuotes) && *psrc != W('\0'))
+ {
+ switch (*psrc)
+ {
+ case W('\\'):
+ iSlash = 0;
+ while (*psrc == W('\\'))
+ {
+ iSlash++;
+ psrc++;
+ }
+
+ if (*psrc == W('\"'))
+ {
+ for ( ; iSlash >= 2; iSlash -= 2)
+ {
+ if (fDoWrite)
+ *pdst = W('\\');
+
+ pdst++;
+ }
+
+ if (iSlash & 1)
+ {
+ if (fDoWrite)
+ *pdst = *psrc;
+
+ psrc++;
+ pdst++;
+ }
+ else
+ {
+ fInQuotes = !fInQuotes;
+ psrc++;
+ }
+ }
+ else
+ for ( ; iSlash > 0; iSlash--)
+ {
+ if (fDoWrite)
+ *pdst = W('\\');
+
+ pdst++;
+ }
+
+ break;
+
+ case W('\"'):
+ fInQuotes = !fInQuotes;
+ psrc++;
+ break;
+
+ default:
+ if (fDoWrite)
+ *pdst = *psrc;
+
+ psrc++;
+ pdst++;
+ }
+ }
+
+ if (fDoWrite)
+ *pdst = W('\0');
+
+ pdst++;
+ }
+
+
+ _ASSERTE(*psrc == W('\0'));
+ *pdstout = pdst;
+ return argcount;
+}
+
+
+// Function to parse apart a command line and return the
+// arguments just like argv and argc
+// This function is a little funky because of the pointer work
+// but it is neat because it allows the recipient of the char**
+// to only have to do a single delete []
+LPWSTR* CommandLineToArgvW(__in LPWSTR lpCmdLine, DWORD *pNumArgs)
+{
+
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ INJECT_FAULT(return NULL;);
+ }
+ CONTRACTL_END
+
+ DWORD argcount = 0;
+ LPWSTR retval = NULL;
+ LPWSTR *pslot;
+ // First we need to find out how many strings there are in the command line
+ _ASSERTE(lpCmdLine);
+ _ASSERTE(pNumArgs);
+
+ LPWSTR pdst = NULL;
+ argcount = ParseCommandLine(lpCmdLine, &pdst);
+
+ // This check is because on WinCE the Application Name is not passed in as an argument to the app!
+ if (argcount == 0)
+ {
+ *pNumArgs = 0;
+ return NULL;
+ }
+
+ // Now we need alloc a buffer the size of the command line + the number of strings * DWORD
+ retval = new (nothrow) WCHAR[(argcount*sizeof(WCHAR*))/sizeof(WCHAR) + (pdst - (LPWSTR)NULL)];
+ if(!retval)
+ return NULL;
+
+ pdst = (LPWSTR)( argcount*sizeof(LPWSTR*) + (BYTE*)retval );
+ ParseCommandLine(lpCmdLine, &pdst);
+ pdst = (LPWSTR)( argcount*sizeof(LPWSTR*) + (BYTE*)retval );
+ pslot = (LPWSTR*)retval;
+ for (DWORD i = 0; i < argcount; i++)
+ {
+ *(pslot++) = pdst;
+ while (*pdst != W('\0'))
+ {
+ pdst++;
+ }
+ pdst++;
+ }
+
+
+
+ *pNumArgs = argcount;
+ return (LPWSTR*)retval;
+
+}
+
+
+
+
+//************************************************************************
+// CQuickHeap
+//
+// A fast non-multithread-safe heap for short term use.
+// Destroying the heap frees all blocks allocated from the heap.
+// Blocks cannot be freed individually.
+//
+// The heap uses COM+ exceptions to report errors.
+//
+// The heap does not use any internal synchronization so it is not
+// multithreadsafe.
+//************************************************************************
+CQuickHeap::CQuickHeap()
+{
+ LIMITED_METHOD_CONTRACT;
+
+ m_pFirstQuickBlock = NULL;
+ m_pFirstBigQuickBlock = NULL;
+ m_pNextFree = NULL;
+}
+
+CQuickHeap::~CQuickHeap()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ FORBID_FAULT;
+ }
+ CONTRACTL_END
+
+ QuickBlock *pQuickBlock = m_pFirstQuickBlock;
+ while (pQuickBlock) {
+ QuickBlock *ptmp = pQuickBlock;
+ pQuickBlock = pQuickBlock->m_next;
+ delete [] (BYTE*)ptmp;
+ }
+
+ pQuickBlock = m_pFirstBigQuickBlock;
+ while (pQuickBlock) {
+ QuickBlock *ptmp = pQuickBlock;
+ pQuickBlock = pQuickBlock->m_next;
+ delete [] (BYTE*)ptmp;
+ }
+}
+
+LPVOID CQuickHeap::Alloc(UINT sz)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ SO_TOLERANT; // So long as we cleanup the heap when we're done, all the memory goes with it
+ INJECT_FAULT(COMPlusThrowOM(););
+ } CONTRACTL_END;
+
+ sz = (sz+7) & ~7;
+
+ if ( sz > kBlockSize ) {
+
+ QuickBlock *pQuickBigBlock = (QuickBlock*) new BYTE[sz + sizeof(QuickBlock) - 1];
+ pQuickBigBlock->m_next = m_pFirstBigQuickBlock;
+ m_pFirstBigQuickBlock = pQuickBigBlock;
+
+ return pQuickBigBlock->m_bytes;
+
+
+ } else {
+ if (m_pNextFree == NULL || sz > (UINT)( &(m_pFirstQuickBlock->m_bytes[kBlockSize]) - m_pNextFree )) {
+ QuickBlock *pQuickBlock = (QuickBlock*) new BYTE[kBlockSize + sizeof(QuickBlock) - 1];
+ pQuickBlock->m_next = m_pFirstQuickBlock;
+ m_pFirstQuickBlock = pQuickBlock;
+ m_pNextFree = pQuickBlock->m_bytes;
+ }
+ LPVOID pv = m_pNextFree;
+ m_pNextFree += sz;
+ return pv;
+ }
+}
+
+//----------------------------------------------------------------------------
+// Output functions that avoid the crt's.
+//----------------------------------------------------------------------------
+
+static
+void NPrintToHandleA(HANDLE Handle, const char *pszString, size_t BytesToWrite)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ FORBID_FAULT;
+ }
+ CONTRACTL_END
+
+ if (Handle == INVALID_HANDLE_VALUE || Handle == NULL)
+ return;
+
+ BOOL success;
+ DWORD dwBytesWritten;
+ const size_t maxWriteFileSize = 32767; // This is somewhat arbitrary limit, but 2**16-1 doesn't work
+
+ while (BytesToWrite > 0) {
+ DWORD dwChunkToWrite = (DWORD) min(BytesToWrite, maxWriteFileSize);
+ // No CharNextExA on CoreSystem, we just assume no multi-byte characters (this code path shouldn't be
+ // used in the production codepath for currently supported CoreSystem based products anyway).
+#ifndef FEATURE_CORESYSTEM
+ if (dwChunkToWrite < BytesToWrite) {
+ break;
+ // must go by char to find biggest string that will fit, taking DBCS chars into account
+ //dwChunkToWrite = 0;
+ //const char *charNext = pszString;
+ //while (dwChunkToWrite < maxWriteFileSize-2 && charNext) {
+ // charNext = CharNextExA(0, pszString+dwChunkToWrite, 0);
+ // dwChunkToWrite = (DWORD)(charNext - pszString);
+ //}
+ //if (dwChunkToWrite == 0)
+ // break;
+ }
+#endif // !FEATURE_CORESYSTEM
+
+ // Try to write to handle. If this is not a CUI app, then this is probably
+ // not going to work unless the dev took special pains to set their own console
+ // handle during CreateProcess. So try it, but don't yell if it doesn't work in
+ // that case. Also, if we redirect stdout to a pipe then the pipe breaks (ie, we
+ // write to something like the UNIX head command), don't complain.
+ success = WriteFile(Handle, pszString, dwChunkToWrite, &dwBytesWritten, NULL);
+ if (!success)
+ {
+#if defined(_DEBUG)
+ // This can happen if stdout is a closed pipe. This might not help
+ // much, but we'll have half a chance of seeing this.
+ OutputDebugStringA("CLR: Writing out an unhandled exception to stdout failed!\n");
+ OutputDebugStringA(pszString);
+#endif //_DEBUG
+
+ break;
+ }
+ else {
+ _ASSERTE(dwBytesWritten == dwChunkToWrite);
+ }
+ pszString = pszString + dwChunkToWrite;
+ BytesToWrite -= dwChunkToWrite;
+ }
+
+}
+
+static
+void PrintToHandleA(HANDLE Handle, const char *pszString)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ FORBID_FAULT;
+ }
+ CONTRACTL_END
+
+ size_t len = strlen(pszString);
+ NPrintToHandleA(Handle, pszString, len);
+}
+
+void PrintToStdOutA(const char *pszString) {
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ FORBID_FAULT;
+ }
+ CONTRACTL_END
+
+ HANDLE Handle = GetStdHandle(STD_OUTPUT_HANDLE);
+ PrintToHandleA(Handle, pszString);
+}
+
+
+void PrintToStdOutW(const WCHAR *pwzString)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACTL_END
+
+ MAKE_MULTIBYTE_FROMWIDE_BESTFIT(pStr, pwzString, GetConsoleOutputCP());
+
+ PrintToStdOutA(pStr);
+}
+
+void PrintToStdErrA(const char *pszString) {
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ FORBID_FAULT;
+ }
+ CONTRACTL_END
+
+ HANDLE Handle = GetStdHandle(STD_ERROR_HANDLE);
+ PrintToHandleA(Handle, pszString);
+}
+
+
+void PrintToStdErrW(const WCHAR *pwzString)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACTL_END
+
+ MAKE_MULTIBYTE_FROMWIDE_BESTFIT(pStr, pwzString, GetConsoleOutputCP());
+
+ PrintToStdErrA(pStr);
+}
+
+
+
+void NPrintToStdOutA(const char *pszString, size_t nbytes) {
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ FORBID_FAULT;
+ }
+ CONTRACTL_END
+
+ HANDLE Handle = GetStdHandle(STD_OUTPUT_HANDLE);
+ NPrintToHandleA(Handle, pszString, nbytes);
+}
+
+
+void NPrintToStdOutW(const WCHAR *pwzString, size_t nchars)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACTL_END
+
+ LPSTR pStr;
+ MAKE_MULTIBYTE_FROMWIDEN_BESTFIT(pStr, pwzString, (int)nchars, nbytes, GetConsoleOutputCP());
+
+ NPrintToStdOutA(pStr, nbytes);
+}
+
+void NPrintToStdErrA(const char *pszString, size_t nbytes) {
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ FORBID_FAULT;
+ }
+ CONTRACTL_END
+
+ HANDLE Handle = GetStdHandle(STD_ERROR_HANDLE);
+ NPrintToHandleA(Handle, pszString, nbytes);
+}
+
+
+void NPrintToStdErrW(const WCHAR *pwzString, size_t nchars)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACTL_END
+
+ LPSTR pStr;
+
+ MAKE_MULTIBYTE_FROMWIDEN_BESTFIT(pStr, pwzString, (int)nchars, nbytes, GetConsoleOutputCP());
+
+ NPrintToStdErrA(pStr, nbytes);
+}
+//----------------------------------------------------------------------------
+
+
+
+
+
+//+--------------------------------------------------------------------------
+//
+// Function: VMDebugOutputA( . . . . )
+// VMDebugOutputW( . . . . )
+//
+// Synopsis: Output a message formatted in printf fashion to the debugger.
+// ANSI and wide character versions are both provided. Only
+// present in debug builds (i.e. when _DEBUG is defined).
+//
+// Arguments: [format] --- ANSI or Wide character format string
+// in printf/OutputDebugString-style format.
+//
+// [ ... ] --- Variable length argument list compatible
+// with the format string.
+//
+// Returns: Nothing.
+//
+// Notes: Has internal static sized character buffer of
+// width specified by the preprocessor constant DEBUGOUT_BUFSIZE.
+//
+//---------------------------------------------------------------------------
+#ifdef _DEBUG
+
+#define DEBUGOUT_BUFSIZE 1024
+
+void __cdecl VMDebugOutputA(__in LPSTR format, ...)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+ va_list argPtr;
+ va_start(argPtr, format);
+
+ char szBuffer[DEBUGOUT_BUFSIZE];
+
+ if(vsprintf_s(szBuffer, DEBUGOUT_BUFSIZE-1, format, argPtr) > 0)
+ OutputDebugStringA(szBuffer);
+ va_end(argPtr);
+}
+
+void __cdecl VMDebugOutputW(__in LPWSTR format, ...)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FORBID_FAULT;
+ STATIC_CONTRACT_DEBUG_ONLY;
+
+ va_list argPtr;
+ va_start(argPtr, format);
+
+ WCHAR wszBuffer[DEBUGOUT_BUFSIZE];
+
+ if(vswprintf_s(wszBuffer, DEBUGOUT_BUFSIZE-2, format, argPtr) > 0)
+ WszOutputDebugString(wszBuffer);
+ va_end(argPtr);
+}
+
+#endif // #ifdef DACCESS_COMPILE
+
+//*****************************************************************************
+// Compare VarLoc's
+//*****************************************************************************
+
+bool operator ==(const ICorDebugInfo::VarLoc &varLoc1,
+ const ICorDebugInfo::VarLoc &varLoc2)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+ if (varLoc1.vlType != varLoc2.vlType)
+ return false;
+
+ switch(varLoc1.vlType)
+ {
+ case ICorDebugInfo::VLT_REG:
+ case ICorDebugInfo::VLT_REG_BYREF:
+ return varLoc1.vlReg.vlrReg == varLoc2.vlReg.vlrReg;
+
+ case ICorDebugInfo::VLT_STK:
+ case ICorDebugInfo::VLT_STK_BYREF:
+ return varLoc1.vlStk.vlsBaseReg == varLoc2.vlStk.vlsBaseReg &&
+ varLoc1.vlStk.vlsOffset == varLoc2.vlStk.vlsOffset;
+
+ case ICorDebugInfo::VLT_REG_REG:
+ return varLoc1.vlRegReg.vlrrReg1 == varLoc2.vlRegReg.vlrrReg1 &&
+ varLoc1.vlRegReg.vlrrReg2 == varLoc2.vlRegReg.vlrrReg2;
+
+ case ICorDebugInfo::VLT_REG_STK:
+ return varLoc1.vlRegStk.vlrsReg == varLoc2.vlRegStk.vlrsReg &&
+ varLoc1.vlRegStk.vlrsStk.vlrssBaseReg == varLoc2.vlRegStk.vlrsStk.vlrssBaseReg &&
+ varLoc1.vlRegStk.vlrsStk.vlrssOffset == varLoc2.vlRegStk.vlrsStk.vlrssOffset;
+
+ case ICorDebugInfo::VLT_STK_REG:
+ return varLoc1.vlStkReg.vlsrStk.vlsrsBaseReg == varLoc2.vlStkReg.vlsrStk.vlsrsBaseReg &&
+ varLoc1.vlStkReg.vlsrStk.vlsrsOffset == varLoc2.vlStkReg.vlsrStk.vlsrsBaseReg &&
+ varLoc1.vlStkReg.vlsrReg == varLoc2.vlStkReg.vlsrReg;
+
+ case ICorDebugInfo::VLT_STK2:
+ // <TODO>@TODO : VLT_STK(2) is overloaded to also indicate valueclasses
+ // which should be VLT_STK_n. Just have a parameterized VLT_STK_n.</TODO>
+ return varLoc1.vlStk2.vls2BaseReg == varLoc1.vlStk2.vls2BaseReg &&
+ varLoc1.vlStk2.vls2Offset == varLoc1.vlStk2.vls2Offset;
+
+ case ICorDebugInfo::VLT_FPSTK:
+ return varLoc1.vlFPstk.vlfReg == varLoc1.vlFPstk.vlfReg;
+
+ default:
+ _ASSERTE(!"Bad vlType"); return false;
+ }
+}
+
+#endif // #ifndef DACCESS_COMPILE
+
+//*****************************************************************************
+// The following are used to read and write data given NativeVarInfo
+// for primitive types. For ValueClasses, FALSE will be returned.
+//*****************************************************************************
+
+SIZE_T GetRegOffsInCONTEXT(ICorDebugInfo::RegNum regNum)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+#ifdef _TARGET_X86_
+ switch(regNum)
+ {
+ case ICorDebugInfo::REGNUM_EAX: return offsetof(T_CONTEXT,Eax);
+ case ICorDebugInfo::REGNUM_ECX: return offsetof(T_CONTEXT,Ecx);
+ case ICorDebugInfo::REGNUM_EDX: return offsetof(T_CONTEXT,Edx);
+ case ICorDebugInfo::REGNUM_EBX: return offsetof(T_CONTEXT,Ebx);
+ // TODO: Fix AMBIENT_SP handling.
+ // AMBIENT_SP It isn't necessarily the same value as ESP. We probably shouldn't try
+ // and handle REGNUM_AMBIENT_SP here, and instead update our callers (eg.
+ // GetNativeVarVal) to handle this case explicitly. This logic should also be
+ // merged with the parallel (but correct in this case) logic in mscordbi.
+ case ICorDebugInfo::REGNUM_ESP:
+ case ICorDebugInfo::REGNUM_AMBIENT_SP:
+ return offsetof(T_CONTEXT,Esp);
+ case ICorDebugInfo::REGNUM_EBP: return offsetof(T_CONTEXT,Ebp);
+ case ICorDebugInfo::REGNUM_ESI: return offsetof(T_CONTEXT,Esi);
+ case ICorDebugInfo::REGNUM_EDI: return offsetof(T_CONTEXT,Edi);
+ default: _ASSERTE(!"Bad regNum"); return (SIZE_T) -1;
+ }
+#elif defined(_TARGET_AMD64_)
+ switch(regNum)
+ {
+ case ICorDebugInfo::REGNUM_RAX: return offsetof(CONTEXT, Rax);
+ case ICorDebugInfo::REGNUM_RCX: return offsetof(CONTEXT, Rcx);
+ case ICorDebugInfo::REGNUM_RDX: return offsetof(CONTEXT, Rdx);
+ case ICorDebugInfo::REGNUM_RBX: return offsetof(CONTEXT, Rbx);
+ case ICorDebugInfo::REGNUM_RSP: return offsetof(CONTEXT, Rsp);
+ case ICorDebugInfo::REGNUM_RBP: return offsetof(CONTEXT, Rbp);
+ case ICorDebugInfo::REGNUM_RSI: return offsetof(CONTEXT, Rsi);
+ case ICorDebugInfo::REGNUM_RDI: return offsetof(CONTEXT, Rdi);
+ case ICorDebugInfo::REGNUM_R8: return offsetof(CONTEXT, R8);
+ case ICorDebugInfo::REGNUM_R9: return offsetof(CONTEXT, R9);
+ case ICorDebugInfo::REGNUM_R10: return offsetof(CONTEXT, R10);
+ case ICorDebugInfo::REGNUM_R11: return offsetof(CONTEXT, R11);
+ case ICorDebugInfo::REGNUM_R12: return offsetof(CONTEXT, R12);
+ case ICorDebugInfo::REGNUM_R13: return offsetof(CONTEXT, R13);
+ case ICorDebugInfo::REGNUM_R14: return offsetof(CONTEXT, R14);
+ case ICorDebugInfo::REGNUM_R15: return offsetof(CONTEXT, R15);
+ default: _ASSERTE(!"Bad regNum"); return (SIZE_T)(-1);
+ }
+#elif defined(_TARGET_ARM_)
+
+ switch(regNum)
+ {
+ case ICorDebugInfo::REGNUM_R0: return offsetof(T_CONTEXT, R0);
+ case ICorDebugInfo::REGNUM_R1: return offsetof(T_CONTEXT, R1);
+ case ICorDebugInfo::REGNUM_R2: return offsetof(T_CONTEXT, R2);
+ case ICorDebugInfo::REGNUM_R3: return offsetof(T_CONTEXT, R3);
+ case ICorDebugInfo::REGNUM_R4: return offsetof(T_CONTEXT, R4);
+ case ICorDebugInfo::REGNUM_R5: return offsetof(T_CONTEXT, R5);
+ case ICorDebugInfo::REGNUM_R6: return offsetof(T_CONTEXT, R6);
+ case ICorDebugInfo::REGNUM_R7: return offsetof(T_CONTEXT, R7);
+ case ICorDebugInfo::REGNUM_R8: return offsetof(T_CONTEXT, R8);
+ case ICorDebugInfo::REGNUM_R9: return offsetof(T_CONTEXT, R9);
+ case ICorDebugInfo::REGNUM_R10: return offsetof(T_CONTEXT, R10);
+ case ICorDebugInfo::REGNUM_R11: return offsetof(T_CONTEXT, R11);
+ case ICorDebugInfo::REGNUM_R12: return offsetof(T_CONTEXT, R12);
+ case ICorDebugInfo::REGNUM_SP: return offsetof(T_CONTEXT, Sp);
+ case ICorDebugInfo::REGNUM_PC: return offsetof(T_CONTEXT, Pc);
+ case ICorDebugInfo::REGNUM_LR: return offsetof(T_CONTEXT, Lr);
+ case ICorDebugInfo::REGNUM_AMBIENT_SP: return offsetof(T_CONTEXT, Sp);
+ default: _ASSERTE(!"Bad regNum"); return (SIZE_T)(-1);
+ }
+#else
+ PORTABILITY_ASSERT("GetRegOffsInCONTEXT is not implemented on this platform.");
+ return (SIZE_T) -1;
+#endif // _TARGET_X86_
+}
+
+SIZE_T DereferenceByRefVar(SIZE_T addr)
+{
+ STATIC_CONTRACT_WRAPPER;
+
+ SIZE_T result = NULL;
+
+#if defined(DACCESS_COMPILE)
+ HRESULT hr = DacReadAll(addr, &result, sizeof(result), false);
+ if (FAILED(hr))
+ {
+ result = NULL;
+ }
+
+#else // !DACCESS_COMPILE
+ EX_TRY
+ {
+ AVInRuntimeImplOkayHolder AVOkay;
+
+ result = *(SIZE_T*)addr;
+ }
+ EX_CATCH
+ {
+ }
+ EX_END_CATCH(SwallowAllExceptions);
+
+#endif // !DACCESS_COMPILE
+
+ return result;
+}
+
+// How are errors communicated to the caller?
+ULONG NativeVarLocations(const ICorDebugInfo::VarLoc & varLoc,
+ PT_CONTEXT pCtx,
+ ULONG numLocs,
+ NativeVarLocation* locs)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+ _ASSERTE(numLocs >= MAX_NATIVE_VAR_LOCS);
+
+ bool fByRef = false;
+ switch(varLoc.vlType)
+ {
+ SIZE_T regOffs;
+ TADDR baseReg;
+
+ case ICorDebugInfo::VLT_REG_BYREF:
+ fByRef = true; // fall through
+ case ICorDebugInfo::VLT_REG:
+ regOffs = GetRegOffsInCONTEXT(varLoc.vlReg.vlrReg);
+ locs->addr = (ULONG64)(ULONG_PTR)pCtx + regOffs;
+ if (fByRef)
+ {
+ locs->addr = (ULONG64)DereferenceByRefVar((SIZE_T)locs->addr);
+ }
+ locs->size = sizeof(SIZE_T);
+ {
+ locs->contextReg = true;
+ }
+ return 1;
+
+ case ICorDebugInfo::VLT_STK_BYREF:
+ fByRef = true; // fall through
+ case ICorDebugInfo::VLT_STK:
+ regOffs = GetRegOffsInCONTEXT(varLoc.vlStk.vlsBaseReg);
+ baseReg = *(TADDR *)(regOffs + (BYTE*)pCtx);
+ locs->addr = baseReg + varLoc.vlStk.vlsOffset;
+ if (fByRef)
+ {
+ locs->addr = (ULONG64)DereferenceByRefVar((SIZE_T)locs->addr);
+ }
+ locs->size = sizeof(SIZE_T);
+ locs->contextReg = false;
+ return 1;
+
+ case ICorDebugInfo::VLT_REG_REG:
+ regOffs = GetRegOffsInCONTEXT(varLoc.vlRegReg.vlrrReg1);
+ locs->addr = (ULONG64)(ULONG_PTR)pCtx + regOffs;
+ locs->size = sizeof(SIZE_T);
+ locs->contextReg = true;
+ locs++;
+
+ regOffs = GetRegOffsInCONTEXT(varLoc.vlRegReg.vlrrReg2);
+ locs->addr = (ULONG64)(ULONG_PTR)pCtx + regOffs;
+ locs->size = sizeof(SIZE_T);
+ locs->contextReg = true;
+ return 2;
+
+ case ICorDebugInfo::VLT_REG_STK:
+ regOffs = GetRegOffsInCONTEXT(varLoc.vlRegStk.vlrsReg);
+ locs->addr = (ULONG64)(ULONG_PTR)pCtx + regOffs;
+ locs->size = sizeof(SIZE_T);
+ locs->contextReg = true;
+ locs++;
+
+ regOffs = GetRegOffsInCONTEXT(varLoc.vlRegStk.vlrsStk.vlrssBaseReg);
+ baseReg = *(TADDR *)(regOffs + (BYTE*)pCtx);
+ locs->addr = baseReg + varLoc.vlRegStk.vlrsStk.vlrssOffset;
+ locs->size = sizeof(SIZE_T);
+ locs->contextReg = false;
+ return 2;
+
+ case ICorDebugInfo::VLT_STK_REG:
+ regOffs = GetRegOffsInCONTEXT(varLoc.vlStkReg.vlsrStk.vlsrsBaseReg);
+ baseReg = *(TADDR *)(regOffs + (BYTE*)pCtx);
+ locs->addr = baseReg + varLoc.vlStkReg.vlsrStk.vlsrsOffset;
+ locs->size = sizeof(SIZE_T);
+ locs->contextReg = false;
+ locs++;
+
+ regOffs = GetRegOffsInCONTEXT(varLoc.vlStkReg.vlsrReg);
+ locs->addr = (ULONG64)(ULONG_PTR)pCtx + regOffs;
+ locs->size = sizeof(SIZE_T);
+ locs->contextReg = true;
+ return 2;
+
+ case ICorDebugInfo::VLT_STK2:
+ regOffs = GetRegOffsInCONTEXT(varLoc.vlStk2.vls2BaseReg);
+ baseReg = *(TADDR *)(regOffs + (BYTE*)pCtx);
+ locs->addr = baseReg + varLoc.vlStk2.vls2Offset;
+ locs->size = 2 * sizeof(SIZE_T);
+ locs->contextReg = false;
+ return 1;
+
+ case ICorDebugInfo::VLT_FPSTK:
+ _ASSERTE(!"NYI");
+ return 0;
+
+ default:
+ _ASSERTE(!"Bad locType");
+ return 0;
+ }
+}
+
+
+BOOL CompareFiles(HANDLE hFile1,HANDLE hFile2)
+{
+
+ STATIC_CONTRACT_THROWS;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ BY_HANDLE_FILE_INFORMATION fileinfo1;
+ BY_HANDLE_FILE_INFORMATION fileinfo2;
+ if (!GetFileInformationByHandle(hFile1,&fileinfo1) ||
+ !GetFileInformationByHandle(hFile2,&fileinfo2))
+ ThrowLastError();
+ return fileinfo1.nFileIndexLow == fileinfo2.nFileIndexLow &&
+ fileinfo1.nFileIndexHigh == fileinfo2.nFileIndexHigh &&
+ fileinfo1.dwVolumeSerialNumber==fileinfo2.dwVolumeSerialNumber;
+}
+
+
+#ifndef DACCESS_COMPILE
+
+// Returns the location at which the variable
+// begins. Returns NULL for register vars. For reg-stack
+// split, it'll return the addr of the stack part.
+// This also works for VLT_REG (a single register).
+SIZE_T *NativeVarStackAddr(const ICorDebugInfo::VarLoc & varLoc,
+ PCONTEXT pCtx)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+ SIZE_T *dwAddr = NULL;
+
+ bool fByRef = false;
+ switch(varLoc.vlType)
+ {
+ SIZE_T regOffs;
+ const BYTE * baseReg;
+
+ case ICorDebugInfo::VLT_REG_BYREF:
+ fByRef = true; // fall through
+ case ICorDebugInfo::VLT_REG:
+ regOffs = GetRegOffsInCONTEXT(varLoc.vlReg.vlrReg);
+ dwAddr = (SIZE_T *)(regOffs + (BYTE*)pCtx);
+ if (fByRef)
+ {
+ dwAddr = (SIZE_T*)(*dwAddr);
+ }
+ LOG((LF_CORDB, LL_INFO100, "NVSA: VLT_REG @ 0x%x (by ref = %d)\n", dwAddr, fByRef));
+ break;
+
+ case ICorDebugInfo::VLT_STK_BYREF:
+ fByRef = true; // fall through
+ case ICorDebugInfo::VLT_STK:
+ regOffs = GetRegOffsInCONTEXT(varLoc.vlStk.vlsBaseReg);
+ baseReg = (const BYTE *)*(SIZE_T *)(regOffs + (BYTE*)pCtx);
+ dwAddr = (SIZE_T *)(baseReg + varLoc.vlStk.vlsOffset);
+ if (fByRef)
+ {
+ dwAddr = (SIZE_T*)(*dwAddr);
+ }
+ LOG((LF_CORDB, LL_INFO100, "NVSA: VLT_STK @ 0x%x (by ref = %d)\n", dwAddr, fByRef));
+ break;
+
+ case ICorDebugInfo::VLT_STK2:
+ // <TODO>@TODO : VLT_STK2 is overloaded to also mean VLT_STK_n.
+ // return FALSE if n > 2;</TODO>
+
+ regOffs = GetRegOffsInCONTEXT(varLoc.vlStk2.vls2BaseReg);
+ baseReg = (const BYTE *)*(SIZE_T *)(regOffs + (BYTE*)pCtx);
+ dwAddr = (SIZE_T *)(baseReg + varLoc.vlStk2.vls2Offset);
+ LOG((LF_CORDB, LL_INFO100, "NVSA: VLT_STK_2 @ 0x%x\n",dwAddr));
+ break;
+
+ case ICorDebugInfo::VLT_REG_STK:
+ regOffs = GetRegOffsInCONTEXT(varLoc.vlRegStk.vlrsStk.vlrssBaseReg);
+ baseReg = (const BYTE *)*(SIZE_T *)(regOffs + (BYTE*)pCtx);
+ dwAddr = (SIZE_T *)(baseReg + varLoc.vlRegStk.vlrsStk.vlrssOffset);
+ LOG((LF_CORDB, LL_INFO100, "NVSA: REG_STK @ 0x%x\n",dwAddr));
+ break;
+
+ case ICorDebugInfo::VLT_STK_REG:
+ regOffs = GetRegOffsInCONTEXT(varLoc.vlStkReg.vlsrStk.vlsrsBaseReg);
+ baseReg = (const BYTE *)*(SIZE_T *)(regOffs + (BYTE*)pCtx);
+ dwAddr = (SIZE_T *)(baseReg + varLoc.vlStkReg.vlsrStk.vlsrsOffset);
+ LOG((LF_CORDB, LL_INFO100, "NVSA: STK_REG @ 0x%x\n",dwAddr));
+ break;
+
+ case ICorDebugInfo::VLT_REG_REG:
+ case ICorDebugInfo::VLT_FPSTK:
+ _ASSERTE(!"NYI"); break;
+
+ default:
+ _ASSERTE(!"Bad locType"); break;
+ }
+
+ return dwAddr;
+
+}
+
+
+#if defined(_WIN64)
+void GetNativeVarValHelper(SIZE_T* dstAddrLow, SIZE_T* dstAddrHigh, SIZE_T* srcAddr, SIZE_T size)
+{
+ if (size == 1)
+ *(BYTE*)dstAddrLow = *(BYTE*)srcAddr;
+ else if (size == 2)
+ *(USHORT*)dstAddrLow = *(USHORT*)srcAddr;
+ else if (size == 4)
+ *(ULONG*)dstAddrLow = *(ULONG*)srcAddr;
+ else if (size == 8)
+ *dstAddrLow = *srcAddr;
+ else if (size == 16)
+ {
+ *dstAddrLow = *srcAddr;
+ *dstAddrHigh = *(srcAddr+1);
+ }
+ else
+ {
+ _ASSERTE(!"util.cpp - unreachable code.\n");
+ UNREACHABLE();
+ }
+}
+#endif // _WIN64
+
+
+bool GetNativeVarVal(const ICorDebugInfo::VarLoc & varLoc,
+ PCONTEXT pCtx,
+ SIZE_T * pVal1,
+ SIZE_T * pVal2
+ WIN64_ARG(SIZE_T cbSize))
+{
+
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+ switch(varLoc.vlType)
+ {
+#if !defined(_WIN64)
+ SIZE_T regOffs;
+
+ case ICorDebugInfo::VLT_REG:
+ *pVal1 = *NativeVarStackAddr(varLoc,pCtx);
+ break;
+
+ case ICorDebugInfo::VLT_STK:
+ *pVal1 = *NativeVarStackAddr(varLoc,pCtx);
+ break;
+
+ case ICorDebugInfo::VLT_STK2:
+ *pVal1 = *NativeVarStackAddr(varLoc,pCtx);
+ *pVal2 = *(NativeVarStackAddr(varLoc,pCtx)+ 1);
+ break;
+
+ case ICorDebugInfo::VLT_REG_REG:
+ regOffs = GetRegOffsInCONTEXT(varLoc.vlRegReg.vlrrReg1);
+ *pVal1 = *(SIZE_T *)(regOffs + (BYTE*)pCtx);
+ LOG((LF_CORDB, LL_INFO100, "GNVV: STK_REG_REG 1 @ 0x%x\n",
+ (SIZE_T *)(regOffs + (BYTE*)pCtx)));
+
+ regOffs = GetRegOffsInCONTEXT(varLoc.vlRegReg.vlrrReg2);
+ *pVal2 = *(SIZE_T *)(regOffs + (BYTE*)pCtx);
+ LOG((LF_CORDB, LL_INFO100, "GNVV: STK_REG_REG 2 @ 0x%x\n",
+ (SIZE_T *)(regOffs + (BYTE*)pCtx)));
+ break;
+
+ case ICorDebugInfo::VLT_REG_STK:
+ regOffs = GetRegOffsInCONTEXT(varLoc.vlRegStk.vlrsReg);
+ *pVal1 = *(SIZE_T *)(regOffs + (BYTE*)pCtx);
+ LOG((LF_CORDB, LL_INFO100, "GNVV: STK_REG_STK reg @ 0x%x\n",
+ (SIZE_T *)(regOffs + (BYTE*)pCtx)));
+ *pVal2 = *NativeVarStackAddr(varLoc,pCtx);
+ break;
+
+ case ICorDebugInfo::VLT_STK_REG:
+ *pVal1 = *NativeVarStackAddr(varLoc,pCtx);
+ regOffs = GetRegOffsInCONTEXT(varLoc.vlStkReg.vlsrReg);
+ *pVal2 = *(SIZE_T *)(regOffs + (BYTE*)pCtx);
+ LOG((LF_CORDB, LL_INFO100, "GNVV: STK_STK_REG reg @ 0x%x\n",
+ (SIZE_T *)(regOffs + (BYTE*)pCtx)));
+ break;
+
+ case ICorDebugInfo::VLT_FPSTK:
+ _ASSERTE(!"NYI"); break;
+
+#else // _WIN64
+ case ICorDebugInfo::VLT_REG:
+ case ICorDebugInfo::VLT_REG_FP:
+ case ICorDebugInfo::VLT_STK:
+ GetNativeVarValHelper(pVal1, pVal2, NativeVarStackAddr(varLoc, pCtx), cbSize);
+ break;
+
+ case ICorDebugInfo::VLT_REG_BYREF: // fall through
+ case ICorDebugInfo::VLT_STK_BYREF:
+ _ASSERTE(!"GNVV: This function should not be called for value types");
+ break;
+
+#endif // _WIN64
+
+ default:
+ _ASSERTE(!"Bad locType"); break;
+ }
+
+ return true;
+}
+
+
+#if defined(_WIN64)
+void SetNativeVarValHelper(SIZE_T* dstAddr, SIZE_T valueLow, SIZE_T valueHigh, SIZE_T size)
+{
+ if (size == 1)
+ *(BYTE*)dstAddr = (BYTE)valueLow;
+ else if (size == 2)
+ *(USHORT*)dstAddr = (USHORT)valueLow;
+ else if (size == 4)
+ *(ULONG*)dstAddr = (ULONG)valueLow;
+ else if (size == 8)
+ *dstAddr = valueLow;
+ else if (size == 16)
+ {
+ *dstAddr = valueLow;
+ *(dstAddr+1) = valueHigh;
+ }
+ else
+ {
+ _ASSERTE(!"util.cpp - unreachable code.\n");
+ UNREACHABLE();
+ }
+}
+#endif // _WIN64
+
+
+bool SetNativeVarVal(const ICorDebugInfo::VarLoc & varLoc,
+ PCONTEXT pCtx,
+ SIZE_T val1,
+ SIZE_T val2
+ WIN64_ARG(SIZE_T cbSize))
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+ switch(varLoc.vlType)
+ {
+#if !defined(_WIN64)
+ SIZE_T regOffs;
+
+ case ICorDebugInfo::VLT_REG:
+ *NativeVarStackAddr(varLoc,pCtx) = val1;
+ break;
+
+ case ICorDebugInfo::VLT_STK:
+ *NativeVarStackAddr(varLoc,pCtx) = val1;
+ break;
+
+ case ICorDebugInfo::VLT_STK2:
+ *NativeVarStackAddr(varLoc,pCtx) = val1;
+ *(NativeVarStackAddr(varLoc,pCtx)+ 1) = val2;
+ break;
+
+ case ICorDebugInfo::VLT_REG_REG:
+ regOffs = GetRegOffsInCONTEXT(varLoc.vlRegReg.vlrrReg1);
+ *(SIZE_T *)(regOffs + (BYTE*)pCtx) = val1;
+ LOG((LF_CORDB, LL_INFO100, "SNVV: STK_REG_REG 1 @ 0x%x\n",
+ (SIZE_T *)(regOffs + (BYTE*)pCtx)));
+
+ regOffs = GetRegOffsInCONTEXT(varLoc.vlRegReg.vlrrReg2);
+ *(SIZE_T *)(regOffs + (BYTE*)pCtx) = val2;
+ LOG((LF_CORDB, LL_INFO100, "SNVV: STK_REG_REG 2 @ 0x%x\n",
+ (SIZE_T *)(regOffs + (BYTE*)pCtx)));
+ break;
+
+ case ICorDebugInfo::VLT_REG_STK:
+ regOffs = GetRegOffsInCONTEXT(varLoc.vlRegStk.vlrsReg);
+ *(SIZE_T *)(regOffs + (BYTE*)pCtx) = val1;
+ LOG((LF_CORDB, LL_INFO100, "SNVV: STK_REG_STK reg @ 0x%x\n",
+ (SIZE_T *)(regOffs + (BYTE*)pCtx)));
+ *NativeVarStackAddr(varLoc,pCtx) = val2;
+ break;
+
+ case ICorDebugInfo::VLT_STK_REG:
+ *NativeVarStackAddr(varLoc,pCtx) = val1;
+ regOffs = GetRegOffsInCONTEXT(varLoc.vlStkReg.vlsrReg);
+ *(SIZE_T *)(regOffs + (BYTE*)pCtx) = val2;
+ LOG((LF_CORDB, LL_INFO100, "SNVV: STK_STK_REG reg @ 0x%x\n",
+ (SIZE_T *)(regOffs + (BYTE*)pCtx)));
+ break;
+
+ case ICorDebugInfo::VLT_FPSTK:
+ _ASSERTE(!"NYI"); break;
+
+#else // _WIN64
+ case ICorDebugInfo::VLT_REG:
+ case ICorDebugInfo::VLT_REG_FP:
+ case ICorDebugInfo::VLT_STK:
+ SetNativeVarValHelper(NativeVarStackAddr(varLoc, pCtx), val1, val2, cbSize);
+ break;
+
+ case ICorDebugInfo::VLT_REG_BYREF: // fall through
+ case ICorDebugInfo::VLT_STK_BYREF:
+ _ASSERTE(!"GNVV: This function should not be called for value types");
+ break;
+
+#endif // _WIN64
+
+ default:
+ _ASSERTE(!"Bad locType"); break;
+ }
+
+ return true;
+}
+
+HRESULT VMPostError( // Returned error.
+ HRESULT hrRpt, // Reported error.
+ ...) // Error arguments.
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ GCX_PREEMP();
+
+ va_list marker; // User text.
+ va_start(marker, hrRpt);
+ hrRpt = PostErrorVA(hrRpt, marker);
+ va_end(marker);
+
+ return hrRpt;
+}
+
+#ifndef CROSSGEN_COMPILE
+void VMDumpCOMErrors(HRESULT hrErr)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_PREEMPTIVE;
+ PRECONDITION(FAILED(hrErr));
+ }
+ CONTRACTL_END;
+
+ SafeComHolderPreemp<IErrorInfo> pIErr(NULL);// Error interface.
+ BSTRHolder bstrDesc(NULL); // Description text.
+
+ // Try to get an error info object and display the message.
+ if (SafeGetErrorInfo(&pIErr) == S_OK && pIErr->GetDescription(&bstrDesc) == S_OK)
+ {
+ EEMessageBoxCatastrophic(IDS_EE_GENERIC, IDS_FATAL_ERROR, (BSTR)bstrDesc);
+ }
+ else
+ {
+ // Just give out the failed hr return code.
+ EEMessageBoxCatastrophic(IDS_COMPLUS_ERROR, IDS_FATAL_ERROR, hrErr);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Helper method to load mscorsn.dll. It is used when an app requests a legacy
+// mode where mscorsn.dll it to be loaded during startup.
+//-----------------------------------------------------------------------------
+const WCHAR g_pwzOldStrongNameLibrary[] = W("mscorsn.dll");
+#define cchOldStrongNameLibrary ( \
+ (sizeof(g_pwzOldStrongNameLibrary)/sizeof(WCHAR)))
+
+HRESULT LoadMscorsn()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_TRIGGERS;
+ INJECT_FAULT(return FALSE;);
+ }
+ CONTRACTL_END;
+
+ DWORD size = 0;
+ HRESULT hr = GetInternalSystemDirectory(NULL, &size);
+ if (hr != HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER))
+ return hr;
+
+ DWORD dwLength = size + cchOldStrongNameLibrary;
+ if (dwLength < size)
+ return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW);
+ NewArrayHolder<WCHAR> wszPath(new (nothrow) WCHAR[dwLength]);
+ if (!wszPath)
+ return E_OUTOFMEMORY;
+
+ hr = GetInternalSystemDirectory(wszPath, &size);
+ if (FAILED(hr))
+ return hr;
+
+ wcscat_s(wszPath, dwLength, g_pwzOldStrongNameLibrary);
+ CLRLoadLibrary(wszPath);
+ return S_OK;
+}
+
+#ifndef FEATURE_PAL
+
+//-----------------------------------------------------------------------------
+// WszSHGetFolderPath
+//
+// @func takes the CSIDL of a folder and returns the path name
+//
+// @rdesc Result Handle
+//-----------------------------------------------------------------------------------
+HRESULT WszSHGetFolderPath(
+ HWND hwndOwner,
+ int nFolder,
+ HANDLE hToken,
+ DWORD dwFlags,
+ size_t cchPathMax,
+ __out_ecount(MAX_PATH) LPWSTR pwszPath)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ MODE_PREEMPTIVE;
+ INJECT_FAULT(return E_OUTOFMEMORY;);
+ }
+ CONTRACTL_END;
+
+ // SHGetFolderPath requirement: path buffer >= MAX_PATH chars
+ _ASSERTE(cchPathMax >= MAX_PATH);
+
+ HRESULT hr;
+ ULONG maxLength = MAX_PATH;
+ HMODULE _hmodShell32 = 0;
+ HMODULE _hmodSHFolder = 0;
+
+ ETWOnStartup (LdLibShFolder_V1, LdLibShFolderEnd_V1);
+
+ typedef HRESULT (*PFNSHGETFOLDERPATH_W) (HWND hwndOwner, int nFolder, HANDLE hToken, DWORD dwFlags, LPWSTR pszPath);
+ static PFNSHGETFOLDERPATH_W pfnW = NULL;
+ if (NULL == pfnW)
+ {
+ _hmodShell32 = CLRLoadLibrary(W("shell32.dll"));
+
+ if (_hmodShell32)
+ pfnW = (PFNSHGETFOLDERPATH_W)GetProcAddress(_hmodShell32, "SHGetFolderPathW");
+
+ if (NULL == pfnW)
+ {
+ if (NULL == _hmodSHFolder)
+ _hmodSHFolder = CLRLoadLibrary(W("shfolder.dll"));
+
+ if (_hmodSHFolder)
+ pfnW = (PFNSHGETFOLDERPATH_W)GetProcAddress(_hmodSHFolder, "SHGetFolderPathW");
+ }
+ }
+
+ if (pfnW)
+ hr = pfnW(hwndOwner, nFolder, hToken, dwFlags, pwszPath);
+ else
+ hr = HRESULT_FROM_WIN32(GetLastError());
+
+ // NOTE: We leak the module handles and let the OS gather them at process shutdown.
+
+ return hr;
+}
+
+//-----------------------------------------------------------------------------
+// WszShellExecute
+//
+// @func calls ShellExecute with the provided parameters
+//
+// @rdesc Result
+//-----------------------------------------------------------------------------------
+HRESULT WszShellExecute(
+ HWND hwnd,
+ LPCTSTR lpOperation,
+ LPCTSTR lpFile,
+ LPCTSTR lpParameters,
+ LPCTSTR lpDirectory,
+ INT nShowCmd)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ MODE_PREEMPTIVE;
+ INJECT_FAULT(return E_OUTOFMEMORY;);
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = S_OK;
+ HMODULE _hmodShell32 = 0;
+
+ typedef HINSTANCE (*PFNSHELLEXECUTE_W) (HWND hwnd, LPCTSTR lpOperation, LPCTSTR lpFile, LPCTSTR lpParameters, LPCTSTR lpDirectory, INT nShowCmd);
+ static PFNSHELLEXECUTE_W pfnW = NULL;
+ if (NULL == pfnW)
+ {
+ _hmodShell32 = CLRLoadLibrary(W("shell32.dll"));
+
+ if (_hmodShell32)
+ pfnW = (PFNSHELLEXECUTE_W)GetProcAddress(_hmodShell32, "ShellExecuteW");
+ }
+
+ if (pfnW)
+ {
+ HINSTANCE hSE = pfnW(hwnd, lpOperation, lpFile, lpParameters, lpDirectory, nShowCmd);
+
+ if ((int) hSE <= 32)
+ {
+ hr = HRESULT_FROM_WIN32((int) hSE);
+ }
+ }
+ else
+ {
+ hr = HRESULT_FROM_WIN32(GetLastError());
+ }
+
+ // NOTE: We leak the module handles and let the OS gather them at process shutdown.
+
+ return hr;
+}
+
+#ifndef DACCESS_COMPILE
+//-----------------------------------------------------------------------------
+
+//-----------------------------------------------------------------------------
+// WszShellExecuteEx
+//
+// @func calls ShellExecuteEx with the provided parameters
+//
+// @rdesc Result
+//-----------------------------------------------------------------------------------
+HRESULT WszShellExecuteEx(
+ LPSHELLEXECUTEINFO lpExecInfo)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ MODE_PREEMPTIVE;
+ INJECT_FAULT(return E_OUTOFMEMORY;);
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = S_OK;
+ HMODULE _hmodShell32 = 0;
+
+ typedef BOOL (*PFNSHELLEXECUTEEX_W) (LPSHELLEXECUTEINFO lpExecInfo);
+ static PFNSHELLEXECUTEEX_W pfnW = NULL;
+ if (NULL == pfnW)
+ {
+ _hmodShell32 = CLRLoadLibrary(W("shell32.dll"));
+
+ if (_hmodShell32)
+ pfnW = (PFNSHELLEXECUTEEX_W)GetProcAddress(_hmodShell32, "ShellExecuteExW");
+ }
+
+ if (pfnW)
+ {
+ BOOL bSE = pfnW(lpExecInfo);
+
+ if (bSE)
+ {
+ hr = HRESULT_FROM_WIN32(GetLastError());
+ }
+ }
+ else
+ {
+ hr = HRESULT_FROM_WIN32(GetLastError());
+ }
+
+ // NOTE: We leak the module handles and let the OS gather them at process shutdown.
+
+ return hr;
+}
+
+#endif // #ifndef DACCESS_COMPILE
+
+BOOL IsUsingValidAppDataPath(__in_z WCHAR *userPath)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ MODE_PREEMPTIVE;
+ }
+ CONTRACTL_END;
+
+ WCHAR defaultPath[MAX_PATH];
+ HRESULT hr;
+ HANDLE hToken;
+
+ hToken = (HANDLE)(-1);
+
+ hr = WszSHGetFolderPath(NULL, CSIDL_APPDATA, hToken, SHGFP_TYPE_CURRENT, MAX_PATH, defaultPath);
+ if (FAILED(hr))
+ {
+ hr = WszSHGetFolderPath(NULL, CSIDL_APPDATA, hToken, SHGFP_TYPE_DEFAULT, MAX_PATH, defaultPath);
+ }
+ if (FAILED(hr))
+ return FALSE;
+
+ int result = wcscmp(defaultPath, userPath);
+
+ return result != 0;
+}
+
+#define FOLDER_LOCAL_SETTINGS_W W("Local Settings")
+#define FOLDER_APP_DATA_W W("\\Application Data")
+#define FOLDER_APP_DATA "\\Application Data"
+
+// Gets the location for roaming and local AppData
+BOOL GetUserDir(__out_ecount(bufferCount) WCHAR * buffer, size_t bufferCount, BOOL fRoaming)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ MODE_PREEMPTIVE;
+ INJECT_FAULT(return FALSE;);
+ }
+ CONTRACTL_END;
+
+ // SHGetFolderPath will return the default user profile if the context is that of a user
+ // without a user profile. Since we never want to end up writing files into the default profile
+ // which is used as a template for future user profiles, we first try to find out if the user
+ // profile is not loaded; and if that's the case we return an error.
+
+ if (!IsUserProfileLoaded())
+ return FALSE;
+
+ HRESULT hr;
+
+ // In Windows ME, there is currently a bug that makes local appdata and roaming appdata
+ // point to the same location, so we've decided to "do our own thing" and add \Local Settings before \Application Data
+ if (!fRoaming) {
+ WCHAR appdatafolder[MAX_PATH];
+ hr = WszSHGetFolderPath(NULL, CSIDL_APPDATA|CSIDL_FLAG_CREATE, NULL, SHGFP_TYPE_CURRENT, MAX_PATH, appdatafolder);
+ if (FAILED(hr))
+ {
+ hr = WszSHGetFolderPath(NULL, CSIDL_APPDATA|CSIDL_FLAG_CREATE, NULL, SHGFP_TYPE_DEFAULT, MAX_PATH, appdatafolder);
+ }
+ if (FAILED(hr))
+ return FALSE;
+ hr = WszSHGetFolderPath(NULL, CSIDL_LOCAL_APPDATA|CSIDL_FLAG_CREATE, NULL, SHGFP_TYPE_CURRENT, bufferCount, buffer);
+ if (FAILED(hr))
+ {
+ hr = WszSHGetFolderPath(NULL, CSIDL_LOCAL_APPDATA|CSIDL_FLAG_CREATE, NULL, SHGFP_TYPE_DEFAULT, bufferCount, buffer);
+ }
+ if (FAILED(hr))
+ return FALSE;
+
+ // folders are the same or failed to get local folder
+
+ if (!wcscmp(appdatafolder, buffer))
+ {
+ WCHAR tempPartialPath[MAX_PATH];
+ ULONG slen = (ULONG)wcslen(buffer);
+
+ if (buffer[slen - 1] == W('\\'))
+ {
+ --slen;
+ }
+
+ // Search for the parent directory.
+
+ WCHAR* parentDirectoryEnd = &buffer[slen - 1];
+ tempPartialPath[0] = W('\0');
+
+ for (ULONG index = slen - 1; index > 0; --index)
+ {
+ if (buffer[index] == W('\\'))
+ {
+ if (wcslen(&buffer[index]) >= NumItems(tempPartialPath))
+ {
+ _ASSERTE(!"Buffer not large enough");
+ return FALSE;
+ }
+
+ wcscpy_s( tempPartialPath, COUNTOF(tempPartialPath), &buffer[index] );
+ parentDirectoryEnd = &buffer[index+1];
+ break;
+ }
+ }
+
+ // Create the intermediate directory if it is not present
+ if ((parentDirectoryEnd + wcslen(FOLDER_LOCAL_SETTINGS_W)) >= (buffer + bufferCount))
+ {
+ _ASSERTE(!"Buffer not large enough");
+ return FALSE;
+ }
+
+ SIZE_T cchSafe;
+ // Prefast overflow sanity check the subtraction.
+ if (!ClrSafeInt<SIZE_T>::subtraction(bufferCount, (parentDirectoryEnd - buffer), cchSafe))
+ {
+ _ASSERTE(!"ClrSafeInt: Buffer is not large enough");
+ return FALSE;
+ }
+
+ wcscpy_s(parentDirectoryEnd, cchSafe, FOLDER_LOCAL_SETTINGS_W);
+
+ LONG lresult;
+
+ {
+ // Check if the directory is already present
+ lresult = WszGetFileAttributes(buffer);
+
+ if (lresult == -1)
+ {
+ if (!WszCreateDirectory(buffer, NULL) &&
+ !(WszGetFileAttributes(buffer) & FILE_ATTRIBUTE_DIRECTORY))
+ return FALSE;
+ }
+ else if ((lresult & FILE_ATTRIBUTE_DIRECTORY) == 0)
+ {
+ return FALSE;
+ }
+ }
+ if ((bufferCount - wcslen(buffer)) <= wcslen(tempPartialPath))
+ {
+ _ASSERTE(!"Buffer not large enough");
+ return FALSE;
+ }
+
+ wcscat_s(buffer, bufferCount, tempPartialPath);
+
+ // Check if the directory is already present
+ lresult = WszGetFileAttributes(buffer);
+
+ if (lresult == -1)
+ {
+ if (!WszCreateDirectory(buffer, NULL) &&
+ !(WszGetFileAttributes(buffer) & FILE_ATTRIBUTE_DIRECTORY))
+ return FALSE;
+ }
+ else if ((lresult & FILE_ATTRIBUTE_DIRECTORY) == 0)
+ {
+ return FALSE;
+ }
+ }
+ }
+ else {
+ hr = WszSHGetFolderPath(NULL, CSIDL_APPDATA|CSIDL_FLAG_CREATE, NULL, SHGFP_TYPE_CURRENT, bufferCount, buffer);
+ if (FAILED(hr))
+ {
+ hr = WszSHGetFolderPath(NULL, CSIDL_APPDATA|CSIDL_FLAG_CREATE, NULL, SHGFP_TYPE_DEFAULT, bufferCount, buffer);
+ }
+ if (FAILED(hr))
+ return FALSE;
+
+ if (!IsUsingValidAppDataPath(buffer))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+const WCHAR PROFILE_LIST_PATH[] = W("Software\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList\\");
+#define nProfileListPathKeyLen ( \
+ sizeof(PROFILE_LIST_PATH)/sizeof(WCHAR))
+
+HRESULT GetUserSidString (HANDLE hToken, __deref_out LPWSTR *pwszSid) {
+ DWORD dwSize = 0;
+ GetTokenInformation(hToken, TokenUser, NULL, 0, &dwSize);
+ NewArrayHolder<BYTE> pb(new (nothrow) BYTE[dwSize]);
+ if (pb == NULL)
+ return E_OUTOFMEMORY;
+ if (!GetTokenInformation(hToken, TokenUser, pb, dwSize, &dwSize))
+ return HRESULT_FROM_GetLastError();
+
+ PTOKEN_USER pUser = (PTOKEN_USER) pb.GetValue();
+
+ typedef BOOL (*CONVERTSIDTOSTRINGSID_W) (PSID Sid, LPWSTR* StringSid);
+ static CONVERTSIDTOSTRINGSID_W pfnW = NULL;
+ if (NULL == pfnW) {
+ HMODULE hModAdvapi32 = CLRLoadLibrary(W("advapi32.dll"));
+ if (hModAdvapi32)
+ pfnW = (CONVERTSIDTOSTRINGSID_W) GetProcAddress(hModAdvapi32, "ConvertSidToStringSidW");
+ }
+
+ if (!pfnW)
+ return E_NOTIMPL;
+ if (!pfnW(pUser->User.Sid, pwszSid))
+ return HRESULT_FROM_GetLastError();
+ return S_OK;
+}
+
+BOOL IsUserProfileLoaded() {
+ HandleHolder hToken;
+ if (!OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE, &hToken))
+ if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
+ return FALSE;
+
+ // Get the SID string
+ LPWSTR wszSid = NULL;
+ if (FAILED(GetUserSidString(hToken, &wszSid)))
+ return FALSE;
+
+ // Concatenate the Sid string with the profile list path
+ size_t cchProfileRegPath = nProfileListPathKeyLen + wcslen(wszSid) + 1;
+ NewArrayHolder<WCHAR> wszProfileRegPath(new (nothrow) WCHAR[cchProfileRegPath]);
+ if (wszProfileRegPath == NULL) {
+#undef LocalFree
+ LocalFree(wszSid);
+#define LocalFree(hMem) Dont_Use_LocalFree(hMem)
+ return FALSE;
+ }
+ wcscpy_s(wszProfileRegPath, cchProfileRegPath, PROFILE_LIST_PATH);
+ wcscat_s(wszProfileRegPath, cchProfileRegPath, wszSid);
+
+#undef LocalFree
+ LocalFree(wszSid);
+#define LocalFree(hMem) Dont_Use_LocalFree(hMem)
+
+ // Open the user profile registry key
+ HKEYHolder hKey;
+ return (WszRegOpenKeyEx(HKEY_LOCAL_MACHINE, wszProfileRegPath, 0, KEY_READ, &hKey) == ERROR_SUCCESS);
+}
+
+BOOL GetInternetCacheDir(__out_ecount(bufferCount) WCHAR * buffer, size_t bufferCount)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_TRIGGERS;
+ INJECT_FAULT(return FALSE;);
+ }
+ CONTRACTL_END;
+
+ _ASSERTE( bufferCount == MAX_PATH && "You should pass in a buffer of size MAX_PATH" );
+
+ HRESULT hr = WszSHGetFolderPath( NULL, CSIDL_INTERNET_CACHE, NULL, SHGFP_TYPE_CURRENT, bufferCount, buffer );
+ if (FAILED(hr))
+ hr = WszSHGetFolderPath( NULL, CSIDL_INTERNET_CACHE, NULL, SHGFP_TYPE_DEFAULT, bufferCount, buffer );
+
+ return SUCCEEDED(hr);
+}
+
+//-----------------------------------------------------------------------------
+// Wrap registry functions to use CQuickWSTR to allocate space. This does it
+// in a stack friendly manner.
+//-----------------------------------------------------------------------------
+LONG UtilRegEnumKey(HKEY hKey, // handle to key to query
+ DWORD dwIndex, // index of subkey to query
+ CQuickWSTR* lpName) // buffer for subkey name
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ INJECT_FAULT(return ERROR_NOT_ENOUGH_MEMORY;);
+ }
+ CONTRACTL_END;
+
+ DWORD size = (DWORD)lpName->MaxSize();
+ LONG result = WszRegEnumKeyEx(hKey,
+ dwIndex,
+ lpName->Ptr(),
+ &size,
+ NULL,
+ NULL,
+ NULL,
+ NULL);
+
+ if (result == ERROR_SUCCESS || result == ERROR_MORE_DATA) {
+
+ // Grow or shrink buffer to correct size
+ if (lpName->ReSizeNoThrow(size+1) != NOERROR)
+ result = ERROR_NOT_ENOUGH_MEMORY;
+
+ if (result == ERROR_MORE_DATA) {
+ size = (DWORD)lpName->MaxSize();
+ result = WszRegEnumKeyEx(hKey,
+ dwIndex,
+ lpName->Ptr(),
+ &size,
+ NULL,
+ NULL,
+ NULL,
+ NULL);
+ }
+ }
+
+ return result;
+}
+
+LONG UtilRegQueryStringValueEx(HKEY hKey, // handle to key to query
+ LPCWSTR lpValueName, // address of name of value to query
+ LPDWORD lpReserved, // reserved
+ LPDWORD lpType, // address of buffer for value type
+ CQuickWSTR* lpData)// data buffer
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ INJECT_FAULT(return ERROR_NOT_ENOUGH_MEMORY;);
+ }
+ CONTRACTL_END;
+
+ DWORD size = (DWORD)lpData->MaxSize();
+ LONG result = WszRegQueryValueEx(hKey,
+ lpValueName,
+ lpReserved,
+ lpType,
+ (LPBYTE) lpData->Ptr(),
+ &size);
+
+ if (result == ERROR_SUCCESS || result == ERROR_MORE_DATA) {
+
+ // Grow or shrink buffer to correct size
+ if (lpData->ReSizeNoThrow(size+1) != NOERROR)
+ result = ERROR_NOT_ENOUGH_MEMORY;
+
+ if (result == ERROR_MORE_DATA) {
+ size = (DWORD)lpData->MaxSize();
+ result = WszRegQueryValueEx(hKey,
+ lpValueName,
+ lpReserved,
+ lpType,
+ (LPBYTE) lpData->Ptr(),
+ &size);
+ }
+ }
+
+ return result;
+}
+
+BOOL ReportEventCLR(
+ WORD wType,
+ WORD wCategory,
+ DWORD dwEventID,
+ PSID lpUserSid,
+ SString * message)
+{
+ CONTRACTL {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ } CONTRACTL_END;
+
+ GCX_PREEMP();
+
+ SString buff;
+ buff.Printf(W(".NET Runtime version %s - %s"), VER_FILEVERSION_STR_L, message->GetUnicode());
+
+ DWORD dwRetVal = ClrReportEvent(W(".NET Runtime"),
+ wType, // event type
+ wCategory, // category
+ dwEventID, // event identifier
+ lpUserSid, // user security identifier
+ buff.GetUnicode()); // one substitution string
+
+ // Return BOOLEAN based upon return code
+ return (dwRetVal == ERROR_SUCCESS)?TRUE:FALSE;
+}
+
+// This function checks to see if GetLogicalProcessorInformation API is supported.
+// On success, this function allocates a SLPI array, sets nEntries to number
+// of elements in the SLPI array and returns a pointer to the SLPI array after filling it with information.
+//
+// Note: If successful, IsGLPISupported allocates memory for the SLPI array and expects the caller to
+// free the memory once the caller is done using the information in the SLPI array.
+//
+// If the API is not supported or any failure, returns NULL
+//
+SYSTEM_LOGICAL_PROCESSOR_INFORMATION *IsGLPISupported( PDWORD nEntries )
+{
+ DWORD cbslpi = 0;
+ DWORD dwNumElements = 0;
+ SYSTEM_LOGICAL_PROCESSOR_INFORMATION *pslpi = NULL;
+
+ // We setup the first call to GetLogicalProcessorInformation to fail so that we can obtain
+ // the size of the buffer required to allocate for the SLPI array that is returned
+
+ if (!GetLogicalProcessorInformation(pslpi, &cbslpi) &&
+ GetLastError() != ERROR_INSUFFICIENT_BUFFER)
+ {
+ // If we fail with anything other than an ERROR_INSUFFICIENT_BUFFER here, we punt with failure.
+ return NULL;
+ }
+
+ _ASSERTE(cbslpi);
+
+ // compute the number of SLPI entries required to hold the information returned from GLPI
+
+ dwNumElements = cbslpi / sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION);
+
+ // allocate a buffer in the free heap to hold an array of SLPI entries from GLPI, number of elements in the array is dwNumElements
+
+ pslpi = new (nothrow) SYSTEM_LOGICAL_PROCESSOR_INFORMATION[ dwNumElements ];
+
+ if(pslpi == NULL)
+ {
+ // the memory allocation failed
+ return NULL;
+ }
+
+ // Make call to GetLogicalProcessorInformation. Returns array of SLPI structures
+
+ if (!GetLogicalProcessorInformation(pslpi, &cbslpi))
+ {
+ // GetLogicalProcessorInformation failed
+ delete[] pslpi ; //Allocation was fine but the API call itself failed and so we are releasing the memory before the return NULL.
+ return NULL ;
+ }
+
+ // GetLogicalProcessorInformation successful, set nEntries to number of entries in the SLPI array
+ *nEntries = dwNumElements;
+
+ return pslpi; // return pointer to SLPI array
+
+}//IsGLPISupported
+
+// This function returns the size of highest level cache on the physical chip. If it cannot
+// determine the cachesize this function returns 0.
+size_t GetLogicalProcessorCacheSizeFromOS()
+{
+ size_t cache_size = 0;
+ DWORD nEntries = 0;
+
+ // Try to use GetLogicalProcessorInformation API and get a valid pointer to the SLPI array if successful. Returns NULL
+ // if API not present or on failure.
+
+ SYSTEM_LOGICAL_PROCESSOR_INFORMATION *pslpi = IsGLPISupported(&nEntries) ;
+
+ if (pslpi == NULL)
+ {
+ // GetLogicalProcessorInformation not supported or failed.
+ goto Exit;
+ }
+
+ // Crack the information. Iterate through all the SLPI array entries for all processors in system.
+ // Will return the greatest of all the processor cache sizes or zero
+
+ size_t last_cache_size = 0;
+
+ for (DWORD i=0; i < nEntries; i++)
+ {
+ if (pslpi[i].Relationship == RelationCache)
+ {
+ last_cache_size = max(last_cache_size, pslpi[i].Cache.Size);
+ }
+ }
+ cache_size = last_cache_size;
+Exit:
+
+ if(pslpi)
+ delete[] pslpi; // release the memory allocated for the SLPI array.
+
+ return cache_size;
+}
+
+#endif // !FEATURE_PAL
+
+// This function returns the number of logical processors on a given physical chip. If it cannot
+// determine the number of logical cpus, or the machine is not populated uniformly with the same
+// type of processors, this function returns 0.
+
+DWORD GetLogicalCpuCountFromOS()
+{
+ // No CONTRACT possible because GetLogicalCpuCount uses SEH
+
+ STATIC_CONTRACT_THROWS;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+
+ static DWORD val = 0;
+ DWORD retVal = 0;
+
+// UNIXTODO: Implement the functionality in PAL?
+#ifndef FEATURE_PAL
+
+ DWORD nEntries = 0;
+
+ // Try to use GetLogicalProcessorInformation API and get a valid pointer to the SLPI array if successful. Returns NULL
+ // if API not present or on failure.
+ SYSTEM_LOGICAL_PROCESSOR_INFORMATION *pslpi = IsGLPISupported(&nEntries) ;
+
+ if (pslpi == NULL)
+ {
+ // GetLogicalProcessorInformation no supported
+ goto lDone;
+ }
+
+ DWORD prevcount = 0;
+ DWORD count = 1;
+
+ for (DWORD j = 0; j < nEntries; j++)
+ {
+ if (pslpi[j].Relationship == RelationProcessorCore)
+ {
+ // LTP_PC_SMT indicates HT or SMT
+ if (pslpi[j].ProcessorCore.Flags == LTP_PC_SMT)
+ {
+ SIZE_T pmask = pslpi[j].ProcessorMask;
+
+ // Count the processors in the mask
+ //
+ // These are not the fastest bit counters. There may be processor intrinsics
+ // (which would be best), but there are variants faster than these:
+ // See http://en.wikipedia.org/wiki/Hamming_weight.
+ // This is the naive implementation.
+#if !_WIN64
+ count = (pmask & 0x55555555) + ((pmask >> 1) & 0x55555555);
+ count = (count & 0x33333333) + ((count >> 2) & 0x33333333);
+ count = (count & 0x0F0F0F0F) + ((count >> 4) & 0x0F0F0F0F);
+ count = (count & 0x00FF00FF) + ((count >> 8) & 0x00FF00FF);
+ count = (count & 0x0000FFFF) + ((count >> 16)& 0x0000FFFF);
+#else
+ pmask = (pmask & 0x5555555555555555ull) + ((pmask >> 1) & 0x5555555555555555ull);
+ pmask = (pmask & 0x3333333333333333ull) + ((pmask >> 2) & 0x3333333333333333ull);
+ pmask = (pmask & 0x0f0f0f0f0f0f0f0full) + ((pmask >> 4) & 0x0f0f0f0f0f0f0f0full);
+ pmask = (pmask & 0x00ff00ff00ff00ffull) + ((pmask >> 8) & 0x00ff00ff00ff00ffull);
+ pmask = (pmask & 0x0000ffff0000ffffull) + ((pmask >> 16) & 0x0000ffff0000ffffull);
+ pmask = (pmask & 0x00000000ffffffffull) + ((pmask >> 32) & 0x00000000ffffffffull);
+ count = static_cast<DWORD>(pmask);
+#endif // !_WIN64 else
+ assert (count > 0);
+
+ if (prevcount)
+ {
+ if (count != prevcount)
+ {
+ retVal = 1; // masks are not symmetric
+ goto lDone;
+ }
+ }
+
+ prevcount = count;
+ }
+ }
+ }
+
+ retVal = count;
+
+lDone:
+
+ if(pslpi)
+ {
+ delete[] pslpi; // release the memory allocated for the SLPI array
+ }
+
+#endif // FEATURE_PAL
+
+ return retVal;
+}
+
+#if defined(_TARGET_X86_) || defined(_TARGET_AMD64_)
+
+#define CACHE_WAY_BITS 0xFFC00000 // number of cache WAYS-Associativity is returned in EBX[31:22] (10 bits) using cpuid function 4
+#define CACHE_PARTITION_BITS 0x003FF000 // number of cache Physical Partitions is returned in EBX[21:12] (10 bits) using cpuid function 4
+#define CACHE_LINESIZE_BITS 0x00000FFF // Linesize returned in EBX[11:0] (12 bits) using cpuid function 4
+
+#if defined(_TARGET_X86_)
+ // these are defined in cgenx86.cpp
+ extern DWORD getcpuid(DWORD arg1, unsigned char result[16]);
+ extern DWORD getextcpuid(DWORD arg1, DWORD arg2, unsigned char result[16]);
+#elif defined(_TARGET_AMD64_)
+ // these are defined in src\VM\AMD64\asmhelpers.asm
+ extern "C" DWORD __stdcall getcpuid(DWORD arg1, unsigned char result[16]);
+ extern "C" DWORD __stdcall getextcpuid(DWORD arg1, DWORD arg2, unsigned char result[16]);
+#endif
+
+
+// The following function uses a deterministic mechanism for enumerating/calculating the details of the cache hierarychy at runtime
+// by using deterministic cache parameter leafs on Prescott and higher processors.
+// If successful, this function returns the cache size in bytes of the highest level on-die cache. Returns 0 on failure.
+
+size_t GetIntelDeterministicCacheEnum()
+{
+ LIMITED_METHOD_CONTRACT;
+ size_t retVal = 0;
+ unsigned char buffer[16];
+
+ DWORD maxCpuid = getextcpuid(0,0,buffer);
+
+ DWORD* dwBuffer = (DWORD*)buffer;
+
+ if( (maxCpuid > 3) && (maxCpuid < 0x80000000) ) // Deterministic Cache Enum is Supported
+ {
+ DWORD dwCacheWays, dwCachePartitions, dwLineSize, dwSets;
+ DWORD retEAX = 0;
+ DWORD loopECX = 0;
+ size_t maxSize = 0;
+ size_t curSize = 0;
+
+ // Make First call to getextcpuid with loopECX=0. loopECX provides an index indicating which level to return information about.
+ // The second parameter is input EAX=4, to specify we want deterministic cache parameter leaf information.
+ // getextcpuid with EAX=4 should be executed with loopECX = 0,1, ... until retEAX [4:0] contains 00000b, indicating no more
+ // cache levels are supported.
+
+ getextcpuid(loopECX, 4, buffer);
+ retEAX = dwBuffer[0]; // get EAX
+
+ int i = 0;
+ while(retEAX & 0x1f) // Crack cache enums and loop while EAX > 0
+ {
+
+ dwCacheWays = (dwBuffer[1] & CACHE_WAY_BITS) >> 22;
+ dwCachePartitions = (dwBuffer[1] & CACHE_PARTITION_BITS) >> 12;
+ dwLineSize = dwBuffer[1] & CACHE_LINESIZE_BITS;
+ dwSets = dwBuffer[2]; // ECX
+
+ curSize = (dwCacheWays+1)*(dwCachePartitions+1)*(dwLineSize+1)*(dwSets+1);
+
+ if (maxSize < curSize)
+ maxSize = curSize;
+
+ loopECX++;
+ getextcpuid(loopECX, 4, buffer);
+ retEAX = dwBuffer[0] ; // get EAX[4:0];
+ i++;
+ if (i > 16) // prevent infinite looping
+ return 0;
+ }
+ retVal = maxSize;
+ }
+
+ return retVal ;
+}
+
+// The following function uses CPUID function 2 with descriptor values to determine the cache size. This requires a-priori
+// knowledge of the descriptor values. This works on gallatin and prior processors (already released processors).
+// If successful, this function returns the cache size in bytes of the highest level on-die cache. Returns 0 on failure.
+
+size_t GetIntelDescriptorValuesCache()
+{
+ LIMITED_METHOD_CONTRACT;
+ size_t size = 0;
+ size_t maxSize = 0;
+ unsigned char buffer[16];
+
+ getextcpuid(0,2, buffer); // call CPUID with EAX function 2H to obtain cache descriptor values
+
+ for (int i = buffer[0]; --i >= 0; )
+ {
+ int j;
+ for (j = 3; j < 16; j += 4)
+ {
+ // if the information in a register is marked invalid, set to null descriptors
+ if (buffer[j] & 0x80)
+ {
+ buffer[j-3] = 0;
+ buffer[j-2] = 0;
+ buffer[j-1] = 0;
+ buffer[j-0] = 0;
+ }
+ }
+
+ for (j = 1; j < 16; j++)
+ {
+ switch (buffer[j]) // need to add descriptor values for 8M and 12M when they become known
+ {
+ case 0x41:
+ case 0x79:
+ size = 128*1024;
+ break;
+
+ case 0x42:
+ case 0x7A:
+ case 0x82:
+ size = 256*1024;
+ break;
+
+ case 0x22:
+ case 0x43:
+ case 0x7B:
+ case 0x83:
+ case 0x86:
+ size = 512*1024;
+ break;
+
+ case 0x23:
+ case 0x44:
+ case 0x7C:
+ case 0x84:
+ case 0x87:
+ size = 1024*1024;
+ break;
+
+ case 0x25:
+ case 0x45:
+ case 0x85:
+ size = 2*1024*1024;
+ break;
+
+ case 0x29:
+ size = 4*1024*1024;
+ break;
+ }
+ if (maxSize < size)
+ maxSize = size;
+ }
+
+ if (i > 0)
+ getextcpuid(0,2, buffer);
+ }
+ return maxSize;
+}
+
+
+
+#define NUM_LOGICAL_BITS 0x00FF0000 // EBX[23:16] Bit 16-23 in ebx contains the number of logical
+ // processors per physical processor (using cpuid function 1)
+#define INITIAL_APIC_ID_BITS 0xFF000000 // EBX[31:24] Bits 24-31 (8 bits) return the 8-bit unique
+ // initial APIC ID for the processor this code is running on.
+ // Default value = 0xff if HT is not supported
+
+// This function uses CPUID function 1 to return the number of logical processors on a given physical chip.
+// It returns the number of logicals processors on a physical chip.
+
+DWORD GetLogicalCpuCountFallback()
+{
+ BYTE LogicalNum = 0;
+ BYTE PhysicalNum = 0;
+ DWORD lProcCounter = 0;
+ unsigned char buffer[16];
+
+ DWORD* dwBuffer = (DWORD*)buffer;
+ DWORD retVal = 1;
+
+ getextcpuid(0,1, buffer); //call CPUID with EAX=1
+
+ if (dwBuffer[3] & (1<<28)) // edx:bit 28 is HT bit
+ {
+ PhysicalNum = (BYTE) g_SystemInfo.dwNumberOfProcessors ; // total # of processors
+ LogicalNum = (BYTE) ((dwBuffer[1] & NUM_LOGICAL_BITS) >> 16); // # of logical per physical
+
+ if(LogicalNum > 1)
+ {
+#ifdef FEATURE_CORESYSTEM
+ // CoreSystem doesn't expose GetProcessAffinityMask or SetProcessAffinityMask or anything
+ // functionally equivalent. Just assume 1:1 mapping if we get here (in reality we shouldn't since
+ // all CoreSystems support GetLogicalProcessorInformation so GetLogicalCpuCountFromOS should have
+ // taken care of everything.
+ goto fDone;
+#else // FEATURE_CORESYSTEM
+ HANDLE hCurrentProcessHandle;
+ DWORD_PTR dwProcessAffinity;
+ DWORD_PTR dwSystemAffinity;
+ DWORD_PTR dwAffinityMask;
+
+ // Calculate the appropriate shifts and mask based on the
+ // number of logical processors.
+
+ BYTE i = 1, PHY_ID_MASK = 0xFF, PHY_ID_SHIFT = 0;
+ while (i < LogicalNum)
+ {
+ i *= 2;
+ PHY_ID_MASK <<= 1;
+ PHY_ID_SHIFT++;
+ }
+ hCurrentProcessHandle = GetCurrentProcess();
+
+ GetProcessAffinityMask(hCurrentProcessHandle, &dwProcessAffinity, &dwSystemAffinity);
+
+ // Check if available process affinity mask is equal to the available system affinity mask
+ // If the masks are equal, then all the processors the OS utilizes are available to the
+ // application.
+
+ if (dwProcessAffinity != dwSystemAffinity)
+ {
+ retVal = 0;
+ goto fDone;
+ }
+
+ dwAffinityMask = 1;
+
+ // loop over all processors, running APIC ID retrieval code starting
+ // with the first one by setting process affinity.
+ while (dwAffinityMask != 0 && dwAffinityMask <= dwProcessAffinity)
+ {
+ // Check if this CPU is available
+ if (dwAffinityMask & dwProcessAffinity)
+ {
+ if (SetProcessAffinityMask(hCurrentProcessHandle, dwAffinityMask))
+ {
+ BYTE APIC_ID, LOG_ID, PHY_ID;
+ __SwitchToThread(0, CALLER_LIMITS_SPINNING); // Give OS time to switch CPU
+
+ getextcpuid(0,1, buffer); //call cpuid with EAX=1
+
+ APIC_ID = (dwBuffer[1] & INITIAL_APIC_ID_BITS) >> 24;
+ LOG_ID = APIC_ID & ~PHY_ID_MASK;
+ PHY_ID = APIC_ID >> PHY_ID_SHIFT;
+ if (LOG_ID != 0)
+ lProcCounter++;
+ }
+ }
+ dwAffinityMask = dwAffinityMask << 1;
+ }
+ // Reset the processor affinity
+
+ SetProcessAffinityMask(hCurrentProcessHandle, dwProcessAffinity);
+
+ // Check if HT is enabled on all the processors
+ if(lProcCounter > 0 && (lProcCounter == (DWORD)(PhysicalNum / LogicalNum)))
+ {
+ retVal = lProcCounter;
+ goto fDone;
+ }
+#endif // FEATURE_CORESYSTEM
+ }
+ }
+fDone:
+
+ return retVal;
+}
+
+#endif // _TARGET_X86_ || _TARGET_AMD64_
+
+size_t GetLargestOnDieCacheSize(BOOL bTrueSize)
+{
+ // No CONTRACT possible because GetLargestOnDieCacheSize uses SEH
+
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+
+#if defined(_TARGET_AMD64_) || defined (_TARGET_X86_)
+
+ static size_t maxSize;
+ static size_t maxTrueSize;
+
+ if (maxSize)
+ {
+ // maxSize and maxTrueSize cached
+ if (bTrueSize)
+ {
+ return maxTrueSize;
+ }
+ else
+ {
+ return maxSize;
+ }
+ }
+
+ DefaultCatchFilterParam param;
+ param.pv = COMPLUS_EXCEPTION_EXECUTE_HANDLER;
+
+ PAL_TRY(DefaultCatchFilterParam *, pParam, &param)
+ {
+ unsigned char buffer[16];
+ DWORD* dwBuffer = (DWORD*)buffer;
+
+ DWORD maxCpuId = getcpuid(0, buffer);
+
+ if (dwBuffer[1] == 'uneG')
+ {
+ if (dwBuffer[3] == 'Ieni')
+ {
+ if (dwBuffer[2] == 'letn')
+ {
+ /*
+ //The following lines are commented because the OS API on Windows 2003 SP1 is not returning the Cache Relation information on x86.
+ //Once the OS API (LH and above) is updated with this information, we should start using the OS API to get the cache enumeration by
+ //uncommenting the lines below.
+
+ tempSize = GetLogicalProcessorCacheSizeFromOS(); //use OS API for cache enumeration on LH and above
+ */
+ size_t tempSize = 0;
+ if (maxCpuId >= 2) // cpuid support for cache size determination is available
+ {
+ tempSize = GetIntelDeterministicCacheEnum(); // try to use use deterministic cache size enumeration
+ if (!tempSize)
+ { // deterministic enumeration failed, fallback to legacy enumeration using descriptor values
+ tempSize = GetIntelDescriptorValuesCache();
+ }
+ }
+
+ // update maxSize once with final value
+ maxTrueSize = tempSize;
+
+#ifdef _WIN64
+ if (maxCpuId >= 2)
+ {
+ // If we're running on a Prescott or greater core, EM64T tests
+ // show that starting with a gen0 larger than LLC improves performance.
+ // Thus, start with a gen0 size that is larger than the cache. The value of
+ // 3 is a reasonable tradeoff between workingset and performance.
+ maxSize = maxTrueSize * 3;
+ }
+ else
+#endif
+ {
+ maxSize = maxTrueSize;
+ }
+ }
+ }
+ }
+
+ if (dwBuffer[1] == 'htuA') {
+ if (dwBuffer[3] == 'itne') {
+ if (dwBuffer[2] == 'DMAc') {
+
+ if (getcpuid(0x80000000, buffer) >= 0x80000006)
+ {
+ getcpuid(0x80000006, buffer);
+
+ DWORD dwL2CacheBits = dwBuffer[2];
+ DWORD dwL3CacheBits = dwBuffer[3];
+
+ maxTrueSize = (size_t)((dwL2CacheBits >> 16) * 1024); // L2 cache size in ECX bits 31-16
+
+ getcpuid(0x1, buffer);
+ DWORD dwBaseFamily = (dwBuffer[0] & (0xF << 8)) >> 8;
+ DWORD dwExtFamily = (dwBuffer[0] & (0xFF << 20)) >> 20;
+ DWORD dwFamily = dwBaseFamily >= 0xF ? dwBaseFamily + dwExtFamily : dwBaseFamily;
+
+ if (dwFamily >= 0x10)
+ {
+ BOOL bSkipAMDL3 = FALSE;
+
+ if (dwFamily == 0x10) // are we running on a Barcelona (Family 10h) processor?
+ {
+ // check model
+ DWORD dwBaseModel = (dwBuffer[0] & (0xF << 4)) >> 4 ;
+ DWORD dwExtModel = (dwBuffer[0] & (0xF << 16)) >> 16;
+ DWORD dwModel = dwBaseFamily >= 0xF ? (dwExtModel << 4) | dwBaseModel : dwBaseModel;
+
+ switch (dwModel)
+ {
+ case 0x2:
+ // 65nm parts do not benefit from larger Gen0
+ bSkipAMDL3 = TRUE;
+ break;
+
+ case 0x4:
+ default:
+ bSkipAMDL3 = FALSE;
+ }
+ }
+
+ if (!bSkipAMDL3)
+ {
+ // 45nm Greyhound parts (and future parts based on newer northbridge) benefit
+ // from increased gen0 size, taking L3 into account
+ getcpuid(0x80000008, buffer);
+ DWORD dwNumberOfCores = (dwBuffer[2] & (0xFF)) + 1; // NC is in ECX bits 7-0
+
+ DWORD dwL3CacheSize = (size_t)((dwL3CacheBits >> 18) * 512 * 1024); // L3 size in EDX bits 31-18 * 512KB
+ // L3 is shared between cores
+ dwL3CacheSize = dwL3CacheSize / dwNumberOfCores;
+ maxTrueSize += dwL3CacheSize; // due to exclusive caches, add L3 size (possibly zero) to L2
+ // L1 is too small to worry about, so ignore it
+ }
+ }
+
+
+ maxSize = maxTrueSize;
+ }
+ }
+ }
+ }
+ }
+ PAL_EXCEPT_FILTER(DefaultCatchFilter)
+ {
+ }
+ PAL_ENDTRY
+
+ // printf("GetLargestOnDieCacheSize returns %d, adjusted size %d\n", maxSize, maxTrueSize);
+ if (bTrueSize)
+ return maxTrueSize;
+ else
+ return maxSize;
+
+#else
+
+ size_t cache_size = GetLogicalProcessorCacheSizeFromOS() ; // Returns the size of the highest level processor cache
+ return cache_size;
+
+#endif
+}
+
+//---------------------------------------------------------------------
+
+#ifndef FEATURE_PAL
+ThreadLocaleHolder::~ThreadLocaleHolder()
+{
+#ifdef FEATURE_USE_LCID
+#ifdef FEATURE_INCLUDE_ALL_INTERFACES
+ IHostTaskManager *pManager = CorHost2::GetHostTaskManager();
+ if (pManager)
+ {
+ BEGIN_SO_TOLERANT_CODE_CALLING_HOST(GetThread());
+ pManager->SetLocale(m_locale);
+ END_SO_TOLERANT_CODE_CALLING_HOST;
+ }
+ else
+#endif // FEATURE_INCLUDE_ALL_INTERFACES
+#endif // FEATURE_USE_LCID
+ {
+ SetThreadLocale(m_locale);
+ }
+}
+
+HMODULE CLRGetModuleHandle(LPCWSTR lpModuleFileName)
+{
+ // Don't use dynamic contract: will override GetLastError value
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FORBID_FAULT;
+ STATIC_CONTRACT_SO_TOLERANT;
+
+ ThreadAffinityHolder affinity;
+
+ HMODULE hMod = WszGetModuleHandle(lpModuleFileName);
+ return hMod;
+}
+
+
+HMODULE CLRGetCurrentModuleHandle()
+{
+ // Don't use dynamic contract: will override GetLastError value
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FORBID_FAULT;
+ STATIC_CONTRACT_SO_TOLERANT;
+
+ ThreadAffinityHolder affinity;
+
+ HMODULE hMod = WszGetModuleHandle(NULL);
+ return hMod;
+}
+
+#ifndef FEATURE_CORECLR
+static ICLRRuntimeInfo *GetCLRRuntime()
+{
+ LIMITED_METHOD_CONTRACT;
+ return g_pCLRRuntime;
+}
+#endif // !FEATURE_CORECLR
+
+#endif // !FEATURE_PAL
+
+extern LPVOID EEHeapAllocInProcessHeap(DWORD dwFlags, SIZE_T dwBytes);
+extern BOOL EEHeapFreeInProcessHeap(DWORD dwFlags, LPVOID lpMem);
+extern void ShutdownRuntimeWithoutExiting(int exitCode);
+extern BOOL IsRuntimeStarted(DWORD *pdwStartupFlags);
+
+void * GetCLRFunction(LPCSTR FunctionName)
+{
+
+ void* func = NULL;
+ BEGIN_ENTRYPOINT_VOIDRET;
+
+ LIMITED_METHOD_CONTRACT;
+
+ if (strcmp(FunctionName, "EEHeapAllocInProcessHeap") == 0)
+ {
+ func = (void*)EEHeapAllocInProcessHeap;
+ }
+ else if (strcmp(FunctionName, "EEHeapFreeInProcessHeap") == 0)
+ {
+ func = (void*)EEHeapFreeInProcessHeap;
+ }
+#ifndef FEATURE_CORECLR
+ else if (strcmp(FunctionName, "GetCLRRuntime") == 0)
+ {
+ func = (void*)GetCLRRuntime;
+ }
+#endif // !FEATURE_CORECLR
+ else if (strcmp(FunctionName, "ShutdownRuntimeWithoutExiting") == 0)
+ {
+ func = (void*)ShutdownRuntimeWithoutExiting;
+ }
+ else if (strcmp(FunctionName, "IsRuntimeStarted") == 0)
+ {
+ func = (void*)IsRuntimeStarted;
+ }
+ else {
+ _ASSERTE ("Unknown function name");
+ func = NULL;
+ }
+ END_ENTRYPOINT_VOIDRET;
+
+ return func;
+}
+
+#endif // CROSSGEN_COMPILE
+
+LPVOID
+CLRMapViewOfFileEx(
+ IN HANDLE hFileMappingObject,
+ IN DWORD dwDesiredAccess,
+ IN DWORD dwFileOffsetHigh,
+ IN DWORD dwFileOffsetLow,
+ IN SIZE_T dwNumberOfBytesToMap,
+ IN LPVOID lpBaseAddress
+ )
+{
+#ifdef _DEBUG
+#ifdef _TARGET_X86_
+
+ char *tmp = new (nothrow) char;
+ if (!tmp)
+ {
+ SetLastError(ERROR_OUTOFMEMORY);
+ return NULL;
+ }
+ delete tmp;
+
+#endif // _TARGET_X86_
+#endif // _DEBUG
+
+ LPVOID pv = MapViewOfFileEx(hFileMappingObject,dwDesiredAccess,dwFileOffsetHigh,dwFileOffsetLow,dwNumberOfBytesToMap,lpBaseAddress);
+
+#ifdef FEATURE_INCLUDE_ALL_INTERFACES
+ IHostMemoryManager *memoryManager = CorHost2::GetHostMemoryManager();
+ if (pv == NULL && memoryManager)
+ {
+ BEGIN_SO_TOLERANT_CODE_CALLING_HOST(GetThread());
+ if (SUCCEEDED(memoryManager->NeedsVirtualAddressSpace(lpBaseAddress, dwNumberOfBytesToMap)))
+ {
+ // after host releases VA, let us try again.
+ pv = MapViewOfFileEx(hFileMappingObject,dwDesiredAccess,dwFileOffsetHigh,dwFileOffsetLow,dwNumberOfBytesToMap,lpBaseAddress);
+ }
+ END_SO_TOLERANT_CODE_CALLING_HOST;
+ }
+#endif // FEATURE_INCLUDE_ALL_INTERFACES
+
+ if (!pv)
+ {
+ if(GetLastError()==ERROR_SUCCESS)
+ SetLastError(ERROR_OUTOFMEMORY);
+ return NULL;
+ }
+
+#ifdef _DEBUG
+#ifdef _TARGET_X86_
+ if (pv && g_pConfig && g_pConfig->ShouldInjectFault(INJECTFAULT_MAPVIEWOFFILE))
+ {
+ MEMORY_BASIC_INFORMATION mbi;
+ memset(&mbi, 0, sizeof(mbi));
+ if (!ClrVirtualQuery(pv, &mbi, sizeof(mbi)))
+ {
+ if(GetLastError()==ERROR_SUCCESS)
+ SetLastError(ERROR_OUTOFMEMORY);
+ return NULL;
+ }
+ UnmapViewOfFile(pv);
+ pv = ClrVirtualAlloc(lpBaseAddress, mbi.RegionSize, MEM_RESERVE, PAGE_NOACCESS);
+ }
+ else
+#endif // _TARGET_X86_
+#endif // _DEBUG
+ {
+#ifdef FEATURE_INCLUDE_ALL_INTERFACES
+ if (memoryManager)
+ {
+ SIZE_T dwNumberOfBytesMapped = 0;
+ // Find out the size of the whole region.
+ LPVOID lpAddr = pv;
+ MEMORY_BASIC_INFORMATION mbi;
+ while (TRUE)
+ {
+ memset(&mbi, 0, sizeof(mbi));
+#undef VirtualQuery
+ if (!::VirtualQuery(lpAddr, &mbi, sizeof(mbi)))
+ {
+ break;
+ }
+#define VirtualQuery(lpAddress, lpBuffer, dwLength) \
+ Dont_Use_VirtualQuery(lpAddress, lpBuffer, dwLength)
+ if (mbi.AllocationBase != pv)
+ {
+ break;
+ }
+ dwNumberOfBytesMapped += mbi.RegionSize;
+ lpAddr = (LPVOID)((BYTE*)lpAddr + mbi.RegionSize);
+ }
+ BEGIN_SO_TOLERANT_CODE_CALLING_HOST(GetThread());
+ memoryManager->AcquiredVirtualAddressSpace(pv, dwNumberOfBytesMapped);
+ END_SO_TOLERANT_CODE_CALLING_HOST;
+ }
+#endif // FEATURE_INCLUDE_ALL_INTERFACES
+ }
+
+ if (!pv && GetLastError()==ERROR_SUCCESS)
+ SetLastError(ERROR_OUTOFMEMORY);
+
+ return pv;
+}
+
+LPVOID
+CLRMapViewOfFile(
+ IN HANDLE hFileMappingObject,
+ IN DWORD dwDesiredAccess,
+ IN DWORD dwFileOffsetHigh,
+ IN DWORD dwFileOffsetLow,
+ IN SIZE_T dwNumberOfBytesToMap
+ )
+{
+ WRAPPER_NO_CONTRACT;
+ return CLRMapViewOfFileEx(hFileMappingObject,dwDesiredAccess,dwFileOffsetHigh,dwFileOffsetLow,dwNumberOfBytesToMap,NULL);
+}
+
+
+BOOL
+CLRUnmapViewOfFile(
+ IN LPVOID lpBaseAddress
+ )
+{
+ STATIC_CONTRACT_ENTRY_POINT;
+
+#ifdef _DEBUG
+#ifdef _TARGET_X86_
+ if (g_pConfig && g_pConfig->ShouldInjectFault(INJECTFAULT_MAPVIEWOFFILE))
+ {
+ return ClrVirtualFree((LPVOID)lpBaseAddress, 0, MEM_RELEASE);
+ }
+ else
+#endif // _TARGET_X86_
+#endif // _DEBUG
+ {
+ BOOL result = UnmapViewOfFile(lpBaseAddress);
+ if (result)
+ {
+#ifdef FEATURE_INCLUDE_ALL_INTERFACES
+ IHostMemoryManager *memoryManager = CorHost2::GetHostMemoryManager();
+ if (memoryManager)
+ {
+ BEGIN_SO_TOLERANT_CODE_CALLING_HOST(GetThread());
+ memoryManager->ReleasedVirtualAddressSpace(lpBaseAddress);
+ END_SO_TOLERANT_CODE_CALLING_HOST;
+ }
+#endif // FEATURE_INCLUDE_ALL_INTERFACES
+ }
+ return result;
+ }
+}
+
+
+#ifndef CROSSGEN_COMPILE
+
+static HMODULE CLRLoadLibraryWorker(LPCWSTR lpLibFileName, DWORD *pLastError)
+{
+ // Don't use dynamic contract: will override GetLastError value
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_TRIGGERS;
+ STATIC_CONTRACT_FAULT;
+ STATIC_CONTRACT_SO_TOLERANT;
+
+ ThreadAffinityHolder affinity;
+ HMODULE hMod;
+ UINT last = SetErrorMode(SEM_NOOPENFILEERRORBOX|SEM_FAILCRITICALERRORS);
+ {
+ INDEBUG(PEDecoder::ForceRelocForDLL(lpLibFileName));
+ hMod = WszLoadLibrary(lpLibFileName);
+ *pLastError = GetLastError();
+ }
+ SetErrorMode(last);
+ return hMod;
+}
+
+HMODULE CLRLoadLibrary(LPCWSTR lpLibFileName)
+{
+ // Don't use dynamic contract: will override GetLastError value
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_TRIGGERS;
+ STATIC_CONTRACT_FAULT;
+
+ DWORD dwLastError = 0;
+ HMODULE hmod = 0;
+
+ // This method should be marked "throws" due to the probe here.
+ STATIC_CONTRACT_VIOLATION(ThrowsViolation);
+
+ BEGIN_SO_TOLERANT_CODE(GetThread());
+ hmod = CLRLoadLibraryWorker(lpLibFileName, &dwLastError);
+ END_SO_TOLERANT_CODE;
+
+ SetLastError(dwLastError);
+ return hmod;
+}
+
+#ifndef FEATURE_PAL
+
+static HMODULE CLRLoadLibraryExWorker(LPCWSTR lpLibFileName, HANDLE hFile, DWORD dwFlags, DWORD *pLastError)
+
+{
+ // Don't use dynamic contract: will override GetLastError value
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_TRIGGERS;
+ STATIC_CONTRACT_FAULT;
+ STATIC_CONTRACT_SO_TOLERANT;
+
+ ThreadAffinityHolder affinity;
+ HMODULE hMod;
+ UINT last = SetErrorMode(SEM_NOOPENFILEERRORBOX|SEM_FAILCRITICALERRORS);
+ {
+ INDEBUG(PEDecoder::ForceRelocForDLL(lpLibFileName));
+ hMod = WszLoadLibraryEx(lpLibFileName, hFile, dwFlags);
+ *pLastError = GetLastError();
+ }
+ SetErrorMode(last);
+ return hMod;
+}
+
+HMODULE CLRLoadLibraryEx(LPCWSTR lpLibFileName, HANDLE hFile, DWORD dwFlags)
+{
+ // Don't use dynamic contract: will override GetLastError value
+
+ // This will throw in the case of SO
+ //STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_TRIGGERS;
+ STATIC_CONTRACT_FAULT;
+
+ DWORD lastError = ERROR_SUCCESS;
+ HMODULE hmod = NULL;
+
+ BEGIN_SO_TOLERANT_CODE(GetThread());
+ hmod = CLRLoadLibraryExWorker(lpLibFileName, hFile, dwFlags, &lastError);
+ END_SO_TOLERANT_CODE;
+
+ SetLastError(lastError);
+ return hmod;
+}
+
+#endif // !FEATURE_PAL
+
+BOOL CLRFreeLibrary(HMODULE hModule)
+{
+ // Don't use dynamic contract: will override GetLastError value
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_TRIGGERS;
+ STATIC_CONTRACT_FORBID_FAULT;
+ STATIC_CONTRACT_SO_TOLERANT;
+
+ ThreadAffinityHolder affinity;
+ return FreeLibrary(hModule);
+}
+
+VOID CLRFreeLibraryAndExitThread(HMODULE hModule,DWORD dwExitCode)
+{
+ // Don't use dynamic contract: will override GetLastError value
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_TRIGGERS;
+ STATIC_CONTRACT_FORBID_FAULT;
+ STATIC_CONTRACT_SO_TOLERANT;
+
+ ThreadAffinityHolder affinity;
+
+ // This is no-return
+ FreeLibraryAndExitThread(hModule,dwExitCode);
+}
+
+#endif // CROSSGEN_COMPILE
+
+#endif // #ifndef DACCESS_COMPILE
+
+GPTR_IMPL(JITNotification, g_pNotificationTable);
+GVAL_IMPL(ULONG32, g_dacNotificationFlags);
+
+BOOL IsValidMethodCodeNotification(USHORT Notification)
+{
+ // If any bit is on other than that given by a valid combination of flags, no good.
+ if (Notification & ~(
+ CLRDATA_METHNOTIFY_NONE |
+ CLRDATA_METHNOTIFY_GENERATED |
+ CLRDATA_METHNOTIFY_DISCARDED))
+ {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+JITNotifications::JITNotifications(JITNotification *jitTable)
+{
+ LIMITED_METHOD_CONTRACT;
+ if (jitTable)
+ {
+ // Bookkeeping info is held in the first slot
+ m_jitTable = jitTable + 1;
+ }
+ else
+ {
+ m_jitTable = NULL;
+ }
+}
+
+BOOL JITNotifications::FindItem(TADDR clrModule, mdToken token, UINT *indexOut)
+{
+ LIMITED_METHOD_CONTRACT;
+ if (m_jitTable == NULL)
+ {
+ return FALSE;
+ }
+
+ if (indexOut == NULL)
+ {
+ return FALSE;
+ }
+
+ UINT Length = GetLength();
+ for(UINT i=0; i < Length; i++)
+ {
+ JITNotification *pCurrent = m_jitTable + i;
+ if (!pCurrent->IsFree() &&
+ pCurrent->clrModule == clrModule &&
+ pCurrent->methodToken == token)
+ {
+ *indexOut = i;
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+// if clrModule is NULL, all active notifications are changed to NType
+BOOL JITNotifications::SetAllNotifications(TADDR clrModule,USHORT NType,BOOL *changedOut)
+{
+ if (m_jitTable == NULL)
+ {
+ return FALSE;
+ }
+
+ if (changedOut == NULL)
+ {
+ return FALSE;
+ }
+
+ *changedOut = FALSE;
+
+ UINT Length = GetLength();
+ for(UINT i=0; i < Length; i++)
+ {
+ JITNotification *pCurrent = m_jitTable + i;
+ if (!pCurrent->IsFree() &&
+ ((clrModule == NULL) || (pCurrent->clrModule == clrModule))&&
+ pCurrent->state != NType)
+ {
+ pCurrent->state = NType;
+ *changedOut = TRUE;
+ }
+ }
+
+ if (*changedOut && NType == CLRDATA_METHNOTIFY_NONE)
+ {
+ // Need to recompute length if we removed notifications
+ for (UINT iCurrent=Length; iCurrent > 0; iCurrent--)
+ {
+ JITNotification *pCurrent = m_jitTable + (iCurrent - 1);
+ if (pCurrent->IsFree())
+ {
+ DecrementLength();
+ }
+ }
+ }
+ return TRUE;
+}
+
+BOOL JITNotifications::SetNotification(TADDR clrModule, mdToken token, USHORT NType)
+{
+ UINT iIndex;
+
+ if (!IsActive())
+ {
+ return FALSE;
+ }
+
+ if (clrModule == NULL)
+ {
+ return FALSE;
+ }
+
+ if (NType == CLRDATA_METHNOTIFY_NONE)
+ {
+ // Remove an item if it exists
+ if (FindItem(clrModule, token, &iIndex))
+ {
+ JITNotification *pItem = m_jitTable + iIndex;
+ pItem->SetFree();
+ _ASSERTE(iIndex < GetLength());
+ // Update highest?
+ if (iIndex == (GetLength()-1))
+ {
+ DecrementLength();
+ }
+ }
+ return TRUE;
+ }
+
+ if (FindItem(clrModule, token, &iIndex))
+ {
+ JITNotification *pItem = m_jitTable + iIndex;
+ _ASSERTE(pItem->IsFree() == FALSE);
+ pItem->state = NType;
+ return TRUE;
+ }
+
+ // Find first free item
+ UINT iFirstFree = GetLength();
+ for (UINT i = 0; i < iFirstFree; i++)
+ {
+ JITNotification *pCurrent = m_jitTable + i;
+ if (pCurrent->state == CLRDATA_METHNOTIFY_NONE)
+ {
+ iFirstFree = i;
+ break;
+ }
+ }
+
+ if (iFirstFree == GetLength() &&
+ iFirstFree == GetTableSize())
+ {
+ // No more room
+ return FALSE;
+ }
+
+ JITNotification *pCurrent = m_jitTable + iFirstFree;
+ pCurrent->SetState(clrModule, token, NType);
+ if (iFirstFree == GetLength())
+ {
+ IncrementLength();
+ }
+
+ return TRUE;
+}
+
+UINT JITNotifications::GetLength()
+{
+ LIMITED_METHOD_CONTRACT;
+ _ASSERTE(IsActive());
+
+ if (!IsActive())
+ {
+ return 0;
+ }
+
+ return (UINT) (m_jitTable - 1)->methodToken;
+}
+
+void JITNotifications::IncrementLength()
+{
+ _ASSERTE(IsActive());
+
+ if (!IsActive())
+ {
+ return;
+ }
+
+ UINT *pShort = (UINT *) &((m_jitTable - 1)->methodToken);
+ (*pShort)++;
+}
+
+void JITNotifications::DecrementLength()
+{
+ _ASSERTE(IsActive());
+
+ if (!IsActive())
+ {
+ return;
+ }
+
+ UINT *pShort = (UINT *) &((m_jitTable - 1)->methodToken);
+ (*pShort)--;
+}
+
+UINT JITNotifications::GetTableSize()
+{
+ _ASSERTE(IsActive());
+
+ if (!IsActive())
+ {
+ return 0;
+ }
+
+ return ((UINT) (m_jitTable - 1)->clrModule);
+}
+
+USHORT JITNotifications::Requested(TADDR clrModule, mdToken token)
+{
+ LIMITED_METHOD_CONTRACT;
+ UINT iIndex;
+ if (FindItem(clrModule, token, &iIndex))
+ {
+ JITNotification *pItem = m_jitTable + iIndex;
+ _ASSERTE(pItem->IsFree() == FALSE);
+ return pItem->state;
+ }
+
+ return CLRDATA_METHNOTIFY_NONE;
+}
+
+#ifdef DACCESS_COMPILE
+
+JITNotification *JITNotifications::InitializeNotificationTable(UINT TableSize)
+{
+ // We use the first entry in the table for recordkeeping info.
+
+ JITNotification *retTable = new (nothrow) JITNotification[TableSize+1];
+ if (retTable)
+ {
+ // Set the length
+ UINT *pUint = (UINT *) &(retTable->methodToken);
+ *pUint = 0;
+ // Set the table size
+ pUint = (UINT *) &(retTable->clrModule);
+ *pUint = TableSize;
+ }
+ return retTable;
+}
+
+template <class NotificationClass>
+BOOL UpdateOutOfProcTable(__GlobalPtr<NotificationClass*, DPTR(NotificationClass)> pHostTable, NotificationClass* copyFrom, UINT tableSize)
+{
+
+ ClrSafeInt<ULONG32> allocSize = S_SIZE_T(sizeof(NotificationClass)) * ClrSafeInt<UINT>(tableSize);
+ if (allocSize.IsOverflow())
+ {
+ return FALSE;
+ }
+
+ if (dac_cast<TADDR>(pHostTable) == NULL)
+ {
+ // The table has not been initialized in the target. Allocate space for it and update the pointer
+ // in the target so that we'll use this allocated memory from now on. Note that we never free this
+ // memory, but this isn't a big deal because it's only a single allocation.
+ TADDR Location;
+
+ if (DacAllocVirtual(0, allocSize.Value(),
+ MEM_COMMIT, PAGE_READWRITE, false,
+ &Location) != S_OK)
+ {
+ return FALSE;
+ }
+
+ DPTR(DPTR(NotificationClass)) ppTable = &pHostTable;
+ *ppTable = DPTR(NotificationClass)(Location);
+ if (DacWriteHostInstance(ppTable,false) != S_OK)
+ {
+ return FALSE;
+ }
+ }
+
+ // We store recordkeeping info right before the m_jitTable pointer, that must be written as well.
+ if (DacWriteAll(dac_cast<TADDR>(pHostTable), copyFrom,
+ allocSize.Value(), false) != S_OK)
+ {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+BOOL JITNotifications::UpdateOutOfProcTable()
+{
+ return ::UpdateOutOfProcTable<JITNotification>(g_pNotificationTable, m_jitTable - 1, GetTableSize() + 1);
+}
+#endif // DACCESS_COMPILE
+
+GPTR_IMPL(GcNotification, g_pGcNotificationTable);
+
+GcNotifications::GcNotifications(GcNotification *gcTable)
+{
+ LIMITED_METHOD_CONTRACT;
+ if (gcTable)
+ {
+ // Bookkeeping info is held in the first slot
+ m_gcTable = gcTable + 1;
+ }
+ else
+ {
+ m_gcTable = NULL;
+ }
+}
+
+BOOL GcNotifications::FindItem(GcEvtArgs ev_, UINT *indexOut)
+{
+ LIMITED_METHOD_CONTRACT;
+ if (m_gcTable == NULL)
+ {
+ return FALSE;
+ }
+
+ if (indexOut == NULL)
+ {
+ return FALSE;
+ }
+
+ UINT length = Length();
+ for (UINT i = 0; i < length; i++)
+ {
+ if (m_gcTable[i].IsMatch(ev_))
+ {
+ *indexOut = i;
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+
+BOOL GcNotifications::SetNotification(GcEvtArgs ev)
+{
+ if (!IsActive())
+ {
+ return FALSE;
+ }
+
+ if (ev.typ < 0 || ev.typ >= GC_EVENT_TYPE_MAX)
+ {
+ return FALSE;
+ }
+
+ // build the "match" event
+ GcEvtArgs evStar = { ev.typ };
+ switch (ev.typ)
+ {
+ case GC_MARK_END:
+ // specify mark event matching all generations
+ evStar.condemnedGeneration = -1;
+ break;
+ default:
+ break;
+ }
+
+ // look for the entry that matches the evStar argument
+ UINT idx;
+ if (!FindItem(evStar, &idx))
+ {
+ // Find first free item
+ UINT iFirstFree = Length();
+ for (UINT i = 0; i < iFirstFree; i++)
+ {
+ GcNotification *pCurrent = m_gcTable + i;
+ if (pCurrent->IsFree())
+ {
+ iFirstFree = i;
+ break;
+ }
+ }
+
+ if (iFirstFree == Length() &&
+ iFirstFree == GetTableSize())
+ {
+ // No more room
+ return FALSE;
+ }
+
+ // guarantee the free cell is zeroed out
+ m_gcTable[iFirstFree].SetFree();
+ idx = iFirstFree;
+ }
+
+ // Now update the state
+ m_gcTable[idx].ev.typ = ev.typ;
+ switch (ev.typ)
+ {
+ case GC_MARK_END:
+ if (ev.condemnedGeneration == 0)
+ {
+ m_gcTable[idx].SetFree();
+ }
+ else
+ {
+ m_gcTable[idx].ev.condemnedGeneration |= ev.condemnedGeneration;
+ }
+ break;
+ default:
+ break;
+ }
+
+ // and if needed, update the array's length
+ if (idx == Length())
+ {
+ IncrementLength();
+ }
+
+ return TRUE;
+}
+
+#ifdef DACCESS_COMPILE
+
+GcNotification *GcNotifications::InitializeNotificationTable(UINT TableSize)
+{
+ // We use the first entry in the table for recordkeeping info.
+
+ GcNotification *retTable = new (nothrow) GcNotification[TableSize+1];
+ if (retTable)
+ {
+ // Set the length
+ UINT *pUint = (UINT *) &(retTable[0].ev.typ);
+ *pUint = 0;
+ // Set the table size
+ ++pUint;
+ *pUint = TableSize;
+ }
+ return retTable;
+}
+
+BOOL GcNotifications::UpdateOutOfProcTable()
+{
+ return ::UpdateOutOfProcTable<GcNotification>(g_pGcNotificationTable, m_gcTable - 1, GetTableSize() + 1);
+}
+#endif // DACCESS_COMPILE
+
+
+void DACNotifyExceptionHelper(TADDR *args,UINT argCount)
+{
+ struct Param
+ {
+ TADDR *args;
+ UINT argCount;
+ } param;
+ param.args = args;
+ param.argCount = argCount;
+
+ PAL_TRY(Param *, pParam, &param)
+ {
+ if (IsDebuggerPresent() && !CORDebuggerAttached())
+ {
+ RaiseException(CLRDATA_NOTIFY_EXCEPTION, 0, pParam->argCount, (ULONG_PTR *) pParam->args);
+ }
+ }
+ PAL_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ }
+ PAL_ENDTRY
+}
+
+// <TODO> FIX IN BETA 2
+//
+// g_dacNotificationFlags is only modified by the DAC and therefore the
+// optmizer can assume that it will always be its default value and has
+// been seen to eliminate the code in DoModuleLoadNotification,
+// etc... such that DAC notifications are no longer sent.
+//
+// TODO: fix this in Beta 2
+// the RIGHT fix is to make g_dacNotificationFlags volatile, but currently
+// we don't have DAC macros to do that. Additionally, there are a number
+// of other places we should look at DAC definitions to determine if they
+// should be also declared volatile.
+//
+// for now we just turn off optimization for these guys
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable: 4748)
+#pragma optimize("", off)
+#endif // _MSC_VER
+ // called from the runtime
+void DACNotify::DoJITNotification(MethodDesc *MethodDescPtr)
+{
+ WRAPPER_NO_CONTRACT;
+ TADDR Args[2] = { JIT_NOTIFICATION, (TADDR) MethodDescPtr };
+ DACNotifyExceptionHelper(Args,2);
+}
+
+void DACNotify::DoJITDiscardNotification(MethodDesc *MethodDescPtr)
+{
+ TADDR Args[2] = { JIT_DISCARD_NOTIFICATION, (TADDR) MethodDescPtr };
+ DACNotifyExceptionHelper(Args,2);
+}
+
+void DACNotify::DoModuleLoadNotification(Module *ModulePtr)
+{
+ WRAPPER_NO_CONTRACT;
+ if ((g_dacNotificationFlags & CLRDATA_NOTIFY_ON_MODULE_LOAD) != 0)
+ {
+ TADDR Args[2] = { MODULE_LOAD_NOTIFICATION, (TADDR) ModulePtr};
+ DACNotifyExceptionHelper(Args, 2);
+ }
+}
+
+void DACNotify::DoModuleUnloadNotification(Module *ModulePtr)
+{
+ WRAPPER_NO_CONTRACT;
+ if ((g_dacNotificationFlags & CLRDATA_NOTIFY_ON_MODULE_UNLOAD) != 0)
+ {
+ TADDR Args[2] = { MODULE_UNLOAD_NOTIFICATION, (TADDR) ModulePtr};
+ DACNotifyExceptionHelper(Args, 2);
+ }
+}
+
+void DACNotify::DoExceptionNotification(Thread* ThreadPtr)
+{
+ if ((g_dacNotificationFlags & CLRDATA_NOTIFY_ON_EXCEPTION) != 0)
+ {
+ TADDR Args[2] = { EXCEPTION_NOTIFICATION, (TADDR) ThreadPtr};
+ DACNotifyExceptionHelper(Args, 2);
+ }
+}
+
+void DACNotify::DoGCNotification(const GcEvtArgs& args)
+{
+ WRAPPER_NO_CONTRACT;
+ if (args.typ == GC_MARK_END)
+ {
+ TADDR Args[3] = { GC_NOTIFICATION, (TADDR) args.typ, args.condemnedGeneration };
+ DACNotifyExceptionHelper(Args, 3);
+ }
+}
+
+void DACNotify::DoExceptionCatcherEnterNotification(MethodDesc *MethodDescPtr, DWORD nativeOffset)
+{
+ WRAPPER_NO_CONTRACT;
+ if ((g_dacNotificationFlags & CLRDATA_NOTIFY_ON_EXCEPTION_CATCH_ENTER) != 0)
+ {
+ TADDR Args[3] = { CATCH_ENTER_NOTIFICATION, (TADDR) MethodDescPtr, (TADDR)nativeOffset };
+ DACNotifyExceptionHelper(Args, 3);
+ }
+}
+
+#ifdef _MSC_VER
+#pragma optimize("", on)
+#pragma warning(pop)
+#endif // _MSC_VER
+// </TODO>
+
+ // called from the DAC
+int DACNotify::GetType(TADDR Args[])
+{
+ // Type is an enum, and will thus fit into an int.
+ return static_cast<int>(Args[0]);
+}
+
+BOOL DACNotify::ParseJITNotification(TADDR Args[], TADDR& MethodDescPtr)
+{
+ _ASSERTE(Args[0] == JIT_NOTIFICATION);
+ if (Args[0] != JIT_NOTIFICATION)
+ {
+ return FALSE;
+ }
+
+ MethodDescPtr = Args[1];
+
+ return TRUE;
+}
+
+BOOL DACNotify::ParseJITDiscardNotification(TADDR Args[], TADDR& MethodDescPtr)
+{
+ _ASSERTE(Args[0] == JIT_DISCARD_NOTIFICATION);
+ if (Args[0] != JIT_DISCARD_NOTIFICATION)
+ {
+ return FALSE;
+ }
+
+ MethodDescPtr = Args[1];
+
+ return TRUE;
+}
+
+BOOL DACNotify::ParseModuleLoadNotification(TADDR Args[], TADDR& Module)
+{
+ _ASSERTE(Args[0] == MODULE_LOAD_NOTIFICATION);
+ if (Args[0] != MODULE_LOAD_NOTIFICATION)
+ {
+ return FALSE;
+ }
+
+ Module = Args[1];
+
+ return TRUE;
+}
+
+BOOL DACNotify::ParseModuleUnloadNotification(TADDR Args[], TADDR& Module)
+{
+ _ASSERTE(Args[0] == MODULE_UNLOAD_NOTIFICATION);
+ if (Args[0] != MODULE_UNLOAD_NOTIFICATION)
+ {
+ return FALSE;
+ }
+
+ Module = Args[1];
+
+ return TRUE;
+}
+
+BOOL DACNotify::ParseExceptionNotification(TADDR Args[], TADDR& ThreadPtr)
+{
+ _ASSERTE(Args[0] == EXCEPTION_NOTIFICATION);
+ if (Args[0] != EXCEPTION_NOTIFICATION)
+ {
+ return FALSE;
+ }
+
+ ThreadPtr = Args[1];
+
+ return TRUE;
+}
+
+
+BOOL DACNotify::ParseGCNotification(TADDR Args[], GcEvtArgs& args)
+{
+ _ASSERTE(Args[0] == GC_NOTIFICATION);
+ if (Args[0] != GC_NOTIFICATION)
+ {
+ return FALSE;
+ }
+
+ BOOL bRet = FALSE;
+
+ args.typ = (GcEvt_t) Args[1];
+ switch (args.typ)
+ {
+ case GC_MARK_END:
+ {
+ // The condemnedGeneration is an int.
+ args.condemnedGeneration = static_cast<int>(Args[2]);
+ bRet = TRUE;
+ break;
+ }
+ default:
+ bRet = FALSE;
+ break;
+ }
+
+ return bRet;
+}
+
+BOOL DACNotify::ParseExceptionCatcherEnterNotification(TADDR Args[], TADDR& MethodDescPtr, DWORD& nativeOffset)
+{
+ _ASSERTE(Args[0] == CATCH_ENTER_NOTIFICATION);
+ if (Args[0] != CATCH_ENTER_NOTIFICATION)
+ {
+ return FALSE;
+ }
+
+ MethodDescPtr = Args[1];
+ nativeOffset = (DWORD) Args[2];
+ return TRUE;
+}
+
+
+#if !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE)
+
+
+#if defined(_DEBUG) && !defined(FEATURE_PAL)
+
+typedef USHORT
+(__stdcall *PFNRtlCaptureStackBackTrace)(
+ IN ULONG FramesToSkip,
+ IN ULONG FramesToCapture,
+ OUT PVOID * BackTrace,
+ OUT PULONG BackTraceHash);
+
+static PFNRtlCaptureStackBackTrace s_RtlCaptureStackBackTrace = NULL;
+
+WORD UtilCaptureStackBackTrace(
+ ULONG FramesToSkip,
+ ULONG FramesToCapture,
+ PVOID * BackTrace,
+ OUT PULONG BackTraceHash)
+{
+ WRAPPER_NO_CONTRACT;
+
+#ifdef _DEBUG
+ Thread* t = GetThread();
+ if (t != NULL) {
+ // the thread should not have a hijack set up or we can't walk the stack.
+ _ASSERTE(!(t->m_State & Thread::TS_Hijacked));
+ }
+#endif
+
+ if(!s_RtlCaptureStackBackTrace)
+ {
+ // Don't need to worry about race conditions here since it will be the same value
+ HMODULE hModNtdll = GetModuleHandleA("ntdll.dll");
+ s_RtlCaptureStackBackTrace = reinterpret_cast<PFNRtlCaptureStackBackTrace>(
+ GetProcAddress(hModNtdll, "RtlCaptureStackBackTrace"));
+ }
+ if (!s_RtlCaptureStackBackTrace) {
+ return 0;
+ }
+ ULONG hash;
+ if (BackTraceHash == NULL) {
+ BackTraceHash = &hash;
+ }
+ return s_RtlCaptureStackBackTrace(FramesToSkip, FramesToCapture, BackTrace, BackTraceHash);
+}
+
+#endif // #if _DEBUG && !FEATURE_PAL
+
+
+#ifdef _DEBUG
+DisableDelayLoadCheckForOleaut32::DisableDelayLoadCheckForOleaut32()
+{
+ GetThread()->SetThreadStateNC(Thread::TSNC_DisableOleaut32Check);
+}
+
+DisableDelayLoadCheckForOleaut32::~DisableDelayLoadCheckForOleaut32()
+{
+ GetThread()->ResetThreadStateNC(Thread::TSNC_DisableOleaut32Check);
+}
+
+BOOL DelayLoadOleaut32CheckDisabled()
+{
+ Thread *pThread = GetThread();
+ if (pThread && pThread->HasThreadStateNC(Thread::TSNC_DisableOleaut32Check))
+ {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+#endif
+
+BOOL EnableARM()
+{
+#ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
+ CONTRACTL
+ {
+ NOTHROW;
+ // TODO: this should really be GC_TRIGGERS so we wouldn't need the
+ // CONTRACT_VIOLATION below but the hosting API that calls this
+ // can be called on a COOP thread and it has a GC_NOTRIGGER contract.
+ // We should use the AD unload thread to call this function on.
+ GC_NOTRIGGER;
+ SO_TOLERANT;
+ }
+ CONTRACTL_END;
+
+ BOOL fARMEnabled = g_fEnableARM;
+
+ if (!fARMEnabled)
+ {
+ if (ThreadStore::s_pThreadStore)
+ {
+ // We need to establish the baselines for the CPU usage counting.
+ Thread *pThread = NULL;
+ CONTRACT_VIOLATION(GCViolation);
+
+ // I am returning TRUE here so the caller will NOT enable
+ // ARM - if we can't take the thread store lock, something
+ // is already kind of messed up so no need to proceed with
+ // enabling ARM.
+ BEGIN_SO_INTOLERANT_CODE_NOTHROW(GetThread(), return TRUE);
+ // Take the thread store lock while we enumerate threads.
+ ThreadStoreLockHolder tsl ;
+
+ while ((pThread = ThreadStore::GetThreadList(pThread)) != NULL)
+ {
+ if (pThread->IsUnstarted() || pThread->IsDead())
+ continue;
+ pThread->QueryThreadProcessorUsage();
+ }
+
+ END_SO_INTOLERANT_CODE;
+ }
+ g_fEnableARM = TRUE;
+ }
+
+ return fARMEnabled;
+#else // FEATURE_APPDOMAIN_RESOURCE_MONITORING
+ return FALSE;
+#endif // FEATURE_APPDOMAIN_RESOURCE_MONITORING
+}
+
+#endif // !DACCESS_COMPILE && !CROSSGEN_COMPILE
+
+
+static BOOL TrustMeIAmSafe(void *pLock)
+{
+ LIMITED_METHOD_CONTRACT;
+ return TRUE;
+}
+
+LockOwner g_lockTrustMeIAmThreadSafe = { NULL, TrustMeIAmSafe };
+
+
+DangerousNonHostedSpinLock g_randomLock;
+CLRRandom g_random;
+
+
+int GetRandomInt(int maxVal)
+{
+#ifndef CROSSGEN_COMPILE
+ // Use the thread-local Random instance if possible
+ Thread* pThread = GetThread();
+ if (pThread)
+ return pThread->GetRandom()->Next(maxVal);
+#endif
+
+ // No Thread object - need to fall back to the global generator.
+ // In DAC builds we don't need the lock (DAC is single-threaded) and can't get it anyway (DNHSL isn't supported)
+#ifndef DACCESS_COMPILE
+ DangerousNonHostedSpinLockHolder lh(&g_randomLock);
+#endif
+ if (!g_random.IsInitialized())
+ g_random.Init();
+ return g_random.Next(maxVal);
+}
+
+// These wrap the SString:L:CompareCaseInsenstive function in a way that makes it
+// easy to fix code that uses _stricmp. _stricmp should be avoided as it uses the current
+// C-runtime locale rather than the invariance culture.
+//
+// Note that unlike the real _stricmp, these functions unavoidably have a throws/gc_triggers/inject_fault
+// contract. So if need a case-insensitive comparison in a place where you can't tolerate this contract,
+// you've got a problem.
+int __cdecl stricmpUTF8(const char* szStr1, const char* szStr2)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ INJECT_FAULT(COMPlusThrowOM());
+ }
+ CONTRACTL_END
+
+ SString sStr1 (SString::Utf8, szStr1);
+ SString sStr2 (SString::Utf8, szStr2);
+ return sStr1.CompareCaseInsensitive(sStr2);
+
+}
+
+#ifndef DACCESS_COMPILE
+//
+// Casing Table Helpers for use in the EE.
+//
+
+// // Convert szIn to lower case in the Invariant locale.
+INT32 InternalCasingHelper::InvariantToLower(__out_bcount_opt(cMaxBytes) LPUTF8 szOut, int cMaxBytes, __in_z LPCUTF8 szIn)
+{
+ CONTRACTL {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ INJECT_FAULT(COMPlusThrowOM());
+ } CONTRACTL_END
+
+ return InvariantToLowerHelper(szOut, cMaxBytes, szIn, TRUE /*fAllowThrow*/);
+}
+
+// Convert szIn to lower case in the Invariant locale.
+INT32 InternalCasingHelper::InvariantToLowerNoThrow(__out_bcount_opt(cMaxBytes) LPUTF8 szOut, int cMaxBytes, __in_z LPCUTF8 szIn)
+{
+ CONTRACTL {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ INJECT_FAULT(return 0;);
+ } CONTRACTL_END
+
+
+ return InvariantToLowerHelper(szOut, cMaxBytes, szIn, FALSE /*fAllowThrow*/);
+}
+
+// Convert szIn to lower case in the Invariant locale.
+INT32 InternalCasingHelper::InvariantToLowerHelper(__out_bcount_opt(cMaxBytes) LPUTF8 szOut, int cMaxBytes, __in_z LPCUTF8 szIn, BOOL fAllowThrow)
+{
+
+ CONTRACTL {
+ // This fcn can trigger a lazy load of the TextInfo class.
+ if (fAllowThrow) THROWS; else NOTHROW;
+ if (fAllowThrow) GC_TRIGGERS; else GC_NOTRIGGER;
+ if (fAllowThrow) {INJECT_FAULT(COMPlusThrowOM());} else {INJECT_FAULT(return 0);}
+ MODE_ANY;
+
+ PRECONDITION((cMaxBytes == 0) || CheckPointer(szOut));
+ PRECONDITION(CheckPointer(szIn));
+ } CONTRACTL_END
+
+ int inLength = (int)(strlen(szIn)+1);
+ INT32 result = 0;
+
+ LPCUTF8 szInSave = szIn;
+ LPUTF8 szOutSave = szOut;
+ BOOL bFoundHighChars=FALSE;
+ //Compute our end point.
+ LPCUTF8 szEnd;
+ INT32 wideCopyLen;
+
+ CQuickBytes qbOut;
+ LPWSTR szWideOut;
+
+ if (cMaxBytes != 0 && szOut == NULL) {
+ if (fAllowThrow) {
+ COMPlusThrowHR(ERROR_INVALID_PARAMETER);
+ }
+ SetLastError(ERROR_INVALID_PARAMETER);
+ result = 0;
+ goto Exit;
+ }
+
+ if (cMaxBytes) {
+ szEnd = szOut + min(inLength, cMaxBytes);
+ //Walk the string copying the characters. Change the case on
+ //any character between A-Z.
+ for (; szOut<szEnd; szOut++, szIn++) {
+ if (*szIn>='A' && *szIn<='Z') {
+ *szOut = *szIn | 0x20;
+ }
+ else {
+ if (((UINT32)(*szIn))>((UINT32)0x80)) {
+ bFoundHighChars = TRUE;
+ break;
+ }
+ *szOut = *szIn;
+ }
+ }
+
+ if (!bFoundHighChars) {
+ //If we copied everything, tell them how many bytes we copied,
+ //and arrange it so that the original position of the string + the returned
+ //length gives us the position of the null (useful if we're appending).
+ if (--inLength > cMaxBytes) {
+ if (fAllowThrow) {
+ COMPlusThrowHR(HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER));
+ }
+ SetLastError(ERROR_INSUFFICIENT_BUFFER);
+ result = 0;
+ goto Exit;
+ }
+
+ result = inLength;
+ goto Exit;
+ }
+ }
+ else {
+ szEnd = szIn + inLength;
+ for (; szIn<szEnd; szIn++) {
+ if (((UINT32)(*szIn))>((UINT32)0x80)) {
+ bFoundHighChars = TRUE;
+ break;
+ }
+ }
+
+ if (!bFoundHighChars) {
+ result = inLength;
+ goto Exit;
+ }
+ }
+
+ szOut = szOutSave;
+
+#ifndef FEATURE_PAL
+
+ //convert the UTF8 to Unicode
+ //MAKE_WIDEPTR_FROMUTF8(szInWide, szInSave);
+
+ int __lszInWide;
+ LPWSTR szInWide;
+ __lszInWide = WszMultiByteToWideChar(CP_UTF8, 0, szInSave, -1, 0, 0);
+ if (__lszInWide > MAKE_MAX_LENGTH)
+ RaiseException(EXCEPTION_INT_OVERFLOW, EXCEPTION_NONCONTINUABLE, 0, 0);
+ szInWide = (LPWSTR) alloca(__lszInWide*sizeof(WCHAR));
+ if (szInWide == NULL) {
+ if (fAllowThrow) {
+ COMPlusThrowOM();
+ } else {
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ result = 0;
+ goto Exit;
+ }
+ }
+ if (0==WszMultiByteToWideChar(CP_UTF8, 0, szInSave, -1, szInWide, __lszInWide)) {
+ RaiseException(ERROR_NO_UNICODE_TRANSLATION, EXCEPTION_NONCONTINUABLE, 0, 0);
+ }
+
+
+ wideCopyLen = (INT32)wcslen(szInWide)+1;
+ if (fAllowThrow) {
+ szWideOut = (LPWSTR)qbOut.AllocThrows(wideCopyLen * sizeof(WCHAR));
+ }
+ else {
+ szWideOut = (LPWSTR)qbOut.AllocNoThrow(wideCopyLen * sizeof(WCHAR));
+ if (!szWideOut) {
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ result = 0;
+ goto Exit;
+ }
+ }
+
+ //Do the casing operation
+ NewApis::LCMapStringEx(W(""), LCMAP_LOWERCASE, szInWide, wideCopyLen, szWideOut, wideCopyLen, NULL, NULL, 0);
+
+ //Convert the Unicode back to UTF8
+ result = WszWideCharToMultiByte(CP_UTF8, 0, szWideOut, wideCopyLen, szOut, cMaxBytes, NULL, NULL);
+
+ if ((result == 0) && fAllowThrow) {
+ COMPlusThrowWin32();
+ }
+
+#endif // !FEATURE_PAL
+
+Exit:
+ return result;
+}
+
+//
+//
+// COMCharacter and Helper functions
+//
+//
+
+#ifndef FEATURE_PAL
+/*============================GetCharacterInfoHelper============================
+**Determines character type info (digit, whitespace, etc) for the given char.
+**Args: c is the character on which to operate.
+** CharInfoType is one of CT_CTYPE1, CT_CTYPE2, CT_CTYPE3 and specifies the type
+** of information being requested.
+**Returns: The bitmask returned by GetStringTypeEx. The caller needs to know
+** how to interpret this.
+**Exceptions: ArgumentException if GetStringTypeEx fails.
+==============================================================================*/
+INT32 GetCharacterInfoHelper(WCHAR c, INT32 CharInfoType)
+{
+ WRAPPER_NO_CONTRACT;
+
+ unsigned short result=0;
+ if (!GetStringTypeEx(LOCALE_USER_DEFAULT, CharInfoType, &(c), 1, &result)) {
+ _ASSERTE(!"This should not happen, verify the arguments passed to GetStringTypeEx()");
+ }
+ return(INT32)result;
+}
+#endif // !FEATURE_PAL
+
+/*==============================nativeIsWhiteSpace==============================
+**The locally available version of IsWhiteSpace. Designed to be called by other
+**native methods. The work is mostly done by GetCharacterInfoHelper
+**Args: c -- the character to check.
+**Returns: true if c is whitespace, false otherwise.
+**Exceptions: Only those thrown by GetCharacterInfoHelper.
+==============================================================================*/
+BOOL COMCharacter::nativeIsWhiteSpace(WCHAR c)
+{
+ WRAPPER_NO_CONTRACT;
+
+#ifndef FEATURE_PAL
+ if (c <= (WCHAR) 0x7F) // common case
+ {
+ BOOL result = (c == ' ') || (c == '\r') || (c == '\n') || (c == '\t') || (c == '\f') || (c == (WCHAR) 0x0B);
+
+ ASSERT(result == ((GetCharacterInfoHelper(c, CT_CTYPE1) & C1_SPACE)!=0));
+
+ return result;
+ }
+
+ // GetCharacterInfoHelper costs around 160 instructions
+ return((GetCharacterInfoHelper(c, CT_CTYPE1) & C1_SPACE)!=0);
+#else // !FEATURE_PAL
+ return iswspace(c);
+#endif // !FEATURE_PAL
+}
+
+/*================================nativeIsDigit=================================
+**The locally available version of IsDigit. Designed to be called by other
+**native methods. The work is mostly done by GetCharacterInfoHelper
+**Args: c -- the character to check.
+**Returns: true if c is whitespace, false otherwise.
+**Exceptions: Only those thrown by GetCharacterInfoHelper.
+==============================================================================*/
+BOOL COMCharacter::nativeIsDigit(WCHAR c)
+{
+ WRAPPER_NO_CONTRACT;
+#ifndef FEATURE_PAL
+ return((GetCharacterInfoHelper(c, CT_CTYPE1) & C1_DIGIT)!=0);
+#else // !FEATURE_PAL
+ return iswdigit(c);
+#endif // !FEATURE_PAL
+}
+
+BOOL RuntimeFileNotFound(HRESULT hr)
+{
+ LIMITED_METHOD_CONTRACT;
+ return Assembly::FileNotFound(hr);
+}
+
+#ifndef FEATURE_PAL
+HRESULT GetFileVersion( // S_OK or error
+ LPCWSTR wszFilePath, // Path to the executable.
+ ULARGE_INTEGER* pFileVersion) // Put file version here.
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ //
+ // Note that this code is equivalent to FusionGetFileVersionInfo, found in fusion\asmcache\asmcache.cpp
+ //
+
+ // Avoid confusion.
+ pFileVersion->QuadPart = 0;
+
+ DWORD ret;
+
+ DWORD dwHandle = 0;
+ DWORD bufSize = GetFileVersionInfoSizeW(wszFilePath, &dwHandle);
+ if (!bufSize)
+ {
+ return HRESULT_FROM_GetLastErrorNA();
+ }
+
+ // Allocate the buffer for the version info structure
+ // _alloca() can't return NULL -- raises STATUS_STACK_OVERFLOW.
+ BYTE* pVersionInfoBuffer = reinterpret_cast< BYTE* >(_alloca(bufSize));
+
+ ret = GetFileVersionInfoW(wszFilePath, dwHandle, bufSize, pVersionInfoBuffer);
+ if (!ret)
+ {
+ return HRESULT_FROM_GetLastErrorNA();
+ }
+
+ // Extract the actual File Version number that we care about.
+ UINT versionInfoSize = 0;
+ VS_FIXEDFILEINFO* pVSFileInfo;
+ ret = VerQueryValueW(pVersionInfoBuffer, W("\\"),
+ reinterpret_cast< void **>(&pVSFileInfo), &versionInfoSize);
+ if (!ret || versionInfoSize == 0)
+ {
+ return HRESULT_FROM_GetLastErrorNA();
+ }
+
+ pFileVersion->HighPart = pVSFileInfo->dwFileVersionMS;
+ pFileVersion->LowPart = pVSFileInfo->dwFileVersionLS;
+
+ return S_OK;
+}
+#endif // !FEATURE_PAL
+
+#endif // !DACCESS_COMPILE